Pyrel dev log, part 5

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

    #61
    Originally posted by LostTemplar
    Just do the same with display code (with several different ways to display characters) and it will be fine.
    To be sure I understand, are you suggesting that we have multiple implementations of the heatmap code that are either user-selectable or that the game automatically picks between? Because otherwise I'm not sure how the display code is relevant to the current conversation.

    Comment

    • LostTemplar
      Knight
      • Aug 2009
      • 670

      #62
      I mean that it is probably good idea to write display code in C also to make sure that it is fast.
      Maybe there are some other bottlenecks worth rewriting in C. Something, that is very unlikely to be changed in any variant.

      Comment

      • Derakon
        Prophet
        • Dec 2009
        • 9022

        #63
        I wouldn't worry about the display code -- it's pretty zippy right now. If we ever need even more speed out of it then I can rewrite it to use OpenGL rendering, which would be even faster than C code could ever manage.

        In any event, the golden rule of optimization: first make it work, then if it's not fast enough, measure its speed, make changes, and verify that those changes actually improved things (and didn't make it not work).

        Comment

        • Derakon
          Prophet
          • Dec 2009
          • 9022

          #64
          I've started work on creature spellcasting, on the assumption that a complete creature AI will make the game much more interesting. I've run into a bit of a problem with the current Proc system.

          As it stands, the parameters that are passed to a Proc's trigger() function (where it takes effect) depend on how the Proc is triggered. For example, if the Proc is triggered by using an item, then the parameters are the item user, the item itself, and the gameMap (i.e. current game state):
          Code:
          def trigger(self, user, item, gameMap):
          If the Proc is triggered by casting a spell, then the parameters ought to be the spellcaster, the target, and the gameMap:
          Code:
          def trigger(self, source, target, gameMap):
          Ideally I should be able to use the same Proc for both e.g. potions of Speed and the spell Haste Self (both the player and monster versions). The Proc that handles temporary speed boosts doesn't actually care about the item being used, so that's fine. But as it stands, the caller would have to know what kinds of parameters the Proc expects, which makes the caller's code very messy as it must arrange arguments in the way the Proc expects.

          Now, obviously, if a Proc requires certain bits of information, then that information has to be provided to the Proc. And some Procs will need to know what item was used to invoke them -- but those Procs will presumably only ever be used in conjunction with items. Other Procs that are more general need to be more flexible. So here's what I'm thinking.

          In Python (and many other languages) you can write functions that accept any arguments. In Python's case, that works like this:
          Code:
          def trigger(self, **kwargs):
          The "kwargs" parameter is a dictionary that maps parameter names to their values. So if you called the trigger() function like this:
          Code:
          proc.trigger(item = speedPotion, target = player, gameMap = gameMap)
          then the dictionary would contain the keys "item", "target", and "gameMap". If you called it like this:
          Code:
          proc.trigger(caster = player, target = player, gameMap = gameMap)
          then the dictionary would contain "caster", "target", and "gameMap" instead. As far as the Proc is concerned, as long as the "target" parameter is present, everything's fine -- that's the only parameter it actually needs to function.

          I think this will work, but I'm putting it out here in case there's something I missed. Feedback is welcome.

          Comment

          • Magnate
            Angband Devteam member
            • May 2007
            • 5110

            #65
            Seems eminently sensible to me. The whole point of procs is to use them for as many things as possible! It just means a bit more code in each proc to disentangle different argument combinations, but IMO that's worthwhile for cleaner calling code.
            "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

              #66
              That approach to calling Procs seems to be working well so far. I've made some progress with monster spellcasting -- the filthy street urchin in Debug Town will fling firebolts around with reckless abandon now, and those firebolts will damage items in the player's inventory. In fact all elemental damage to inventory items should work now; I still need to do damage to equipment (from acid/disenchantment) and damage to floor items -- but floor items will require area-of-effect spells first.

              Comment

              • scud
                Swordsman
                • Jan 2011
                • 323

                #67
                Originally posted by Derakon
                the filthy street urchin in Debug Town will fling firebolts around with reckless abandon now, and those firebolts will damage items in the player's inventory
                *crosses off Debug Town from list of places to visit*

                Comment

                • Derakon
                  Prophet
                  • Dec 2009
                  • 9022

                  #68
                  Originally posted by scud
                  *crosses off Debug Town from list of places to visit*
                  I'm semi-seriously considering adding The Filthy Street Ãœrchin as a secret boss character now.

                  I'm pretty satisfied with the basic spellcasting framework now. I've added an element system, including acid damage to armor, destruction of inventory items, and destruction of floor items. Elemental effects are defined in a new file, element.txt:
                  Code:
                      {
                          "name": "acid",
                          "damageCap": 1600,
                          "display": {"ascii": {"color": "L_GREEN"}},
                          "procs": [
                              {
                                  "name": "damage equipment",
                                  "message": "was damaged by absorbing acid!",
                                  "failMessage": "was undamaged!",
                                  "validSlots": ["back", "body", "feet", "hands",
                                      "head", "shield"],
                                  "damagedStat": "armorMod",
                                  "capStat": "baseArmor",
                                  "HPDamageMultiplier": 0.5
                              }
                          ]
                      }
                  New spells can be created fairly easily; here's some example spell definitions:
                  Code:
                      {
                          "name": "haste self",
                          "code": "temporary stat mod",
                          "target": "self",
                          "statName": "speed", "modAmount": 1, "duration": "10+d10",
                          "message": "%s speeds up"
                      },
                      {
                          "name": "poison bolt",
                          "code": "launch projectile",
                          "target": "creature",
                          "damage": "level#6", "element": "poison",
                          "message": "%s casts a poison bolt"
                      },
                      {
                          "name": "fire ball",
                          "code": "launch explosive",
                          "target": "creature",
                          "damage": "level#6", "radius": 3, "element": "fire",
                          "message": "%s casts a fire ball"
                      }
                  And here's (part of) a creature record using those spells:
                  Code:
                      "name": "Filthy street urchin",
                      "nativeDepth": 0,
                      "experienceValue": 0,
                      "rarity": 2,
                      "magic": {
                          "frequency": 1,
                          "spells": ["fire ball", "haste self"]
                      },
                  The # syntax in the spell definitions is representing dice rolling in Pyrel's expression evaluator -- I couldn't use "d" because the evaluator assumes that alphabetic strings indicate variable names, and I don't understand the code well enough (d_m wrote it) to change that.

                  One thing I came to realize while working on all this is that Pyrel will be playable well before it has all of the weird little edge cases of Vanilla. For example:

                  * No diminishing returns on speed
                  * All elemental attacks have the same damage cap regardless of source (c.f. darkness storms in Vanilla beating out the cap on darkness damage)
                  * No stacking temporary/permanent resists
                  * Square field of view / square explosions

                  The reason for this is that I'm focusing on implementing the core functionality first, and just leaving room for edge cases for later -- but because they're edge cases they have relatively little impact on gameplay and I'm sure someone will be playing the game as soon as it's winnable.

                  As a side effect, we should get some interesting insight on how necessary some of these edge cases are. I look forward to it.

                  Similarly, I'm not bothering to look too closely at Vanilla's formulae right now. For example, I've set the damage-over-time from cuts to be (timer / 10) HP per turn, and for poison to be (timer / 50) (and meanwhile, the timer for poison is set to be (damage / 5) turns). I expect Vanilla's values to be completely different, but I'm not especially bothered by that on the assumption that most of these values were never really seriously inspected to begin with.

                  Of course, changing those is trivial, so if we decide that Vanilla's values are really the ones to use, then putting them in is easy. I just haven't wanted to spend time on tracking down the relevant source code.

                  Comment

                  • Magnate
                    Angband Devteam member
                    • May 2007
                    • 5110

                    #69
                    Originally posted by Derakon
                    I'm semi-seriously considering adding The Filthy Street Ãœrchin as a secret boss character now.
                    Do it!
                    I'm pretty satisfied with the basic spellcasting framework now. I've added an element system, including acid damage to armor, destruction of inventory items, and destruction of floor items. Elemental effects are defined in a new file, element.txt:
                    I'm very encouraged to see this. I got as far as speccing out an element.txt for V/v4 before you started Pyrel. My collected thoughts are at http://trac.rephial.org/wiki/NewEffects, in case it is helpful.
                    New spells can be created fairly easily; here's some example spell definitions:
                    These don't seem to allow for spells which deliver multiple effects (e.g. cure poison, heal hp, remove stunning etc.). Are you clear on the differences between spells and procs? I had always imagined that the "atomic" effects would be procs, and that spells would be calls of one or more procs with certain variables set (e.g. amounts of damage/healing, range etc.).
                    The # syntax in the spell definitions is representing dice rolling in Pyrel's expression evaluator -- I couldn't use "d" because the evaluator assumes that alphabetic strings indicate variable names, and I don't understand the code well enough (d_m wrote it) to change that.
                    I can probably help here, since he wrote it for me to do the evaluations of formulae in lootTemplates. Obviously d_m himself could be more help when he's around ;-)
                    One thing I came to realize while working on all this is that Pyrel will be playable well before it has all of the weird little edge cases of Vanilla. For example:

                    * No diminishing returns on speed
                    * All elemental attacks have the same damage cap regardless of source (c.f. darkness storms in Vanilla beating out the cap on darkness damage)
                    * No stacking temporary/permanent resists
                    * Square field of view / square explosions

                    The reason for this is that I'm focusing on implementing the core functionality first, and just leaving room for edge cases for later -- but because they're edge cases they have relatively little impact on gameplay and I'm sure someone will be playing the game as soon as it's winnable.

                    As a side effect, we should get some interesting insight on how necessary some of these edge cases are. I look forward to it.

                    Similarly, I'm not bothering to look too closely at Vanilla's formulae right now. For example, I've set the damage-over-time from cuts to be (timer / 10) HP per turn, and for poison to be (timer / 50) (and meanwhile, the timer for poison is set to be (damage / 5) turns). I expect Vanilla's values to be completely different, but I'm not especially bothered by that on the assumption that most of these values were never really seriously inspected to begin with.

                    Of course, changing those is trivial, so if we decide that Vanilla's values are really the ones to use, then putting them in is easy. I just haven't wanted to spend time on tracking down the relevant source code.
                    I'm sure this is the right approach. The question arises as to who, if anyone, will bother to add all the edge cases to make a version of Pyrel that plays exactly like a version of V - and, if nobody does, what that will mean for Pyrel. It won't be as different as Sil or FA, but different enough to dissatisfy V purists.

                    On a point of detail, I see no reason why DamageCap should be a property of an element, other than that this is how V does it. Logically damage caps should be set on a per-instance basis (i.e. part of the spell definition in the monster record). That would be much more flexible.
                    "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

                      #70
                      Originally posted by Magnate
                      These don't seem to allow for spells which deliver multiple effects (e.g. cure poison, heal hp, remove stunning etc.). Are you clear on the differences between spells and procs? I had always imagined that the "atomic" effects would be procs, and that spells would be calls of one or more procs with certain variables set (e.g. amounts of damage/healing, range etc.).
                      Mm, this is a fair point. I see a few possible options; the simplest would probably be for the code that parses spells from the creature record to check a new file, spells.txt, before it goes to proc_templates.txt. spells.txt would allow for spells that chain several procs together. If no entry in spells.txt is found for the specified spell name, then proc_templates.txt would serve as a fallback option.

                      I can probably help here, since he wrote it for me to do the evaluations of formulae in lootTemplates. Obviously d_m himself could be more help when he's around ;-)
                      Right, I spoke with him a bit while I was working on extending the evaluator; if he finds the spare time to work on it then that'd be ideal. But I don't think it's the end of the world to use '#' as the operator, so this is pretty low-priority for me.

                      The question arises as to who, if anyone, will bother to add all the edge cases to make a version of Pyrel that plays exactly like a version of V - and, if nobody does, what that will mean for Pyrel. It won't be as different as Sil or FA, but different enough to dissatisfy V purists.
                      What I expect will happen is that people will playtest, say "I don't like that this particular edge case isn't handled", and then someone will implement that edge case. And the ones that aren't complained about won't be changed.

                      On a point of detail, I see no reason why DamageCap should be a property of an element, other than that this is how V does it. Logically damage caps should be set on a per-instance basis (i.e. part of the spell definition in the monster record). That would be much more flexible.
                      This is the default damage cap; if you want to specify a different one for a specific spell then you can of course do so. I haven't added the code for applying damage caps yet, though.

                      Comment

                      • Magnate
                        Angband Devteam member
                        • May 2007
                        • 5110

                        #71
                        Originally posted by Derakon
                        Mm, this is a fair point. I see a few possible options; the simplest would probably be for the code that parses spells from the creature record to check a new file, spells.txt, before it goes to proc_templates.txt. spells.txt would allow for spells that chain several procs together. If no entry in spells.txt is found for the specified spell name, then proc_templates.txt would serve as a fallback option.
                        Sounds excellent.
                        This is the default damage cap; if you want to specify a different one for a specific spell then you can of course do so. I haven't added the code for applying damage caps yet, though.
                        But why do you want a per-element damage cap, ever? Why not have a single global default damage cap?

                        My point is that damage caps are a kludge. If something is too dangerous it needs rebalancing for the case where it's too dangerous, not capping.
                        "Been away so long I hardly knew the place, gee it's good to be back home" - The Beatles

                        Comment

                        • Pete Mack
                          Prophet
                          • Apr 2007
                          • 6883

                          #72
                          Originally posted by Magnate
                          Sounds excellent.

                          But why do you want a per-element damage cap, ever? Why not have a single global default damage cap?

                          My point is that damage caps are a kludge. If something is too dangerous it needs rebalancing for the case where it's too dangerous, not capping.
                          Because doing it your way would drive players crazy.
                          Damage caps are for breath attacks only. If different monsters have different max damage (beyond the max damage imposed by HP), then players will have to have specific knowledge of those monsters that do extra damage.

                          Comment

                          • Derakon
                            Prophet
                            • Dec 2009
                            • 9022

                            #73
                            Regarding damage caps: your average fire hound does ~11 damage/breath because they have 35HP. Meanwhile Glaurung has 6250HP; if he used the same formula with no cap then he'd be doing over 2k damage with a breath when he's at full health. Even resisted, that's instadeath for, say, most mages.

                            So I'd say we can either keep the damage cap system, or we can change the formula for calculating damage so that it has a nonlinear dependency on creature health (like a logarithmic formula). Functionally that works out to being like a damage cap -- damage asymptotically approaches some fixed value but does not cross it -- but it's not a hard "damage cannot ever exceed this". On the flipside, that makes it very hard to determine how much damage you can expect a given attack to deal -- assuming we keep the HP dependency of breath damage. Which I would like to do; I think it's a neat mechanic.

                            Also, my plan for damage caps attaches them to the elements, not to the attack type. While most attacks in Vanilla that hit the cap are breath attacks, darkness storms and nether balls can potentially hit the damage cap. I understand Vanilla doesn't work this way, but I think it's more intuitive and the effect on balance will be minimal.

                            Comment

                            • Magnate
                              Angband Devteam member
                              • May 2007
                              • 5110

                              #74
                              Originally posted by Derakon
                              Regarding damage caps: your average fire hound does ~11 damage/breath because they have 35HP. Meanwhile Glaurung has 6250HP; if he used the same formula with no cap then he'd be doing over 2k damage with a breath when he's at full health. Even resisted, that's instadeath for, say, most mages.

                              So I'd say we can either keep the damage cap system, or we can change the formula for calculating damage so that it has a nonlinear dependency on creature health (like a logarithmic formula). Functionally that works out to being like a damage cap -- damage asymptotically approaches some fixed value but does not cross it -- but it's not a hard "damage cannot ever exceed this". On the flipside, that makes it very hard to determine how much damage you can expect a given attack to deal -- assuming we keep the HP dependency of breath damage. Which I would like to do; I think it's a neat mechanic.
                              So do I - but I can imagine a scenario where I want different cap for the same element for two different monsters. Let's say we want to give a mumak cousin a fire breath - we'd want a lower cap than for more powerful monsters with the same hp.
                              Also, my plan for damage caps attaches them to the elements, not to the attack type. While most attacks in Vanilla that hit the cap are breath attacks, darkness storms and nether balls can potentially hit the damage cap. I understand Vanilla doesn't work this way, but I think it's more intuitive and the effect on balance will be minimal.
                              Yes, no problem with this at all.
                              "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

                                #75
                                Originally posted by Magnate
                                So do I - but I can imagine a scenario where I want different cap for the same element for two different monsters. Let's say we want to give a mumak cousin a fire breath - we'd want a lower cap than for more powerful monsters with the same hp.
                                I could see that. I think this is one of those things that you'd possibly change if you were working on a variant -- it's not especially difficult to subvert the damage cap if you think it's a good idea.

                                I threw together a first pass at defining all of the resistable elements. You'll note that instead of experience drain here, I have temporary level drain. It's an idea I've always wanted to try out, since I think it'd be a lot more interesting gameplay-wise. No permanent damage to your character, however the immediate ramifications are much more severe than losing experience.

                                I added the "shortName" field with an eye towards the player status display and monster memory. Here's what the player status display looks like currently:



                                Obviously missing several fields, but they won't be hard to add. I gave the player a semi-random assortment of resistances for this screenshot. I figure that FA, SI, etc. can be fit in as well without taking up much space. More importantly, a similar display can be used in monster memory, which I want to have use a more tabular arrangement like this old example. Ideally you'd also be able to mouse over an entry in the sidebar and get a popup that gives you more information on that particular stat.

                                Also notice that I'm using blue for "normal value" instead of green. . This is mostly as a nod towards red-green colorblind players. I guess that this could be an option, but I don't think it's an especially huge deal for non-colorblind players to get used to blue instead of green. As your HP (or other stats) get depleted, the color will smoothly cycle through purple to red, using HSV (hue-saturation-value) to do color rotations.

                                Oh yeah, and the STR/INT/etc. stats will have linearized display. No more 18/x stats.

                                Comment

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