Pyrel dev log, part 3
Collapse
X
-
No, this is the only place in the game where such concepts are duplicated, and changing the entire stat system to accommodate it (by using tuples as keys instead of strings) doesn't make much sense to me. I don't see how it enables code re-use anyway -- the important thing is the calculations, and those can be written so that they accept the relevant stats as parameters. The only bit of duplication would be where we go "if you're doing melee, then use this set of stats; otherwise, use this set".Comment
-
No, this is the only place in the game where such concepts are duplicated, and changing the entire stat system to accommodate it (by using tuples as keys instead of strings) doesn't make much sense to me. I don't see how it enables code re-use anyway -- the important thing is the calculations, and those can be written so that they accept the relevant stats as parameters. The only bit of duplication would be where we go "if you're doing melee, then use this set of stats; otherwise, use this set"."Been away so long I hardly knew the place, gee it's good to be back home" - The BeatlesComment
-
Mm, I didn't explain myself very well.
Functionally there's no real difference between a tuple ('melee', 'finesse') and a string 'meleeFinesse', except that I'd have to modify the game code to load tuples if we decided to support them. The code is not made any easier to deal with if we use tuples -- we still have to track down the (foo, 'finesse') skill to get the relevant finesse score.
Basically the difference boils down to doing one of these two things:Code:getDamage(stats['meleeFinesse'], stats['meleeProwess']) getDamage('melee')
Comment
-
Basically the difference boils down to doing one of these two things:Code:getDamage(stats['meleeFinesse'], stats['meleeProwess']) getDamage('melee')
Zaiband: end the "I shouldn't have survived that" experience. V3.0.6 fork on Hg.
Zaiband 3.0.10 ETA Mar. 7 2011 (Yes, schedule slipped. Latest testing indicates not enough assert() calls to allow release.)
Z.C++: pre-alpha C/C++ compiler system (usable preprocessor). Also on Hg. Z.C++ 0.0.10 ETA December 31 2011Comment
-
As an added note, presumably different combat styles would use different calculations for damage anyway -- missile combat needs to synthesize a "weapon" out of your bow and ammo, karate would need some means of deriving damage dice external to your weapon, etc. So there'd be no code re-use anyway.Comment
-
As an added note, presumably different combat styles would use different calculations for damage anyway -- missile combat needs to synthesize a "weapon" out of your bow and ammo, karate would need some means of deriving damage dice external to your weapon, etc. So there'd be no code re-use anyway.
stats->melee->finesse
stats->missile->finesse
and friends
are nicer to use than
stats->meleeFinesse
stats->missileFinesse
and so on.
The latter means you have many more names at the same hierarchical level, where the former is much neater and more logical. But it's quite possible that this consideration is entirely irrelevant in Pyrel, in which case I apologise for having wasted everybody's time ;-)"Been away so long I hardly knew the place, gee it's good to be back home" - The BeatlesComment
-
Mm, you could certainly organize things that way -- a hierarchical system for categorizing which stats go where. You'd use nested dicts for that, not tuples, but that's pretty irrelevant. The question again is, what does this buy us in terms of code re-use or cleanliness? Is it solely a matter of having a hierarchical structure instead of a flat one?
I guess my concern with setting up a hierarchy like this is that there's an implicit assumption that no stat outside of a given tier will care about the stats in that tier (e.g. AC never cues off of melee finesse). Of course you're under no obligation to obey that assumption, but it bugs me when the internal structure of data doesn't reflect how that data is used. Code should be as intuitive as possible all the time; it's hard enough to understand even when everything is internally consistent!
That said, I'll ask some of my other coding buddies what they think and if they can come up with some strong arguments one way or another.Comment
-
Mm, you could certainly organize things that way -- a hierarchical system for categorizing which stats go where. You'd use nested dicts for that, not tuples, but that's pretty irrelevant. The question again is, what does this buy us in terms of code re-use or cleanliness? Is it solely a matter of having a hierarchical structure instead of a flat one?
I guess my concern with setting up a hierarchy like this is that there's an implicit assumption that no stat outside of a given tier will care about the stats in that tier (e.g. AC never cues off of melee finesse). Of course you're under no obligation to obey that assumption, but it bugs me when the internal structure of data doesn't reflect how that data is used. Code should be as intuitive as possible all the time; it's hard enough to understand even when everything is internally consistent!
1. Neatness isn't the same as intuitiveness. All the stats being in the same tier makes for a very crowded and messy namespace, but is completely intuitive if they are all of equivalent level (i.e. don't cue off one another)
2. The game engine shouldn't contain any assumptions which can possibly be avoided. The whole point of your object-model approach, IIUC, was to set out only the lowest level assumptions about object interactions, and allow gameplay to be determined by rules that are not hard-coded. Hence all the careful thinking about designing the stat tiers so that they did not exclude any permutations of stat influences one might want to allow.
Thinking more about both these, especially the latter. There are of course *some* hard-coded assumptions - there have to be to process turns and so on."Been away so long I hardly knew the place, gee it's good to be back home" - The BeatlesComment
-
The whole point of your object-model approach, IIUC, was to set out only the lowest level assumptions about object interactions, and allow gameplay to be determined by rules that are not hard-coded. Hence all the careful thinking about designing the stat tiers so that they did not exclude any permutations of stat influences one might want to allow.
Thinking more about both these, especially the latter. There are of course *some* hard-coded assumptions - there have to be to process turns and so on.
Dunno how this applies to PyrelOne for the Dark Lord on his dark throne
In the Land of Mordor where the Shadows lie.Comment
-
True. I was thinking more about naming hierarchies than about code reuse but am let down by my lack of Python-fu. I am thinking that
stats->melee->finesse
stats->missile->finesse
and friends
are nicer to use than
stats->meleeFinesse
stats->missileFinesse
and so on.
The latter means you have many more names at the same hierarchical level, where the former is much neater and more logical. But it's quite possible that this consideration is entirely irrelevant in Pyrel, in which case I apologise for having wasted everybody's time ;-)
However, it should be though about pretty carefully. Unless you gain some amount of encapsulation or reuse from doing this, you're probably just shooting yourself in the foot.
There may also be an argument that using strutuctures with named fields (such as "named tuples") as a single function parameter might be less error prone than using two unnamed parameters, since calling a function with multiple arguments can lead to confusion as to which argument is which, especially if the arguments have the same type or similar names or whatever. (This can be alleviated with keyword args, but unfortunately there no way to force callers to use kwargs in Python AFAIK.)
IME this type of thing also tends to work out best when using aggregation rather than inheritance as an organizing principle -- simply because aggregation/abstraction boundaries tend to be clearer.Comment
-
A peripheral (you may say irrelevant) point that this made me think of - there's different strata of hard-coded. Within the current Angband code, there are things I don't touch except in extremis (z-*.c), things I try to avoid changing but will if necessary (util.c), things I know I need to change but try to keep it minimal (game-event.c), things I'm happy enough to change without thinking too hard (spells*.c) and things where I change stuff just for the hell of it (effects.c). And that's before I even get to the edit files.
Dunno how this applies to Pyrel
1. Things which are currently in .h files (or would be if I had ever finished what I intended to move into .h files) - these could easily be moved out of the code into modifiable text files (for some values of easily):
object flags (including slays and brands)
monster spells and their effects
elements
effects (of potions/scrolls/devices)
monster attack types
player spells
2. Things which are currently in .c files, which would need much more work to extract:
player stats and their bonuses
player class/race special abilities (although you can add flags for these to .txt files, they don't do anything until you add code for them)
combat calculations
monster AI
traps (though Gabe has extracted these in v4)
terrain effects
the scoring algorithm
That's not a bad list, when you consider all the things that it doesn't contain, which can be happily modified in text files: dungeon rooms, pits and vaults, stores, races and classes (other than special abilities), monsters, ego items, random names, flavours, hints, pain messages etc. etc.
My hope is that the object model of Pyrel will enable all these things to be not-hard-coded, though I accept that it will probably be simpler to hard-code some of them to start with."Been away so long I hardly knew the place, gee it's good to be back home" - The BeatlesComment
-
Anything that involves actual code will be "hard-coded" in Pyrel, because the data file format is strictly a data format -- Pyrel will not be interpreting code by loading it from data files. That's generally considered a bad practice because data cannot be trusted. In practice for Pyrel or Angband in general that's not a huge issue, but it's still a bad idea to assume that any particular data you load is going to do what you expect it to do.
Anyway, while the data files will still be just data, that doesn't mean that they can't refer to specific code and cause functions to be invoked. Basically all of the code in proc.py (which should probably be refactored at some point because it's gonna be huge otherwise) is functions that the data files can refer to. For example, if you wanted to, say, link a specific race's STR and CHA scores (they're as strong as they appear to be!) then you could modify their stats loadout to invoke the "percentage bonus based on other stat" proc, telling it to add a bonus to STR based on CHA, and a bonus to CHA based on STR. This wouldn't actually require you to write new code, since that proc already exists.
An awful lot of the game can be boiled down to procs. To take a more complicated example at random, let's say we want different monsters to have different mechanisms for monsters responding to the player making noise. What we do is the following:
1) Create a new proc trigger condition: "on noise"
2) Have a default proc to call when noise is caused, which has a chance of increasing the monster's awareness (until they wake up). This mimicks the current behavior. We stick this default proc into a standard monster template that all monsters use.
3) Override this proc for the monsters you want to behave specially, so that they have some different reaction (e.g. noise damages them, or aggravates them, etc.). Create a new proc for the new behavior.
Ideally, most of the engine should just be about providing hooks that the data files can latch onto, and implementing procs to be invoked when those hooks are triggered.Comment
-
Anything that involves actual code will be "hard-coded" in Pyrel, because the data file format is strictly a data format -- Pyrel will not be interpreting code by loading it from data files. That's generally considered a bad practice because data cannot be trusted. In practice for Pyrel or Angband in general that's not a huge issue, but it's still a bad idea to assume that any particular data you load is going to do what you expect it to do.
Anyway, while the data files will still be just data, that doesn't mean that they can't refer to specific code and cause functions to be invoked. Basically all of the code in proc.py (which should probably be refactored at some point because it's gonna be huge otherwise) is functions that the data files can refer to. For example, if you wanted to, say, link a specific race's STR and CHA scores (they're as strong as they appear to be!) then you could modify their stats loadout to invoke the "percentage bonus based on other stat" proc, telling it to add a bonus to STR based on CHA, and a bonus to CHA based on STR. This wouldn't actually require you to write new code, since that proc already exists.
An awful lot of the game can be boiled down to procs. To take a more complicated example at random, let's say we want different monsters to have different mechanisms for monsters responding to the player making noise. What we do is the following:
1) Create a new proc trigger condition: "on noise"
2) Have a default proc to call when noise is caused, which has a chance of increasing the monster's awareness (until they wake up). This mimicks the current behavior. We stick this default proc into a standard monster template that all monsters use.
3) Override this proc for the monsters you want to behave specially, so that they have some different reaction (e.g. noise damages them, or aggravates them, etc.). Create a new proc for the new behavior.
Ideally, most of the engine should just be about providing hooks that the data files can latch onto, and implementing procs to be invoked when those hooks are triggered.
In the meantime, is there anything in my previous post that you think might not be possible with procs?"Been away so long I hardly knew the place, gee it's good to be back home" - The BeatlesComment
-
player class/race special abilities (although you can add flags for these to .txt files, they don't do anything until you add code for them)
combat calculations
monster AI
traps (though Gabe has extracted these in v4)
terrain effects
the scoring algorithmComment
Comment