Pyrel dev log

Collapse
X
 
  • Time
  • Show
Clear All
new posts

  • Derakon
    replied
    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.

    Leave a comment:


  • Magnate
    replied
    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!

    Leave a comment:


  • nppangband
    replied
    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.

    Leave a comment:


  • Magnate
    replied
    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.

    Leave a comment:


  • Derakon
    replied
    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.

    Leave a comment:


  • Nick
    replied
    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

    Leave a comment:


  • d_m
    replied
    Originally posted by Derakon
    More complicated are indefinite and temporary effects. In both cases, the effect needs to implement an expire() method, which specifies how to clean up when the effect ends. For temporary methods, we just set a timer, and when the timer expires, the effect removes its mutation to the world. For indefinite effects we also need to know when to call the expire() method:

    * onEquip -> onUnequip
    * onEntry -> onExit
    * onPickup -> onDrop
    * onUse -> onUse
    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.

    Originally posted by Derakon
    Effects can also have targeting. We specify the following types of valid targets:

    * Player
    * User-selected direction / tile / other creature (i.e. standard Angband targeting)
    * All inventory
    * User-selected inventory/equipment/floor item
    * Specific (non-user-selected) equipment slot(s)
    * ...more?
    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.

    In one sense I think you're right, but for some reason I imagine effects as more like the thing that happens, and less like all the ways it could come about.

    Originally posted by Derakon
    It's this cached state that we refer to in the course of normal gameplay. Every time a mod is added to or removed from the entity, it is recalculated / updated.
    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.

    Originally posted by Derakon
    Here's another effect -- providing resistance to an element, like you'd get from armor -- written out completely without resorting to templates, just for the sake of clarity:[code]{
    "name": "resist elec",
    "duration": "indefinite",
    "trigger": "onWield",
    "procs": [
    {"type": "permanent resistance",
    "element": "electricity"
    }
    }
    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?

    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.

    Anyway, please feel free to disregard to the degree this feedback isn't useful to you!

    Leave a comment:


  • Derakon
    replied
    Originally posted by Magnate
    Yes, agree on all counts - I think item mods and effects are actually very closely overlapping sets of things, which I think is why I'm so exercised about the framework. I was surprised to see that spells aren't in your initial list of framework items, btw.
    Okay, so, let's see if I can't talk my way through this. This could get a bit rambly.

    We want to have an Effect class. Effects do "stuff" via their trigger() method; basically they're rules for mutating the game world. Effects can trigger at different times:

    * "onUse": When an item is used (including spells -- spellbooks count as items)
    * "onEquip": When an item is equipped
    * "onEntry": When a square is moved into (traps)
    * ...

    Effects can also be marked as one-shots (also including permanent effects), indefinite, or temporary. Examples of one-shot effects would be dealing damage, identifying or enchanting an item, or revealing map information. They're simple.

    More complicated are indefinite and temporary effects. In both cases, the effect needs to implement an expire() method, which specifies how to clean up when the effect ends. For temporary methods, we just set a timer, and when the timer expires, the effect removes its mutation to the world. For indefinite effects we also need to know when to call the expire() method:

    * onEquip -> onUnequip
    * onEntry -> onExit
    * onPickup -> onDrop
    * onUse -> onUse

    Effects can also have targeting. We specify the following types of valid targets:

    * Player
    * User-selected direction / tile / other creature (i.e. standard Angband targeting)
    * All inventory
    * User-selected inventory/equipment/floor item
    * Specific (non-user-selected) equipment slot(s)
    * ...more?

    Now, every entity that is a valid target of an non-instant/one-shot effect (Items and Creatures currently; possibly Terrain as well) must maintain a list of Mods that affect the entity. A Mod is a new class that consists of the following:

    * A flag describing what effect the mod has. For example, STR, RES_FIRE, SPEED.
    * A modifier amount (a simple signed integer)
    * A link back to the Effect? Or to the entity that triggered the Effect? This is mostly for informative purposes (e.g. putting the text "This weapon is receiving a bonus to finesse from your Ring of Finesse +140" on the 'I'nspect screen)

    The entity also maintains a cached "modified state" that sums up how these mods affect the entity. This thus includes things like:
    * The totals of all stat-bonus Mods
    * Which elements the entity is resistant/vulnerable/immune to
    * etc.

    It's this cached state that we refer to in the course of normal gameplay. Every time a mod is added to or removed from the entity, it is recalculated / updated.

    So let's make some example records. First off, we're going to want not just Effect records (effect.txt edit file), but also Effect Template records (effect_template.txt), since a lot of effects, especially when it comes to attack spells, are going to look very similar. So here's an effect template:
    Code:
    {
        "name": "bolt",
        "duration": "instant",
        "targets": ["free target"],
        "procs": [{"type": "animate bolt"}]
    }
    The name is used to let specific Effects map back to this effect; duration and targets should be obvious. "procs" is how we map an effect to the code used to handle it. Somewhere in the code will be a function that maps "animate bolt" to a function that takes the Effect and its target as arguments and mutates the game state to handle display (e.g. by sticking a transient spell effect onto the appropriate tile).

    Here's an Effect record using that template:
    Code:
    {
        "name": "magic missile",
        "templates": ["bolt"],
        "procs": [
            {"type": "damage creature",
             "damage": "3d4"
            },
            {"type": "display bolt",
             "display": {"ascii": {"color": (194, 0, 194)}}
            }
        ],
    }
    Here we include another proc to actually deal damage to the target. The "damage" field is basically an arbitrary metadata field that gets passed to the proc-handling function. We could stick anything we want in there. We replicate the "display bolt" proc here so we can insert the appropriate display metadata (the "display bolt" code handler already knows what kind of symbols to use since it's displaying a bolt, after all).

    Here's another effect -- providing resistance to an element, like you'd get from armor -- written out completely without resorting to templates, just for the sake of clarity:
    Code:
    {
        "name": "resist elec",
        "duration": "indefinite",
        "trigger": "onWield",
        "procs": [
            {"type": "permanent resistance",
             "element": "electricity"
            }
    }
    Here we include the "trigger" field, which the magic missile effect didn't include (it'd have to be specified by the items using the effect). The target is the owner of the item. And of course we invoke a different proc with a different argument.

    Now here's some item records with effects:
    Code:
    {
        "subtype": "Magic Missile",
        "templates": ["wand"],
        "complexity": 3, "cost": 200, "charges": "6+d10",
        "allocations": [{"commonness": 20, "minDepth": 20, "maxDepth": 60}],
        "effects": [
            {"trigger": "onUse",
             "name": "magic missile"
            },
        ]    
    }
    
    {
        "subtype": "Blue Dragon Scale Mail~",
        "templates": ["dragon armor"],
        "display": {"ascii": {"color": [0, 64, 255]}},
        "weight": 10.0,
        "armor": {"baseArmor": 12},
        "effects": [{"name": "resist elec"}]
    },
    Note that the trigger can be stored in the effect record and/or overridden in the item record, much like anything in the effect template record is merged with and/or overridden by the effect that uses that template. Hierarchies on hierarchies on hierarchies, man. Turtles all the way down.

    Okay, this is getting long enough as it is, and I think if I try to add any more to it I'll just confuse myself (and, ergo, everyone trying to understand me). Feedback?

    Leave a comment:


  • Magnate
    replied
    Originally posted by Derakon
    Don't we assume that if the player is aware of their condition then they also know which piece(s) of gear is causing that condition? Since otherwise @ has do to a bunch of tedious equipment swapping next to salamanders to figure out exactly what's giving him rFire, etc.
    I think you're correct, yes. I've tried to think of an example in current gameplay where @ is aware of a condition but not its source, and I can't. So never mind that!
    I suppose you could consider at some level an item mod to be a permanent passive effect...though I guess you could also have items that cast spells when they are equipped (and then can't be unequipped for awhile), or the like. But I think we can still safely delineate between passive and active mods, and leave the active ones for later.
    Yes, agree on all counts - I think item mods and effects are actually very closely overlapping sets of things, which I think is why I'm so exercised about the framework. I was surprised to see that spells aren't in your initial list of framework items, btw.

    If I can come up with a conceptual framework that addresses mods and effects together I'll be interested in your views on it. If you do your item mods framework before I get round to that, I hope you'll forgive me stress-testing it!

    Leave a comment:


  • Derakon
    replied
    Originally posted by Magnate
    Do we need to distinguish between base AC and +ac? I guess you want the former to be a physical property and the latter magical? That works.
    That was my thinking. Base AC is also important for handling acid damage.
    Sure. That's not quite what I was on about though. Consider regen: @ is healing (and getting hungry) faster than usual. There are at least two aspects of knowledge to this: (i) has @ noticed at all and (ii) is @ aware of the source of this change? The latter is the traditional "this property of this item is known", but the former is "this aspect of current state is known", which is different.
    Don't we assume that if the player is aware of their condition then they also know which piece(s) of gear is causing that condition? Since otherwise @ has do to a bunch of tedious equipment swapping next to salamanders to figure out exactly what's giving him rFire, etc.
    Hmmm. I see it a bit differently - you don't have to actually code every piece of complexity, merely keep your design able to accommodate it.
    Well, my goal is to implement all of the framework stuff -- the "this is broadly what you are able to do with X". Once that's done I can open up the dev work to more people since the remaining work will mostly be filling in details (e.g. replacing the "generate a static room" code with "generate a random proper dungeon level" code). Item mods are part of the framework, IMO, because getting them wrong makes all aspects of item handling less pleasant. And items are a Big Deal in Angband.
    Please forgive me if I've misunderstood, but how does targetable (of entities) increase complexity more than targetable (of slots, which you said you already intended)?
    I was waffling on making mods slot-targetable, sorry; I should have made that more clear.

    I was thinking some more about this, and thinking that this is closely tied to how you implement effects (which in turn determines spell handling). Because although most item mods affect only attributes of something (usually @'s stats or resists), some of them affect the environment (OF_IMPACT, OF_TELEPATHY etc.). So it's probably worth thinking about effects at least a bit before making irrevocable conclusions about item mods.
    I suppose you could consider at some level an item mod to be a permanent passive effect...though I guess you could also have items that cast spells when they are equipped (and then can't be unequipped for awhile), or the like. But I think we can still safely delineate between passive and active mods, and leave the active ones for later.

    Originally posted by magnate2 on IRC
    <magnate2> But like you I want to brood on this a little further. I think we need some kind of ontological hierarchy of things in Angband. During my thinking about effects I had got as far as classifying different genui of effects
    [04:29] <magnate2> http://trac.rephial.org/wiki/NewEffects
    [04:30] <magnate2> So I think I can come up with similar thinking about mods (which you may or may not find helpful :-))
    Thanks, I'll check out that page. And if I think of anything about Pyrel that belongs on the wiki instead of in the source documentation, then I'll make a node.

    If it helps, I'd happily volunteer to do Pyrel's item gen, if you want to outsource when you're happy with the mod framework. And I'm not sure if he told you this, but d_m wrote his dungeon gen code in python before porting it to V!
    As I mentioned earlier, my plan definitely is to get the framework in place so everyone knows how to interact with the different components of the engine, and then set Pyrel loose for others to hack on. To be more specific, here's what I want to get done:

    * Item mods, and applying those mods to the player / other items / etc.
    * Simple level gen -- just making a small room with some level-appropriate items and monsters
    * Simple combat -- letting the player take a whack at monsters, letting monsters take a whack at the player (assuming their incredibly stupid AI lets them try, anyway). This is actually almost done; I just need to affect hitpoints.
    * Usable items -- make a potion that can be used to print a message, something like that.
    * Savefiles (which will probably be just a serialization of all extant objects, possibly gzipped)
    * Character birth and death

    That leaves a whole ton of stuff that's really important to gameplay but won't affect the basic engine. Proper AI, v4's combat model, all of the item effects, spells (including monster spells), etc...and honestly the last two items could probably wait until later, if only because we may well be changing the item and monster records and that would mean rewriting the (de)serialization code each time.

    Leave a comment:


  • Magnate
    replied
    Originally posted by Derakon
    * Every trait of an equippable except for base AC, damage dice, weight, flavor, and of course its item category is considered a "mod" to the item.
    Do we need to distinguish between base AC and +ac? I guess you want the former to be a physical property and the latter magical? That works.
    * ItemMods can be known or unknown.

    Later on I/we can deal with affixes; they'll probably be approached as aggregates of Mods, and if every Mod in the aggregation is learned then the affix is learned too.
    Sure. That's not quite what I was on about though. Consider regen: @ is healing (and getting hungry) faster than usual. There are at least two aspects of knowledge to this: (i) has @ noticed at all and (ii) is @ aware of the source of this change? The latter is the traditional "this property of this item is known", but the former is "this aspect of current state is known", which is different.
    Well, you can go two ways with this. If I tried to make Pyrel be capable of anything then I'd certainly never finish, for example.
    Hmmm. I see it a bit differently - you don't have to actually code every piece of complexity, merely keep your design able to accommodate it. But I guess we need to look at specifics:
    I spoke with d_m some about this yesterday evening; making mods targetable does significantly increase complexity even if we disallow second-order effects. I'm not certain I have a good idea for how to implement it cleanly yet; it'll bear some consideration.
    Please forgive me if I've misunderstood, but how does targetable (of entities) increase complexity more than targetable (of slots, which you said you already intended)?

    I was thinking some more about this, and thinking that this is closely tied to how you implement effects (which in turn determines spell handling). Because although most item mods affect only attributes of something (usually @'s stats or resists), some of them affect the environment (OF_IMPACT, OF_TELEPATHY etc.). So it's probably worth thinking about effects at least a bit before making irrevocable conclusions about item mods.
    Oh dear, social pressure.
    But you asked for it :-)

    If it helps, I'd happily volunteer to do Pyrel's item gen, if you want to outsource when you're happy with the mod framework. And I'm not sure if he told you this, but d_m wrote his dungeon gen code in python before porting it to V!

    Leave a comment:


  • Derakon
    replied
    Originally posted by Magnate
    How are you handling player knowledge of player state? Are you keeping two entirely separate caches, one of "real" state and one of "known" state, or are you adding a "known" flag to each element of state, or what? IMO this will be a big determinant of your ID code.
    I'm still working on properly handling equipping items to specific slots -- I haven't had much time to program these last few days and it took a bit longer than anticipated. So the ramifications of equipping items haven't been handled yet. However, here's my plan:

    * Every trait of an equippable except for base AC, damage dice, weight, flavor, and of course its item category is considered a "mod" to the item. Some mods are binary (i.e. flags), some are parameterized (i.e. "pvals"). In the item record they're called "flags" and "mods" respectively. Yes, this means that +to-finesse and +to-prowess would be considered item mods.
    * Flags are transparently mapped to +1 mods on load. The item has a list of mods that it has -- I'll probably create a new ItemMod class for this.
    * Like ItemMods can be merged (e.g. a weapon with multiple Slaying affixes).
    * ItemMods can be known or unknown.

    Later on I/we can deal with affixes; they'll probably be approached as aggregates of Mods, and if every Mod in the aggregation is learned then the affix is learned too.

    I have strong views about this (which you can of course ignore at leisure). I think that added complexity is fine, because it can always always be simplified later. Far better to design in capacity for complexity and ultimately not use it, than try to add it later.
    Well, you can go two ways with this. If I tried to make Pyrel be capable of anything then I'd certainly never finish, for example.

    So I'd encourage you to code the mods framework in a way that is completely flexible:
    - any mod can affect any entity: @ or any monster(s) or any slot or item, including carried (not worn) items (combine your anti-acid cloak with Sangband's protection blankets), including not-self (dagger that boosts archery but not melee)
    - any mod's effect on its affected entities can be on-wield or on-carry (so an artifact rod in the backpack can provide a slowing aura which only affects demons)
    I have notify-on-pickup and notify-on-wield functions coded in, though of course currently they do nothing (and I'd also need notify-on-remove-from-inventory). I'm busy sketching out the framework, and plan to leave the detail work for later.

    I spoke with d_m some about this yesterday evening; making mods targetable does significantly increase complexity even if we disallow second-order effects. I'm not certain I have a good idea for how to implement it cleanly yet; it'll bear some consideration.

    Just my 2p. Please keep up the good work. d_m and noz and I were discussing pyrel over a curry the other night and are all very excited about it.
    Oh dear, social pressure.

    Leave a comment:


  • Magnate
    replied
    Originally posted by Derakon
    Whenever a creature equips or unequips an item, we need to recalculate the flags and bonuses they get from gear. We store a cached version of this for ease of lookup; it's this cached version that we use for all other game logic.
    How are you handling player knowledge of player state? Are you keeping two entirely separate caches, one of "real" state and one of "known" state, or are you adding a "known" flag to each element of state, or what? IMO this will be a big determinant of your ID code.
    I'm also thinking about how to handle the melee/missile stat split. Specifically, there's some weirdness right now with how Rings of Finesse/Prowess apply to missile combat, and you can't currently get off-launcher/ammo slays to apply to missile combat. What I'm thinking is this:
    • Some flags and mods apply only to the item that has those flags/mods. For example, EASY_KNOW, IGNORE_ACID. Conceptually we could have items that are more or less resistant to elemental damage too, so flags with associated pvals could go here.
    • Some flags apply to the wielder. TELEPATHY, RES_ACID, etc. all go here.
    • Some flags apply to one or more equipment slots (or rather the items currently occupying those slots). So you could have a ring that gives you extra finesse but only in melee (a Ring of Fencing?), or a cloak that gives all your other armor slots IGNORE_ACID.
    I'm wondering if this is more complexity than gain though. The main benefits I can think of right now are:

    * Off-weapon combat bonuses are more obvious about what they affect
    * We have a few minor new items that we could create (c.f. anti-acid cloak)

    The cost of course is that the complexity of the mods system is increased. Currently mods implicitly affect either the item or the wielder; we can keep that implicit system and avoid having to specify weapon slots for 99% of existing mods. But the code will still need to handle it. Thoughts? Especially if you can think of other interesting things you could do with items that affect other items when equipped.
    I have strong views about this (which you can of course ignore at leisure). I think that added complexity is fine, because it can always always be simplified later. Far better to design in capacity for complexity and ultimately not use it, than try to add it later.

    So I'd encourage you to code the mods framework in a way that is completely flexible:
    - any mod can affect any entity: @ or any monster(s) or any slot or item, including carried (not worn) items (combine your anti-acid cloak with Sangband's protection blankets), including not-self (dagger that boosts archery but not melee)
    - any mod's effect on its affected entities can be on-wield or on-carry (so an artifact rod in the backpack can provide a slowing aura which only affects demons)

    Just my 2p. Please keep up the good work. d_m and noz and I were discussing pyrel over a curry the other night and are all very excited about it.

    Leave a comment:


  • Derakon
    replied
    I'm in the middle of making items equippable. The basic plan here is:
    • Each item (or item template) record may include an "equipSlots" field. This is a list of slots the item can be equipped to, like "weapon", "launcher", or "neck". Currently all items in Angband can only be wielded to one type of equip slot, but there are variants that e.g. allow a weapon to be wielded to the shield slot, hence the list.
    • Each creature (or creature template) record may include its own "equipSlots" field, which describes the slots the creature has available for wielding items. This is a mapping of slot descriptors (e.g. "on your back", "on your left finger", "providing light") to slot names (e.g. "back", "finger", "light source"). Thus we cover the equipment display as well as non-unique equipment slots (like the two ring fingers). This should make it fairly easy to let variants let the player play as different monster races.
    • Whenever a creature equips or unequips an item, we need to recalculate the flags and bonuses they get from gear. We store a cached version of this for ease of lookup; it's this cached version that we use for all other game logic.


    I'm also thinking about how to handle the melee/missile stat split. Specifically, there's some weirdness right now with how Rings of Finesse/Prowess apply to missile combat, and you can't currently get off-launcher/ammo slays to apply to missile combat. What I'm thinking is this:
    • Some flags and mods apply only to the item that has those flags/mods. For example, EASY_KNOW, IGNORE_ACID. Conceptually we could have items that are more or less resistant to elemental damage too, so flags with associated pvals could go here.
    • Some flags apply to the wielder. TELEPATHY, RES_ACID, etc. all go here.
    • Some flags apply to one or more equipment slots (or rather the items currently occupying those slots). So you could have a ring that gives you extra finesse but only in melee (a Ring of Fencing?), or a cloak that gives all your other armor slots IGNORE_ACID.
    I'm wondering if this is more complexity than gain though. The main benefits I can think of right now are:

    * Off-weapon combat bonuses are more obvious about what they affect
    * We have a few minor new items that we could create (c.f. anti-acid cloak)

    The cost of course is that the complexity of the mods system is increased. Currently mods implicitly affect either the item or the wielder; we can keep that implicit system and avoid having to specify weapon slots for 99% of existing mods. But the code will still need to handle it. Thoughts? Especially if you can think of other interesting things you could do with items that affect other items when equipped.
    Last edited by Derakon; April 19, 2012, 21:15.

    Leave a comment:


  • Derakon
    replied
    Objects can have object templates now, like creature templates. I spent a bunch of time manually creating a bunch of object templates, only to belatedly realize that Angband already had object templates in object_base.txt. They just aren't called out explicitly like the monster templates are. Oh well; I would have had to make adaptations anyway to handle templated display parameters and a few other things (like crowns always having a base AC of 0).

    Example object record:
    Code:
    {"index": 238, "subtype": "Devotion",
    "templates": ["amulet"],
    "mods": [{"bonus": "1+M3", "flags": ["WIS"]}, {"bonus": "d6", "flags": ["CHR"]}, {"bonus": "1", "flags": ["LIGHT"]}],
    "flags": ["SUST_WIS", "SUST_CHR", "", "RES_DARK", "RES_LIGHT", "RES_FIRE", "HOLD_LIFE", "GOOD"],
    "allocations": [{"commonness": 10, "minDepth": 70, "maxDepth": 100}]},
    Corresponding template entry:
    Code:
    {"type": "amulet", "template_name": "amulet",
    "display": {"ascii": {"color": "flavor", "symbol": "\""}},
    "weight": 0.3}
    The "flavor" color means that the color of the object should derive from its flavor. Once I get around to assigning flavors to objects, that will actually be used. I already have the map of flavor to color ready to be used.

    1824 lines, 408 are comments, 208 are whitespace. I'm...having trouble reconciling this count with my earlier count -- how did I manage to turn ~100 lines of whitespace into comments? The command I'm using, for what it's worth, is
    Code:
    find . -name "*py" | grep -v data | xargs cat | grep -P "^\s*#" | grep -cv "#"
    The "grep -P" for some reason returns not just lines that have some amount of whitespace followed by a #, but also lines that have no # at all; oh well, I split them out after.

    Leave a comment:

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