Pyrel dev log, part 6

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • LostTemplar
    Knight
    • Aug 2009
    • 629

    #61
    Add something like target_type to your spell information tables alongside with mana, etc.
    So e.g. identify will have target_type = "unknown item" and fire ball will have "map tile" and so on. Now you can gain all information before casting in UI level. This approach probably cannot replicate all Angband functionality, but everything really important can be done.

    Comment

    • Derakon
      Prophet
      • Dec 2009
      • 8820

      #62
      Originally posted by LostTemplar
      Add something like target_type to your spell information tables alongside with mana, etc.
      So e.g. identify will have target_type = "unknown item" and fire ball will have "map tile" and so on. Now you can gain all information before casting in UI level. This approach probably cannot replicate all Angband functionality, but everything really important can be done.
      I already have that, actually (though currently the only valid targets are "self" and "creature"); the problem is getting from the user which unknown item, map tile, etc. to target. This has to be done from within the Proc execution code, because the engine has no visibility into the consequences of invoking a Proc and thus can't really plan for what information they'll need beyond certain very limited basics.

      Comment

      • LostTemplar
        Knight
        • Aug 2009
        • 629

        #63
        If you know target type, then target can be obtained before actual spell_cast call, in the same UI function as spell itself, so you can avoid any callbacks.

        Well I don't probably understand correctly, but let's imagine that any spell have

        mana cost
        fail rate
        target type
        etc.
        effect_proc (target)

        so at first UI checks all conditions, then ask for target and then calls effect_proc.

        And e.g. monster AI will also consider target type to get a correct target.
        Last edited by LostTemplar; January 1, 2014, 17:06.

        Comment

        • Derakon
          Prophet
          • Dec 2009
          • 8820

          #64
          The problem is that you're assuming we can learn everything we need prior to the point at which we hand off execution to the Proc code. Imagine you have a weapon that, when you attack an enemy, may randomly give you the option to teleport that enemy away. Are we to determine, prior to making the attack, whether or not the player wants to potentially teleport the target?

          Or imagine a spell that lets you set a random number of control points and then fires chain lightning between those points. How many targets do we need? It's not decided until the Proc starts executing.

          Or imagine a spell that pulls up a list of all items on the level and lets you select one to be made to explode. Never mind that this is a bit of a weird situation, but do we really want to write a "list all items on level and pick one" UI prompt that exists in the engine layer, instead of letting the Proc gin up its own special-case Prompt?

          Sorry if I'm harping on this a bit, but the bottom line is that we can't make assumptions about how Procs work if we're to allow them the flexibility that gives them so much power.

          Comment

          • LostTemplar
            Knight
            • Aug 2009
            • 629

            #65
            The problem is that you're assuming we can learn everything we need prior to the point at which we hand off execution to the Proc code. Imagine you have a weapon that, when you attack an enemy, may randomly give you the option to teleport that enemy away. Are we to determine, prior to making the attack, whether or not the player wants to potentially teleport the target?
            You should really be sure you want this. It is a great commitment. If you want then my point is not valid. However spells with cast_on_strike may be limited to either self or attack target, etc. All modern games use this approach and it is reasonable.

            Comment

            • Derakon
              Prophet
              • Dec 2009
              • 8820

              #66
              Originally posted by LostTemplar
              You should really be sure you want this. It is a great commitment. If you want then my point is not valid. However spells with cast_on_strike may be limited to either self or attack target, etc. All modern games use this approach and it is reasonable.
              Yep, I'm sure. And I know it's possible to accomplish what I want in terms of data management; I just need to figure out the best way to do it. The way I'd originally written used threads, and was pretty straightforward to use if a bit tricky in the implementation (which your average use case wouldn't need to care about); frankly I don't understand the system that replaced it (which was written by mtadd), which is the major problem here.

              Comment

              • Derakon
                Prophet
                • Dec 2009
                • 8820

                #67
                Here's an example of what I'm trying to do:
                Code:
                def main():
                    count = 2
                    for i in xrange(3):
                        runner = sub()
                        runner.next()
                        while True:
                            try:
                                count += 1
                                print "Sending",count,"to runner"
                                runner.send(count)
                            except StopIteration:
                                print "Stopped at",count
                                break
                            
                def sub():
                    sub2()
                def sub2():
                    total = yield
                    print "Received total",total
                    print sum(range(total))
                    
                main()
                
                # Prints:
                Traceback (most recent call last):
                  File "test.py", line 22, in <module>
                    main()
                  File "test.py", line 5, in main
                    runner.next()
                AttributeError: 'NoneType' object has no attribute 'next'
                Here the sub() function represents the Command's contextualizeAndExecute() function, and sub2() represents something called by that function. For example, for the Magic Missile spell the call chain is something like: Item.onUse() -> CastCreatureSpellProc.trigger() -> Spell.trigger() -> ProjectileProc.trigger().

                As I understand it, in order for the next() and send() calls to actually work, every single function in the call chain needs to be invoked like "for i in func(): yield i". I'd love to be wrong.

                Comment

                • Magnate
                  Angband Devteam member
                  • May 2007
                  • 4916

                  #68
                  Originally posted by Derakon
                  frankly I don't understand the system that replaced it (which was written by mtadd), which is the major problem here.
                  Sorry to state the bleeding obvious but have you asked him to write a wiki page on it? Last time someone asked how item generation worked in Pyrel I pointed them at the wiki and they went away happy ...
                  "Been away so long I hardly knew the place, gee it's good to be back home" - The Beatles

                  Comment

                  • Derakon
                    Prophet
                    • Dec 2009
                    • 8820

                    #69
                    Originally posted by Magnate
                    Sorry to state the bleeding obvious but have you asked him to write a wiki page on it? Last time someone asked how item generation worked in Pyrel I pointed them at the wiki and they went away happy ...
                    I sent him a message via Bitbucket a day or two ago to get his opinion on this issue; I haven't gotten a response yet. I went onto #python on Freenode and asked for assistance with coroutines, and the general opinion seemed to be "No, you can't use them the way you want to be able to use them", so I'm working on implementing a threads-based approach instead. I don't think I understood the limitations of the coroutines-based approach when I accepted mtadd's patch, alas.

                    Comment

                    • Magnate
                      Angband Devteam member
                      • May 2007
                      • 4916

                      #70
                      Originally posted by Derakon
                      I sent him a message via Bitbucket a day or two ago to get his opinion on this issue; I haven't gotten a response yet. I went onto #python on Freenode and asked for assistance with coroutines, and the general opinion seemed to be "No, you can't use them the way you want to be able to use them", so I'm working on implementing a threads-based approach instead. I don't think I understood the limitations of the coroutines-based approach when I accepted mtadd's patch, alas.
                      Ah well, we live and learn. Kudos to you for trying and adapting.
                      "Been away so long I hardly knew the place, gee it's good to be back home" - The Beatles

                      Comment

                      • Derakon
                        Prophet
                        • Dec 2009
                        • 8820

                        #71
                        Okay, I'm pretty sure I didn't completely muck up the merging, so the latest master on my repo should have the spellcasting changes and a new command-handling system that makes use of events and threads instead of coroutines. The spellbook "Magic for Beginners" now has Phase Door and Magic Missile in it, and they should both work. Obviously all the rest of the spells need to be implemented; so do failure rates, mana costs, proper spell display (and spell ordering; currently the book is unordered so the contents show up in a random order each game), etc.

                        Still, the basics are done, so if you want to implement new player spells, well, now you can. Each spell is its own object:
                        Code:
                        {"templates": "arcane attack spell",
                        "nameInfo": {"variantName": "Magic Missile"},
                        "procs": [{"name": "cast creature spell", "spellName": "magic missile",
                            "damage": "(3+level)#4", "triggerCondition": "item use"}],
                        "spellInfo": {"manaCost": 1, "baseFailureRate": 25, "minLevel": 1}
                        },
                        And the spells are all listed in the entry for the spellbook:
                        Code:
                        {"index": 460, "subtype": "[Magic for Beginners]",
                        "templates": "magic book",
                        "containerContents": [
                            ["arcane attack spell", "Magic Missile"],
                            ["arcane mobility spell", "Phase Door"]
                        ],
                        "nameInfo": {"variantName": "[Magic for Beginners]"},
                        "allocatorRules": [{"commonness": 50, "minDepth": 5, "maxDepth": -1}]},
                        The various subtypes of arcane spells all have differing stats; the base "arcane spell" object template establishes some baselines, and then the subtypes modify them:
                        Code:
                        {"templateName": "arcane spell",
                        "templates": "spell",
                        "classToSpellStats": {
                            "mage": {"minLevelMultiplier": 1.0, "failureMultiplier": 1.0,
                                    "manaMultiplier": 1.0},
                            "ranger": {"minLevelMultiplier": 1.5, "failureMultiplier": 1.5,
                                    "manaMultiplier": 1.25},
                            "rogue": {"minLevelMultiplier": 2.0, "failureMultiplier": 1.5,
                                    "manaMultiplier": 2.0}
                        }
                        },
                        
                        {"templateName": "arcane attack spell",
                        "templates": "arcane spell",
                        "classToSpellStats": {"rogue": {"minLevelMultiplier": 100.0}}
                        },
                        {"templateName": "arcane mobility spell",
                        "templates": "arcane spell",
                        "classToSpellStats": {
                            "ranger": {"minLevelMultiplier": 2.0, "failureMultiplier": 1.5,
                                    "manaMultiplier": 1.5},
                            "rogue": {"minLevelMultiplier": 1.0, "failureMultiplier": 1.0,
                                    "manaMultiplier": 1.5}
                        }
                        },

                        Comment

                        • mtadd
                          Rookie
                          • Nov 2011
                          • 22

                          #72
                          Sorry about the delay, I haven't checked my email the past couple days.

                          The command handler driven with coroutines was instituted really early in the project. The choice to use coroutines over threads could've been simply due to my aversion to threads in Python due to the GIL (global interthread lock) and its performance problems, so I was using coroutines to be able to perform asynchronous context switching in a single-threaded environment. Using the events module looks to be a great solution, obviating the need for the clunky nesting of recursed coroutines. Note that in Python 3.3 and later, a new language keyword "yield from" was added to simplify using coroutines in such a manner q.v. PEP 380 -- Syntax for Delegating to a Subgenerator

                          I haven't tried running your latest update yet, is it working as intended? What GUI platforms are currently supported?

                          Comment

                          • Derakon
                            Prophet
                            • Dec 2009
                            • 8820

                            #73
                            All of the changes were done solely in the CommandHandler class that the various GUI front-ends inherit from, so they should all work. That said, I've only tested the wxPyrel frontend.

                            Coroutines vs. threads ought to have basically identical performance, GIL or no, since the I/O thread is always blocking on the command thread to complete, or the command thread is blocking on the I/O thread to provide input; either way there's only one thread running at a time. I could well believe that coroutines are ever so slightly more performant when written properly, just because you don't need to invoke the scheduler to allocate resources to the different threads, but I can't imagine it making a noticeable difference.

                            Put another way: we have far greater performance concerns.

                            Comment

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