Pyrel dev log

Collapse
X
 
  • Time
  • Show
Clear All
new posts

  • Derakon
    replied
    You're right that there are clearly aspects of terrain that should be handled by flags. Visibility, walkability, etc. should be handled by flags. I was thinking more about the terrain transitions and how to properly handle them in code. Here are some examples of possible terrain transitions:

    * closed door -> (open) -> open door
    * closed door -> (bash) -> (open door or broken door)
    * repeat above for locked and jammed doors
    * most walls -> (tunnel) -> no terrain
    * curtain -> (open or move) -> opened curtain

    This kind of thing makes me want to encode the valid state transitions into the terrain record. The main problem here is that many of the transitions have a difficulty rating and/or a progress value, or may generate items when completed (for treasure veins or rubble), or the like. So there's a lot of complexity here.

    It seems clear I should be trying to leverage the proc/effect code here somehow. Procs are stateful, so I could handle progress that way. That still leaves me with the problem of how to represent all this in the records, though. And I really can't see a way I can avoid having a separate entry for each of normal, locked, and jammed doors, for example...

    (Note that I'm omitting a bunch of stuff like visibility/walkability/etc. flags, and display info, for the sake of brevity)
    Code:
    {
        "name": "granite wall",
        "interactions": (
            {
                "action": "tunnel",
                "procs": ({"name": "tunnelWall", "difficulty": 1000})
            }
        )
    },
    {
        "name": "quartz vein",
        "interactions": (
            {
                "action": "tunnel",
                "procs": ({"name": "tunnelWall", "difficulty": "200})
                )
            }
        )
    },
    {
        "name": "quartz vein with treasure",
        "ancestor": "quartz vein",
        "interactions": (
            {
                "action": "tunnel",
                "postProcs": ({"name": "dropTreasure", "amount": "10+5d10M800"})
            }
        )
    },
    {
        "name": "door",
        "interactions": (
            {
                "action": "open",
                "procs": ({"name": "replaceTerrain", "replacement": "open door"})
            },
            {
                "action": "bash",
                "procs": ({"name": "bashDoor", "difficulty": "1+M10"}, 
                    {"name": "replaceTerrain", "replacement": "broken door"}
                )
            }
        )
    },
    {
        "name": "locked door",
        "ancestor": "door",
        "interactions": (
            {
                "action": "open",
                "preProcs": ({"name": "unlockDoor", "difficulty": "1+M10"})
            }
        )
    }
    So the way I envision the procs working here is, they execute in a chain, and each one has the ability to halt execution of the chain. For example, when bashing a door, first you have to pass the "bashDoor" proc's difficulty check before you're allowed to execute the "replaceTerrain" proc. Terrain that inherits from other terrain can insert its procs before or after the procs that the ancestor terrain implements -- for example, quartz vein with treasure adds on a proc after the "tunnelWall" proc, which thus will only execute when tunnelWall returns True -- i.e. when tunnelling succeeds.

    This is pretty verbose, and it doesn't let you bash a door and end up with a non-broken door afterwords. And I'm not certain that the execution-chain system is going to handle all the different use cases I can think of.

    Leave a comment:


  • Nick
    replied
    Originally posted by Derakon
    I'm starting to wonder if I shouldn't forgo a terrain edit file altogether and just directly instantiate a bunch of classes for the different types of terrain I want to have. It seems like it would be more or less impossible to have anything except a cosmetic difference without modifying the code anyway, unless I want to make for really complicated terrain records.
    One of the things to think about here is how many different terrain flags you have, and in how many ways they can be put together. If the code is all based on flags and there are enough of them, you can (for example) add a terrain type which holds objects, doesn't hold traps, blocks line of sight but not spells and is illuminated by torchlight quickly and easily to an edit file without having to mess with code at all. In that case I guess you would have classes generated by the edit file ... but I haven't really thought that bit through.

    Leave a comment:


  • Derakon
    replied
    I consider the first pass on equipment bonuses and procs to be complete now. Actually it was complete about a week ago but I didn't get around to posting here. There are of course tons of holes in the implementation, but they should all be missing-content holes, not missing-framework holes.

    It bugged me how stupidly slow the draw code was, so I've sped it up a touch. It's still pretty dumb, but at least it's not iterating over the contents of every visible cell on every refresh now. Doesn't mean that it won't need to be optimized further, but at least it's not nagging at my brain any more.

    I think the next serious thing to touch on is terrain, as a prelude to writing the level generation framework. I took a few looks at Angband's terrain edit file and decided I don't really want to just port that straight over like I more-or-less did with item and creature records. That means I get to come up with my own designs.

    First off, in Pyrel "terrain" isn't meaningfully distinct from any other interactable object. Walls are simply Things that sit in cells on the map and obstruct movement. Doors would be walls that can be opened. Floors are implicit. So there's no terrain layer, no traps layer, none of that.

    What does that mean as far as terrain is concerned? I was thinking that perhaps terrain is just the non-item, non-creature stuff that is added to the map during map generation. That's a pretty broad categorization, though, which means that the terrain records (the stuff that goes into the "edit files") would need to be very flexible, with each record having only very few required entries -- pretty much just the name and any display information.

    I mean, doors can be opened, locked, unlocked, bashed, or tunneled through, each of which results in some kind of state transition (well, tunneling destroys the door outright). Walls can just be tunneled through. Stairs can only be interacted with via the < and > commands. These are a lot of disparate abilities. Current Angband handles them via flags. For example, up stairs have the flags

    PWALK | PPASS | MWALK | MPASS | LOOK | EXIT_UP

    meaning that players and monsters can walk on them freely, they're notable enough to jump to with the "look" command, and they serve as an upwards-facing exit out of levels. Some of them make sense as flags -- walkability, for example, is already effectively handled as a flag in Pyrel (walkable objects are not in the BLOCKERS container). Some of them I'd want to represent as a state transition, though.

    For example, opening a door results in an open door; clearly the door should be a member of the OPENABLE container and thus implement the onOpen function. That's nice an atomic. But bashing a door down is not atomic -- the player can weaken the door several times before getting through, while any individual bash attempt may be completely ineffectual. Moreover the difficulty of bashing a door increases with depth. Tunneling through a wall is atomic (you either succeed or you fail, with no memory), but the wall may contain an item (in rubble, or treasure veins), which may or may not be visible before tunneling, and whose value depends on depth. So how do I represent all that?

    What we're basically dealing with here are building blocks for levels. However, I suspect they need to be agnostic of the levels they're being placed into, because so much of that depends on the level generation code. For example, while I could directly encode the difficulty of picking a lock into the terrain record as e.g. 2+1d4M8 or something, who's to say that the level generator wouldn't want to make a vault blocked off by nigh-impossibly-difficult locks?

    I'm starting to wonder if I shouldn't forgo a terrain edit file altogether and just directly instantiate a bunch of classes for the different types of terrain I want to have. It seems like it would be more or less impossible to have anything except a cosmetic difference without modifying the code anyway, unless I want to make for really complicated terrain records.

    I don't have any firm answers, yet, but this is the kind of thing I'm thinking about. Feel free to chime in if you have suggestions.

    Leave a comment:


  • Derakon
    replied
    Progress has been a bit slow lately because I started playing a videogame (King's Bounty: The Legend), and boy howdy do those things have a way of sucking up your spare time. Anyway, I've completed a first attempt at allowing items to modify other items -- I can equip a Ring of Prowess and it will improve the apparent prowess bonus on my equipped weapon. However, I think I want to change the mechanism, since it doesn't handle unequipping the weapon while the ring is still equipped very well. I see two options here:

    1) When the ring is equipped, it applies a bonus to all equipped items in the appropriate slot(s), and adds a trigger to their onUnequip functions to remove that bonus.
    2) Extend the equipment system to include triggers associated with specific slots and actions; the ring would modify that instead of directly modifying the equipped item(s).

    I'm also going to need something like #2 for items that modify stuff in your inventory, since otherwise every time an item gets picked up I'll have to iterate over all my equipment to see if anything should be done about it. So I'm leaning towards #2 as a general-purpose solution.

    Currently 2314 lines, of which 536 are comments and 284 are whitespace. Not a bad ratio! Even if I'm being a bit liberal with the \todo tag...

    Leave a comment:


  • Magnate
    replied
    Thank you. All sounds grand. Hope you're still enjoying it.

    Leave a comment:


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

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

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

    Leave a comment:


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

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

    Leave a comment:


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

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

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

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

    Leave a comment:


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

    Leave a comment:


  • Narvius
    replied
    The only sane way to handle multiple eligible equipment slots is likely to diplay a list of possible slots and let the user pick one. Especially once we consider monster races anything less specific starts to fall apart.
    Even if we consider two ring slots, one occupied, one free, and the user equipping an additional one, automatically filling the free slot may not always be the desired behavior.

    Leave a comment:


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

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

    Leave a comment:


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

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

    Leave a comment:


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

    Leave a comment:


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

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

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

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

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

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

    Leave a comment:


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

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

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

    Leave a comment:

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