Temple of The Roguelike Forums
Development => Programming => Topic started by: jasonpickering on January 12, 2014, 05:44:29 PM
-
So I migh have asked this before, but how do you guys usually go about deciding monsters for a level?
Right now I build an array of all the possible monsters but the problem is that I need to set how many times the monster shows up in the array in each level meaning I need to have a monster probability for each monster for each floor. Do you guys do it a different way?
-
Depends so much on what results you want. Do you want a completely random mix of monsters on each level? Or should levels be a bit thematic, or choose a few kinds of monster to emphasize (lots of orcs on this level, lots of jellies on the next, etc.), with a few wildcards in the mix (there could always be a greater demon around the corner)?
In any scheme, you're probably going to be starting with some metric for how difficult a monster is. One common way is to specify the "native level" of the monster. On level 7, you should see lots of level 7 monsters, as well as some level 6/8 monsters, fewer 5/9s, etc. until you hit the point where you feel like it's too unreasonably out of depth. Let's say you make half your monsters same-depth, and divide the remaining half between deeper and shallower. For each of those cases, maybe give it an 80% probability of being one level out of depth. The other 20% of the time, give it an 80% probability to be two levels out of depth, etc. Exponentiate like that until you hit a limit, and just cut it off there.
So, that's a reasonable way to go, but it doesn't treat monsters any differently. If that's not what you want, you might think about spawning packs of monsters as a unit or other things like that.
-
So I always see that people say like there us an 80% chance if spawning how does that actually work? Is each monster just given a percentage to show up but what does a person actually do to figure that out? Do they fill an array? I guess I am more wondering the actual logic then the code. I like the idea of monster difficulty.nyou could almost do it like a level has a budget of monster levels.
-
If you've got an array of relative "weights" for each monster to be generated on a given level, you can generate an array of probabilities by dividing each weight by the sum of all monsters' weight. Then you can find the array of cumulative probabilities by getting the cumulative sum of probabilities. That will assign each monster in sequence a number that starts at 0 for the the first monster and goes up to something less than 1 for the last monster.
With the cumulative probabilities calculated, you can just pick a random float from 0 to 1. The monster you generate is the last one whose cumulative probability is less than the random number.
This is more or less what std::discrete_distribution does in c++. You can skip the division step and choose random numbers between 0 and total weight if you want, I only did that so that I could talk in terms of probability. You can use binary search in the last step, but for this application it's not too important.
-
Well Jason I keep it simple myself. My boardgame design background shows through here. For KlingonRL, for example, each system was either:
Friendly (Klingon) (star system 1)
Hostile (Federation) (star systems 21-25)
Neutral (Systems 2-17).
-Special Romulan (system 19)
-Special Neutron Star and Space Whales (system 20)
-Pirate Base (system 18)
In each system I had a sort of table you rolled on. For example the Klingon System always had between 3-6 Klingon cruiser rolling around. So...
If System_Number = 1 then
Roll Random (3-6)
Place that number of Klingons, and don't place them on top of each other or on a planet or something dumb.
For Federation systems it was only slight more complex...
If System_Number = 21-25
Roll Random (4-8) for the number of ships to place.
Place that number of ships in valid locations, randomly.
Roll (1-100)
-1-10 then the ship is a Missile Cruiser
-11-20 Science Vessel
-21-30 Battleship
-31 - 100 Standard Cruiser
So if you do the math, you are going to get AT LEAST 4 ships in a Fed system, and usually at least one of those is going to be a Missile Cruiser/Science Vessel/Battleship with the rest as Standard Cruisers.
To apply this generically I would
1. Decide how many baddies are on that level, can be a random number in a certain range.
2. Do a placement loop for each baddie, and choose from a sort of weighted list.
You'll note that this is pretty much exactly how D&D handles wandering monsters. You check what terrain you are in, or level, and then you roll on that table.
I'm not sure how many levels you have, but you can make a different table for each level, or you can make a different table for each level theme. It's really game specific. You can mix them as well, adding modifiers for the deeper levels.
For example a fire level, deep in the dungeon would have more and harder baddies.
Number_of_Monsters = 4-8 plus Depth/10. That would give you +1 monster every 10 levels.
Now place that many monsters by looping through the Fire Level Table.
Roll 1-20 (plus Depth)
-1-9 = Lil Sparky (by depth 9 there are no more of these)
-10-18 = Lil Flamer (by depth 18 there are no more of these)
-19-27 = Big Sparky (rare on level 1, getting more common as level's progress, then cut out at level 27)
-28-36 = Big Flamer
-37+ = Fire Mini Boss (cannot show up until level 17, 1/20 or 5% chance, by level 36 they are all you get)
Of course how you place your monsters, how many will spawn, and how level affects that, and what types and if the type depends only on depth or if level theme has a say...all that is up to the developer. Note in KlingonRL depth did not matter, the player got weaker over time unless they refueled and repaired and progression was nonlinear, so leveling and increasing in difficulty by level design was not necessary.
When I made Sun Crusher!!!, which was linear, every 3 levels a new type of more difficult baddie showed up, and each level had an increased number of baddies and an increased % chance of a harder baddie showing up. It was only 9 levels though, so I did it by set tables.
Number_of_Baddies = 2-4 plus (level/3) (so level 1 had 2-4 baddies, and level 9 had max 5-7)
Level 1- 3 = place all Easy Baddies.
Level 4 - 6 = alternate 1 easy and 1 medium (so they end up being roughly equal amounts easy and medium)
Level 7 - 9 = alternate placing 1 medium and 1 hard (no more easy baddies)
Boss level 10 = all hard baddies, no more mediums.
Anyway that's how I've done it in the past. The level depth modifies the number of bad guys (sometimes) then run through a table depending on the level theme (and/or depth) and if it makes sense for your game you can add modifiers to your table roll depending on the depth.
-
For my 7DRL coming up this year I'm not doing fully procedural levels, but rather doing preset boards (single screen predesigns) with plenty of spaces that randomize, thematic skinning, etc... there will be spawn points on each board, each spawn point creates one monster. That monster will come from a table according to the theme of the level (castle, crypt, fire, ice, cave, etc...) , there will be 5 monsters associated with each level theme, but you will see the first 1 only on level 1 and as you progress deeper the others will start showing up.
It's not completely set yet how many levels I'll have, but say there are 20 levels. You'll not see the 2nd monster type until level 2 (and then it will be rare), you'll see the 3rd by level 6, etc...with earlier monster types slowly being replaced by later ones.
This is easy to do with a simple table roll plus depth.
Example:
Castle_Theme
Roll 1-5 (plus depth) Max Depth = 20
-2-6 = Rat
-7-11 = Squire
-12-16 = Archer
-17- 21 = Guard Dog
-22+ = Knight
Notes by level
-1 Rats only
-2 Squires show up
-6 No more Rats, Squires only
-7 Archers arrive
-11 No more Squires, Archers only
-12 Guard dogs arrive
-16 No more Archers, Guard Dogs only
-17 Knights arrive.
Since it's a 7DRL I might only get one theme done, 5 monsters, 20 levels. Probably the Ice theme...
-
man that is a lot of info to absorb.
I like the simplicity of the dice tables from D&D which I had completely forgotten about. I am reworking it a bit to get a better distribution of characters throughout the 10 levels I am trying. currently my table looks like this:
Roll 1-7 (plus depth) Max Depth = 10
-2-5 = Slime
-6-9 = Demon
-10-13 = Fire Elemental
-14- 17 = Wolf
It needs to have a few more monsters and I need to get a bit better distribution so I can have like about 3 different monsters per floor. I think I am going to need to expand out the roll and that should fix it. the hard part now is balancing the enemies to get a good difficulty curve.
-
Oh also instead of using a d20 for this, you could use 2d10. It would give you a nice bellcurve that would move along the table.
-
So you see:
Slimes on levels 1-4
Demons on levels 1-8
Fire Elementals on levels 3-10
Wolves on 7-10
Level Distribution if you had 7 baddies per level and an even roll distribution (note each 1/7 corresponds to a distribution of 14.3% chance of showing)
1 = 4 slimes and 3 demons
2 = 3 slimes and 4 demons
3 = 2 slimes and 4 demons and 1 Fire Elemental
4 = 1 slimes and 4 demons and 2 Fire Elemental
5 = 4 Demons and 3 Fire Elementals
6 = 3 Demons and 4 Fire Elementals
7 = 2 Demons and 4 Fire Elementals and 1 Wolves
8 = 1 Demons and 4 Fire Elementals and 2 Wolves
9 = 4 Fire Elementals and 3 Wolves
10 = 3 Fire Elementals and 4 Wolves
Note sure that helps, but it's interesting to look at. Seems to be a good distribution.
You could always just fill a table by level, like if you are level 5 then roll on a %die table and fill that way, but this spread looks good.
So if you go through each level, beat the whole game, with 7 baddies per level, on average you would see: 10 slimes, 25 demons, 25 fire elementals and 10 wolves.
-
One thing I have noticed is its hard to have rare monsters. with the RNG its possible to have a level with all the same enemy, and the different enemies is what my game hinges on. I might have to continue playing with this. Or go back to my original idea of the array with enemies in it I am removing after they are picked. :-\
-
Well you could always do a sort of max number of the same enemy in the level.
Like keep track of how many of a type have been spawned on a level, and reroll when you hit a max.
For example:
Roll on table to place a monster.
If Slime is rolled then check.
if Number_of Slimes >= 5 then reroll.
That would assure a variety and also a random number. You could play around with this all day, it need not be super complex.
You could even do a level by level system just as easy.
If Level = 1 then roll and place
2-4 slimes
1-2 demons
If Level = 2 then roll and place
1-3 slimes
2-3 demons
And so on. You can make sure there are neither too many nor too few on each level. You can even make there be a max on each level like this.
If Level = 2 then roll and place
1-3 slimes
5 demons (minus the number of slimes)
Or how about....
If Level = 2 then roll 1d6
1-3: 4 slimes 1 demon
4-5: 2 slimes 2 demons
6: 1 slimes, 1 demon, 1 fire elemental
So 16% of the time you get that rare fire elemental on level 2. I find that 1/6 chance to be a good 'rare' but not 'too rare' % chance. Like when you play a dice game you really really want to roll that 6, it's common enough it might happen but uncommon enough it's awesome when it does and you feel lucky.
For even more rare I find that rolling 12 on a 2D6 just feels freaking epic, but so rare it's sort of a boring roll if you need a 12 because you know you aren't going to get it. Like trying to roll a natural 20, it can happen sometimes but when you know you need to get that 20 it's sort of a deflating roll because it's too rare, you just sort of throw the dice down and shrug. Whereas if you need a 6 on a 6 sided die man you are going to blow on that shit, shake it in your hand, have a lady wish you luck.
The game Yahtzee isn't the most fun, but the fun it has is that the %'s are achievably high, but rare enough to make it awesome when you land something great.
Anyway my magic rare % is 1/6, but that's from boardgames.
-
The thing about dice mechanics is that every time you want to tweak it slightly to improve the balance, it becomes more complicated and difficult to work out the probabilities for, and ypu have to change numbers all over the place that aren't related to the thing you actually want to change.
With a simple weight system you just give each monster its own weight, and the computer adds up to get the right number of sides. That means if you want to change the spawn rate of one monster, you just change that monster's weight and everything else is sorted out automatically. Writing a dice table is basically using this algorithm and then doing all the maths by hand with an artificial constraint on the total weight.
You can make the weights dependent on depth by turning them into a function like 5-depth. That way the monster stops spawning at depth 5.
You can solve the problem of not getting enough rare monsters by subtracting from a monster's weight every time you pick that monster. Just think of it as putting tokens in a bag and picking them out at random. You can either keep the token or put it back in. If you keep picking common monsters, their probability will go down and you'll be more likely to pick a rare monster. You can even skew the system by adding extra tokens for a rare monster, then throwing them away when you finally pick one.
It's a very flexible system, appropriate to the task, and more intuitive than dice imo.
-
If you've spent 25 years making dice/card/boardgames and/or playing table top RPG's the dice are 2nd nature. Of course it does seem silly to apply these limitations to making computer games, but I'm hardly a good programmer.
Can you give an example of how one might do this weights thing, I'd be interested in seeing how it works. I think I know...but...
-
The game Yahtzee isn't the most fun, but the fun it has is that the %'s are achievably high, but rare enough to make it awesome when you land something great.
Yahtzee is, like, the most awesome game invented after Go :-O (with Mikado (http://en.wikipedia.org/wiki/Pick-up_sticks) a close third, of course.)
As always,
Minotauros
-
Here's a simple example without any depth variables:
If System_Number = 21-25
Roll Random (4-8) for the number of ships to place.
Place that number of ships in valid locations, randomly.
Roll (1-100)
-1-10 then the ship is a Missile Cruiser
-11-20 Science Vessel
-21-30 Battleship
-31 - 100 Standard Cruiser
With a weight system this could be something like:
4-8 ships
Missile cruiser: weight 10
Science Vessel: weight 10
Battleship: weight 10
Standard cruiser: weight 70
If you want to tweak the spawn rates for different vessels, you only have to change one number. If you want to limit the number of battleships so that the player doesn't get screwed by bad luck, you can do something like:
4-8 ships
Missile cruiser: weight 2
Science Vessel: weight 2
Battleship: weight 2 minus number of battleships present
Standard cruiser: weight 14
IF you want things to vary with depth, like in
Number_of_Monsters = 4-8 plus Depth/10. That would give you +1 monster every 10 levels.
Now place that many monsters by looping through the Fire Level Table.
Roll 1-20 (plus Depth)
-1-9 = Lil Sparky (by depth 9 there are no more of these)
-10-18 = Lil Flamer (by depth 18 there are no more of these)
-19-27 = Big Sparky (rare on level 1, getting more common as level's progress, then cut out at level 27)
-28-36 = Big Flamer
-37+ = Fire Mini Boss (cannot show up until level 17, 1/20 or 5% chance, by level 36 they are all you get)
You can do something like this (the probabilities are slightly different here, I just chose something with easier arithmetic):
Lil Sparky: Native_Depth = -5
weight = 15 - abs(Native_Depth - Depth)
if weight < 0: weight = 0
if weight > 10: weight = 10
Lil Flamer: Native_Depth = 5
weight = 15 - abs(Native_Depth - Depth)
if weight < 0: weight = 0
if weight > 10: weight = 10
Big Sparky: Native_Depth = 15
weight = 15 - abs(Native_Depth - Depth)
if weight < 0: weight = 0
if weight > 10: weight = 10
Big Flamer: Native_Depth = 20
weight = 15 - abs(Native_Depth - Depth)
if weight < 0: weight = 0
if weight > 10: weight = 10
Fire Mini Boss:
weight = Depth - 20
if weight < 0: weight = 0
I'm using the absolute value function (abs) here; that just gets rid of minus signs, so abs(-5) = 5. You can also use the max and min functions to get rid of those weight<0/weight>10 lines. abs(Native_Depth - Depth) will give you the distance between this depth and the native depth.
Just as a formula in a finished, balanced game, a weights system isn't much easier than the dice system, but if you're tweaking and balancing it's a lot easier to alter one monster's spawn rate without affecting the other monsters, or to add and remove monsters. For instance, if you pointed out that in my version, a Big Sparky can't spawn on level 1, all you do is change its native depth to 14. Everything else gets calculated automatically.
You can also mix in any variables from the game, like details from the spawn tile:
Science Vessel: weight 6 (spawn tile is near a nebula), or 2 (otherwise)
Troll: weight 13 (spawn tile is a bridge), or 3 (otherwise)
If you want a certain monster type to be common in a small range of levels, you can do that easily:
Fire Cultist: Native_Depth = 12
weight = 3 * (5 - abs(Native_Depth - Depth))
if weight < 0: weight = 0
if weight > 10: weight = 10
This monster spawns in a 10-level range, rather than a 30-level range like the rest of them, but the weight still goes up to 10 on levels 11, 12, and 13.
-
so Quendus. You still end up with an array to select from, but where I was setting the array for each level, you are using a quick formula to decide?
-
Yes. Every level you would call this weight() function for all the monster types, and the results would fill up the array. If you don't put tokens back into the bag, then you alter the array after making each choice.
-
that sounds good, but i wonder if perhaps my original way of just building an array for each level might have been the easiest way to go. It would take up some room, but as opposed to doing the equation to figure out how to have 3 slimes on levels 1-3 then 2 for 4-5 then 1 for the rest of the levels, it would be a bit faster.
-
If you want a specific number of each monster type on each floor, I'm not sure there's much point doing any random generation.
Anyway, a function doesn't have to be a mathematical expression. There's nothing stopping you from writing functions like that in whatever language you use:
Slime:
If depth <= 3: weight = 3
Else if depth <= 5: weight = 2
Else: weight = 1.
-
that sounds good, but i wonder if perhaps my original way of just building an array for each level might have been the easiest way to go. It would take up some room, but as opposed to doing the equation to figure out how to have 3 slimes on levels 1-3 then 2 for 4-5 then 1 for the rest of the levels, it would be a bit faster.
It sounds like you're trying to do something out of the ordinary. Could you specify the exact behaviour you'd like to see?
-
Meh...
If level = 1
Roll on table 3 times
1 Slime
2 Slime
3 Slime
4 Slime
5 Demon
6 Demon
Brute force. Simple to change. Easy to see what's happening. You might get all demons or all slimes, no big whoop?
You can have a table for each level or you can add a modifier and make the list longer.
Roll on table 5 times, add depth
1. nothing
2. nothing
3. slime
4. slime
5. slime
6. demon
7. demon
...
15. wolf
Clarity is good. But if complex is your thing you can invent all kinds of subtle ways to do this, as Quendus suggested. Ways that are portable and engine like.
-
Meh...
If level = 1
Roll on table 3 times
1 Slime
2 Slime
3 Slime
4 Slime
5 Demon
6 Demon
Brute force. Simple to change. Easy to see what's happening. You might get all demons or all slimes, no big whoop?
Simple to write. Not so simple to change. If you want to add another slime you've got to either put it at the end (and make the table hard to read) or put it before the demons, which means changing th numbers on the demons. Either way you've got to change the number of sides on the dice. I prefer just saying:
Slime: put 4 copies in the table
Demon: put 2 copies in the table
Then the computer adds 2 to 4 and gets a 6 sided dice. If I want to change it I just change 4 to 5 and the computer does the rest for me.
You can have a table for each level or you can add a modifier and make the list longer.
Roll on table 5 times, add depth
1. nothing
2. nothing
3. slime
4. slime
5. slime
6. demon
7. demon
...
15. wolf
Dice+depth is ok for quickly making a system that places different enemies at different depths, but it's even harder to modify than the one above because adding or removing a monster changes the length of the dungeon, unless you compensate by removing or adding something else. All of the variables are bound together so that you can't change one thing without making big changes to things you didn't want to change. It's also difficult to get key depths out of a table like that, because you can't read the first and last depth a monster appears straight off the table - you have to add and subtract the number of sides on the dice. Same goes for the levels where it's most common.
Overall, it's good if you want to quickly make a monster generation system for a 7DRL, but for anything where you might want to later change the monster list or rebalance because a difficult monster is too common, it's better to choose a more flexible system - a "read-write" one instead of a "read-only" one.
Clarity is good. But if complex is your thing you can invent all kinds of subtle ways to do this, as Quendus suggested. Ways that are portable and engine like.
Clarity is good. I'm not advocating for complexity here, I just think that the relationship between what a developer wants from monster generation and the numbers a developer has to type out to get that system should be as simple as possible. I think that a developer most likely thinks of a range of depths and a relative frequency for each monster, and so the computer should work from information of that kind. That way the developer doesn't have to manually translate to some other kind of information that doesn't directly relate to what they want. I prefer to write code once that takes information in my terms and makes a dice table from it than to translate from my terms to a dice table and back over and over and over again.
tl;dr I don't write games in machine code and I don't write dice tables either.
-
So I had some special requirements for this, because it was being used for my game MicRogue. I wanted it so Enemies were never removed from the spawn list, so there was always a chance to spawn, but also that there needed to be a good variety of enemies in a level, because the game is very boring when all the enemies are the same. I ended up just going with a simple switch and an enemy array. Its probably not the most elegant way, but its easy to change and add monsters and it was quick to code.
// Create the Monster Array
var Monster_Array:Array = []
// 1 = Slime
// 2 = Large Fire
// 3 = Skeleton Warrior
// 4 = Demon
// 5 = Eye
// 6 = Ninja
// 7 = Cockatrice
switch (Floor)
{
case 1: { Monster_Array = [1, 1, 1, 2, 5] }; break;
case 2: { Monster_Array = [1, 1, 1, 2, 5] }; break;
case 3: { Monster_Array = [1, 1, 1, 2, 2, 3, 4, 5] }; break;
case 4: { Monster_Array = [1, 1, 2, 2, 3, 4, 5, 5, 6] }; break;
case 5: { Monster_Array = [1, 1, 2, 2, 3, 4, 5, 5, 6, 7] }; break;
case 6: { Monster_Array = [1, 1, 2, 2, 2, 3, 4, 5, 5, 6, 7] }; break;
case 7: { Monster_Array = [1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7] }; break;
case 8: { Monster_Array = [1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7] }; break;
case 9: { Monster_Array = [1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7] }; break;
case 10: { Monster_Array = [1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7] }; break;
}
-
Looks pretty clear. Does it work in practice? I think that's the real test.
@Quendus - You have to remember that you are an above average programmer, maybe even a great one I dunno, but some people just want to write games. I understand the weights thing, but it's just not as easy for me. So I'd never use it. I'd come back to it and simply forget how it worked or have to parse through it again to fix something. Tables might be harder to change but still trivial in a 10 level game. It's not bad coding and it's not lazy if games are getting made.
-
works pretty well at giving me a good range of guys. The more monsters in the larger arrays give a decent variety. I also added a special enemy system. Each spawned enemy has a chance of being a special type of that enemy.
-
Jason: Looks like a reasonably flexible system. Might not look elegant, but it's got the essentials - you can see at a glance which levels each type of monster is on, and what's on each level, you can change what's on one level without affecting others, you can change one monster's rarity without affecting other stuff, you don't have to do any arithmetic to change stuff (assuming arrays in your language know their own length), you can easily add and remove monsters and levels, and everything's in one place. Pretty sweet. The only disadvantage is the typing time, but copy/paste can partially mitigate that (presumably you did).
@Quendus - You have to remember that you are an above average programmer, maybe even a great one I dunno, but some people just want to write games. I understand the weights thing, but it's just not as easy for me. So I'd never use it. I'd come back to it and simply forget how it worked or have to parse through it again to fix something. Tables might be harder to change but still trivial in a 10 level game. It's not bad coding and it's not lazy if games are getting made.
Jo: I never said anything was bad or lazy coding, I believe in using the right tool for the job. That depends on the job (not using heavy tools on a light problem), future prospects for the job (it may or may not be important that you and others can understand or alter it in future), and the person using them (different tools may be easier for different people to use). When I read the OP, based on Jason's history I imagined a mid-weight problem (multiple monsters, multiple levels), probable future development (because I remembered seeing a lot of iterative development in the Microgue thread here), and a programmer with a reasonable level of experience (I don't know their background, but all Jason's games that I remembered were in Flash, and all the ways I've heard of to make Flash games involved typing code, maybe even object-oriented programming).
Based on those guesses I posted a simple algorithm (it really is simple, all it takes is a few arrays and a couple loops) that's flexible enough to let the designer do just about anything without having to type it all out by hand. I posted the slightly more complicated version (using [3,2,3,0,1] instead of [1,1,1,2,2,3,3,3,5]) just to cover my ass in case someone decided to berate me later about it being inefficient to store so many copies of the same number... instead I get berated because it's too complicated. Either way, at least one of the versions is within the OP's grasp and that's what matters. If the OP ever changes any of the stuff mentioned above, I'll have saved them some time and frustration.
You weren't asking for advice so I wouldn't presume to tell you what methods to use. Especially since using loops and arrays might be really awkward in Game Maker (I don't know how GM works, but I used the Games Factory when I was a kid and I wouldn't have had a clue how to do this kind of stuff). For simple tasks like monster generation, if you have a working solution and the only available improvements come with hefty pricetags like learning arrays and loops, then you're totally right to stick with what you've got. You might write a game sometime where you really do need to learn how this stuff works, in which case the cost attached to this option would go down a long way.
We're alike - I don't make 3D games because the time cost of learning OpenGL properly is big and the monetary cost of getting Unity without plastering "TRIAL VERSION" over the user's screen isn't zero. I still haven't found a game idea that motivates me enough to do either.
-
typing it all in wasn't bad. I planned out all the level stuff in a spreadsheet and then typed it out. Originally I actually used Strings instead of numbers. That was a mistake. My first floor was:
case 1: { Monster_Array = ["Slime", "Slime", "Slime", "Large Fire", "Eye"] }; break;
It ended up being way to much work, so I dropped it to a simple integer. Way easier to work with. I also started with less enemies and added them slowly so it took a bit to type in. To add a monster I just select one randomly from the array and remove it, and The monster count is always less then the total array length. so My first level has 4 guys, it will never have more then 4 slimes.
-
Arrays and other complex data structures are easy in GM, but 2D arrays are about the most complex structure I use, with lots of loops and such. Only code nazis would berate you from doing something less efficient but easier.
As long as a game comes out of your code that's what matters.
I am going to have to up my game considerably if I plan to make any headway on my SuperHero project, but my next 7DRL is going to feature exactly the monster tables we have discussed here.
BTW my last 7DRL had over 3000 lines of code, making anything in Gamemaker requires using the scripting language for the effects of actions. Game Maker Language (GML) is a fully functional high level language.
-
Jason seems to have found a system that works for him, but for the benefit of anybody else considering the same problem, I thought I'd just point out that depending on what language you're using, there are some standard data structures out there that can make the kind of weighted list that Quendus is suggesting both extremely easy to implement and also very fast.
For example, with Java's TreeMap (http://docs.oracle.com/javase/7/docs/api/java/util/TreeMap.html) you can add each enemy type keyed with a cumulative weight score, then generate a random number in the appropriate range and use ceilingEntry to find the next cumulative weight above that:
TreeMap<Double,String> enemies = new TreeMap<Double,String>();
//Add enemies:
enemies.put(1.0, "Monkey");
enemies.put(enemies.lastKey() + 3.0, "Giant Rat");
enemies.put(enemies.lastKey() + 1.0, "Orc");
enemies.put(enemies.lastKey() + 1.5, "Jehovah's Witness");
enemies.put(enemies.lastKey() + 0.1, "Dragon");
//Pick random enemy:
Random rand = new Random();
double randomNumber = rand.nextDouble()*enemies.lastKey();
String randomEnemy = enemies.ceilingEntry(randomNumber).getValue();
You can do exactly the same thing with any collection ordered by a key, such as C#/VB.NET's SortedList (http://msdn.microsoft.com/en-us/library/ms132319%28v=vs.110%29.aspx) or C++'s std::map (http://www.cplusplus.com/reference/map/map/), although you may have to loop through to find the next highest value yourself.
As a barely-relevant aside, on the off chance that anybody is interested in seeing a real-world application of this: I recently used the same basic algorithm to generate a gradient seating pattern for a sports stadium (http://www.arupassociates.com/en/projects/king-abdullah-sports-city/) by randomly mixing three different seat colours, with the weighting of each colour changing as you go up the bowl to produce the gradient: https://pbs.twimg.com/media/Bc0Xrx_IQAEyEkT.jpg:large (https://pbs.twimg.com/media/Bc0Xrx_IQAEyEkT.jpg:large)
-
Jason seems to have found a system that works for him, but for the benefit of anybody else considering the same problem, I thought I'd just point out that depending on what language you're using, there are some standard data structures out there that can make the kind of weighted list that Quendus is suggesting both extremely easy to implement and also very fast.
For example, with Java's TreeMap (http://docs.oracle.com/javase/7/docs/api/java/util/TreeMap.html) you can add each enemy type keyed with a cumulative weight score, then generate a random number in the appropriate range and use ceilingEntry to find the next cumulative weight above that:
TreeMap<Double,String> enemies = new TreeMap<Double,String>();
//Add enemies:
enemies.put(1.0, "Monkey");
enemies.put(enemies.lastKey() + 3.0, "Giant Rat");
enemies.put(enemies.lastKey() + 1.0, "Orc");
enemies.put(enemies.lastKey() + 1.5, "Jehovah's Witness");
enemies.put(enemies.lastKey() + 0.1, "Dragon");
//Pick random enemy:
Random rand = new Random();
double randomNumber = rand.nextDouble()*enemies.lastKey();
String randomEnemy = enemies.ceilingEntry(randomNumber).getValue();
You can do exactly the same thing with any collection ordered by a key, such as C#/VB.NET's SortedList (http://msdn.microsoft.com/en-us/library/ms132319%28v=vs.110%29.aspx) or C++'s std::map (http://www.cplusplus.com/reference/map/map/), although you may have to loop through to find the next highest value yourself.
As a barely-relevant aside, on the off chance that anybody is interested in seeing a real-world application of this: I recently used the same basic algorithm to generate a gradient seating pattern for a sports stadium (http://www.arupassociates.com/en/projects/king-abdullah-sports-city/) by randomly mixing three different seat colours, with the weighting of each colour changing as you go up the bowl to produce the gradient: https://pbs.twimg.com/media/Bc0Xrx_IQAEyEkT.jpg:large (https://pbs.twimg.com/media/Bc0Xrx_IQAEyEkT.jpg:large)
Nice effect! The photo's not great but you can still see the effect clearly. I'm using a similar routine in my own work - I have a histogram generated from a sample of angles, and I use this to produce random angles with the same distribution as the histogram.
I don't know about C#, but in C++ the code wouldn't be much different - you'd more or less replace lastKey() with crbegin().first and ceilingEntry with lower_bound. In C++11 you can just let the standard library handle the whole thing:
// Hoops to jump through to use shiny new random functions
static std::random_device rd;
static std::mt19937 rng(rd());
// Enemy names
std::vector<std::string> enemies = {"Slime", "Demon", "Tomato", "Gorgonzola"}
// Probability distribution
std::discrete_distribution<float> enemy_distribution({2.f, 1.f, 0.2f, 0.0001f});
// Choose an enemy from the distribution
std::string monster_of_the_week = enemies[enemy_distribution(rng)];
[\code]
-
In my old game Squirm, I assigned a weight/frequency to monsters and other random content (items, room themes, etc). Whenever the game needed to pick some content, it would assign temporary weights to each possible choice – based on danger level, frequency and "tags" (enabling specialized selections, like: pick between all items except weapons, and double the probability of items tagged "magic").
This was easy to implement, and worked well enough, but I did run into a problem of "weight inflation": As the number of items to pick became greater, each item became slightly less frequent. For instance, I might add a new magic thimble (Squirm's equivalent to rings) and assign a danger level and frequency that made it show up at a reasonable rate. After a few months of adding more thimbles and stuff, that particular item might practically stop to appear. This was especially a problem for "necessary" items, such as healing implements, as well as the "cool and powerful" stuff, which should be rare enough to feel special, but not so rare that only the developer knows they exist.
I may have missed some nifty tricks to avoid this problem, and I'm pondering how I should do it for my current thing. Probably, I'll want to try out a "draw bag" method to cap how many times an identical choice can be made.
As always,
Minotauros
-
For some things it doesn't matter if you don't see one for the whole game. In that case there's nothing to worry about. For things that are important you can attach a native depth that makes them common and almost guaranteed to appear in an area the player is guaranteed to pass through. If they need ro be common throughout then you can just bump up their overall weight or the spawn rate for everything, or add a shop that can sell things that might not show up in the dungeon. You can also increase the number of levels. If you've got all this content that you think improves the game, and you think it's too rare, but you don't want to upset the balance by making levels denser, then that's a reasonable solution.
That said, if the spawn rate for items gets too high you might start finding there's too much junk on the floor, and that can really be a hassle for players, especially if you have an ID syatem.
-
So I had some special requirements for this, because it was being used for my game MicRogue....
@jasonpickering: So your monsters array gives you relative probabilities and you have the chance to upgrade one monster per-level to a special monster. I'd guess you have a few other constraints you want to apply:
- The total number of monsters per-level. Could be a specific value or a min and max range.
- You want to enforce some kind of variety. For example by saying "I want at least three types of monster on this level".
If you were to put it in Excel I'd guess it might look like this (I made up the numbers for Total and Min. Species):
Level | | Slime | | Large Fire | | Skeleton Warrior | | Demon | | Eye | | Ninja | | Cockatrice | | Total | | Num. Special | | Min. Species |
1 | 3 | 1 | 0 | 0 | 1 | 0 | 0 | 4 | 1 | 2 |
2 | 3 | 1 | 0 | 0 | 1 | 0 | 0 | 4 | 1 | 2 |
3 | 3 | 2 | 1 | 1 | 1 | 0 | 0 | 4 | 1 | 2 |
4 | 2 | 2 | 1 | 1 | 2 | 1 | 0 | 5 | 1 | 3 |
5 | 2 | 2 | 1 | 1 | 2 | 1 | 1 | 5 | 1 | 3 |
6 | 1 | 3 | 2 | 1 | 2 | 1 | 1 | 5 | 1 | 3 |
7 | 1 | 3 | 2 | 2 | 2 | 1 | 1 | 6 | 1 | 3 |
8 | 1 | 3 | 2 | 2 | 2 | 2 | 2 | 6 | 1 | 4 |
9 | 1 | 3 | 2 | 2 | 2 | 2 | 2 | 6 | 1 | 4 |
10 | 1 | 3 | 3 | 2 | 2 | 2 | 2 | 7 | 1 | 4 |
-
that's the gist of it. Although each monster has a 10% chance of being a special monster (which is just an upgraded version of the regular monster. I might increase that chance with the depth. (10 + Depth * 2) so harder monsters show up more. As for the total number of monsters is just 4 + depth/4. so every 4 floors more monsters are spawned. That table is pretty accurate though.
-
Have you thought of having a cost for each enemy/weapon/item and matching it against a current potential value of the player?
Then you could choose random monsters and loot while reducing a pool of points for that location.
So for example room 23 has just been opened by the player, at the moment the player has skills and equipment worth 567 points.
We randomly select some monsters getting the following:
A green slime (120 points)
A skeleton (100 points)
A skeleton (100 points)
A skeleton archer (150 points)
and a giant rat (50 points)
At this point only 47 points remain, and as a giant rat is the cheapest enemy available to this level we stop adding more monsters.
The 47 points are deducted from any loot the player picks up from the encounter to keep things balanced.
You could also include modifiers, like if the player is a cleric, skeletons points value is halved, or if he doesn't have a ranged weapon green slime points value is increased by 15%
This gives a very balanced and fair game.
-
I like the pool of points idea Pickledtezcat. Do you have a game or prototype that uses this idea?
-
My method for 1Quest (to be announced soon ;) ) I have something similar as the pool, but instead of points, I used experience values for monsters :
- First I have a list of available monsters for the environment
- Then depending on the depth, i have a total XP value, a min and a max XP value per monster
For example, if you are in the swamp, the monster list is Gnoll (10 xp), Gnoll Shaman (20), Rat (5), Snake (20), Dryad(50), Troll (100), Fairy (50) etc ...
Then for a depth 3 swamp, I will take monster between 20 and 50 xp value, for a total of 500 xp
This way I have thematic monsters, adapted to the difficulty, with a fixed total XP so I can control the hero evolution travelling through a depth 3 place
-
Have you thought of having a cost for each enemy/weapon/item and matching it against a current potential value of the player?
Then you could choose random monsters and loot while reducing a pool of points for that location.
So for example room 23 has just been opened by the player, at the moment the player has skills and equipment worth 567 points.
We randomly select some monsters getting the following:
A green slime (120 points)
A skeleton (100 points)
A skeleton (100 points)
A skeleton archer (150 points)
and a giant rat (50 points)
At this point only 47 points remain, and as a giant rat is the cheapest enemy available to this level we stop adding more monsters.
The 47 points are deducted from any loot the player picks up from the encounter to keep things balanced.
You could also include modifiers, like if the player is a cleric, skeletons points value is halved, or if he doesn't have a ranged weapon green slime points value is increased by 15%
This gives a very balanced and fair game.
I like the pool of points idea Pickledtezcat. Do you have a game or prototype that uses this idea?
I used something a little like this for my 7DRL last year (http://www.vitruality.com/games/rogues-eye/), although I worked it slightly differently in that I had a pool of points for each level and kept picking monsters off the list and subtracting their points cost from the pool until that number went <=0. For selecting monster 'reinforcements' I gradually added to that pool each turn and when it reached a certain value randomly added a monster and took its points cost from the pool. The more expensive the monster, the longer the cooldown period until the next monster turned up. This worked quite well and was easy to tune in a hurry, which is what you want for a 7DRL!
-
My method for 1Quest (to be announced soon ;) ) I have something similar as the pool, but instead of points, I used experience values for monsters :
- First I have a list of available monsters for the environment
- Then depending on the depth, i have a total XP value, a min and a max XP value per monster
For example, if you are in the swamp, the monster list is Gnoll (10 xp), Gnoll Shaman (20), Rat (5), Snake (20), Dryad(50), Troll (100), Fairy (50) etc ...
Then for a depth 3 swamp, I will take monster between 20 and 50 xp value, for a total of 500 xp
This way I have thematic monsters, adapted to the difficulty, with a fixed total XP so I can control the hero evolution travelling through a depth 3 place
I wouldn't call it controlling player progression unless clearing the whole level is compulsory...
-
At least, i know how much xp he can have. An yes, it will be more difficult in the later levels if the hero skip too much monsters. But each class should have only a couple of really difficults monsters (well ... game isn't finished yet ^^)
-
the whole XP thing sounds very similar to the CR value system I was using before based off of Pathfinder games (Might be in original D&D).
so here is a question the tangentially relates. How do you guys usually go about finding what squares to place your monsters on. Right now I just fill them up randomly, but does anyone use monster placement rules?
-
the whole XP thing sounds very similar to the CR value system I was using before based off of Pathfinder games (Might be in original D&D).
so here is a question the tangentially relates. How do you guys usually go about finding what squares to place your monsters on. Right now I just fill them up randomly, but does anyone use monster placement rules?
Well, if you use room_ids in your level designer (give each tile that is part of a room a unique numbered tag so you can see which parts of a map are in a particular room) you can have the monsters placed in themed groups in a room. You could use arrays to store potential formations (everyone standing around a table looking at a map, people sleeping on mats etc..) as well as props. The arrays can be mirrored or rotated (in python I use reverse or zip functions) to provide variation and you can select randomly from a dictionary of arrays. You can easily check the room is big enough to hold the formation and if not (perhaps a pillar is already placed where the table is supposed to go) you can select another formation. You could also include an extra index in the dictionary to indicate size and then only select those that can fit before randomly choosing an entry.
(https://www.mediafire.com/convkey/e1e5/na2jmbgi6n4pwyj6g.jpg)
Please keep in mind the above is just a sketch, I use a spreadsheet editor for mocking up dictionaries or ideas, keeping notes about possible function calls or stats. I haven't run it to check the result so you'd have to do your own testing.
With this kind of method you could have spiders (a monster) and cocooned prey (a prop), or a book case and scattered books (all props) or a barracks with beds, chests and sleeping guards and lookouts. Further refining the random monster function, you could use a wild card such as False or None instead of xp amount to use XP from the spawn pool I mentioned above. Or you could add an extra argument to the function such as monster type, which only adds monsters with that type to the random generation pool. A typical type would be "furry" which would generate giant rats, wererats, werewolves, cave bears and such, or "insect" which could generate all kinds of bugs, or "human" or whatever. The more tags you have, the more you can control the spawns and create more narrative for your game, but the more chance of getting an empty list.
For instance you spawn a monster formation on level one which includes a random monster call for a "demon". The xp pool is just 100 xp, but the lowest level "demon" monster is 5000xp. That's going to result in no monsters being spawned unless you have a fall back, like:
else:
monster("giant_rat", "crazed")
In which case the player is going to be so busy dealing with a berserk Giant rat they may not notice the "pentagram" and "sacrificial_altar" props in the room.
-
well I really only have the single room. so its all about the layout in that room. Although I do load the level from a XML file. I guess I could place Several monster spawn areas around the room, and then the game can just pick which ones to spawn the monsters at. that might be an easier way.
I am also looking back into the equation idea after looking at my monster amounts. I have nice bell curves in the monster amounts reaching peak at certain points then gradual taper off, so I think I can derive an equation from that, but just make sure that the equations is always starting at 0, but after peak never getting below 1.
-
Have you thought of having a cost for each enemy/weapon/item and matching it against a current potential value of the player?
IVAN has a system where monsters which are allowed to appear depend on the player's HP and equipment. This has counter-intuitive effects. Like, items which increase HP are considered worthless, because they cause stronger monsters to appear. Or, when going to a new level, it might be better to stash the best equipment and have temporary buffs off. This way, strong monsters won't be attracted. Overall, it is not clear whether you should make your character stronger, or not. I recommend against using such kind of systems.
You could also include modifiers, like if the player is a cleric, skeletons points value is halved, or if he doesn't have a ranged weapon green slime points value is increased by 15%
This gives a very balanced and fair game.
Again, in such a design it is not clear what is good for you and what is bad. Clerics have a some special power which helps against skeletons, but on the other hand, they face more monsters. You might consciously decide that you do not want to bring a ranged weapon, because you want less green slimes, or less monsters in the rooms containing the green slimes. And as the total number of points depends on your character, you learn to develop the character in a way which makes them strong, but the game does not see it.
IMO such a design is cheating, and the player will cheat you too.
-
Have you thought of having a cost for each enemy/weapon/item and matching it against a current potential value of the player?
IVAN has a system where monsters which are allowed to appear depend on the player's HP and equipment. This has counter-intuitive effects. Like, items which increase HP are considered worthless, because they cause stronger monsters to appear. Or, when going to a new level, it might be better to stash the best equipment and have temporary buffs off. This way, strong monsters won't be attracted. Overall, it is not clear whether you should make your character stronger, or not. I recommend against using such kind of systems.
You could also include modifiers, like if the player is a cleric, skeletons points value is halved, or if he doesn't have a ranged weapon green slime points value is increased by 15%
This gives a very balanced and fair game.
Again, in such a design it is not clear what is good for you and what is bad. Clerics have a some special power which helps against skeletons, but on the other hand, they face more monsters. You might consciously decide that you do not want to bring a ranged weapon, because you want less green slimes, or less monsters in the rooms containing the green slimes. And as the total number of points depends on your character, you learn to develop the character in a way which makes them strong, but the game does not see it.
IMO such a design is cheating, and the player will cheat you too.
I understand your reservations about such a system, and you have made me think more carefully about using it, but I think The system is sound as I described it.
When I used to DM paper and pencil RPGs in highschool I used to balance the encounters against my players, if they had a weak combination of party members or a couple of poor character builds I'd substitute some of the monsters in the module. I didn't want them to get killed too easily, but I would make it challenging and sometimes they did all end up getting slaughtered even with the substitutions because of making poor tactical choices. I'd do other things to make the game more survivable too, such as including more healing potions if the party (which would some times change from week to week as the club membership shifted) didn't have a healer, or less traps if they didn't have a thief.
When I've played RPG games on computer I've found myself almost always playing a cleric just because, they have a fairly good set of combat abilities, they can heal themselves and allies, they can summon creatures and they can buff themselves. Most games become a walkover in such a case. If I chose to play as a bard or a gnome illusionist it's going to be much much harder, probably impossible unless I play on an easier difficulty setting. This is a problem because I actually don't really like playing a cleric, I actually want to play a thief and sneak my way through the game, but it's rare to find a game that will adapt to my playing style.
I think an auto-tuning game system gives you more freedom to play the game anyway you want to instead of being forced to play only the "right" way.
Such a system could leave itself open to being "gamed" but as I showed above (but didn't thoroughly explain), it's important to have a mixed system. Although most encounters will be balanced to the player's abilities (generating monsters from a pool of XP), there will be some which have a set value. You could strip off all your armor and weapons and descend in to the dungeon in just your loin cloth expecting to fight only pixies and gnomes, but that rare encounter where you face a set of monsters pitched at what "should be" your value at that part of the game if you are playing as expected will soon wish you didn't leave your armor behind.
Besides, an item that gives more HP is always useful right? No matter what enemies you're facing, it's better to have 10 HP than 2HPs. Being stronger is always going to be better than being weaker, as usually some enemies will have special abilities which let them hit above their points value in certain situations. For example spiders have poison, giant rats have disease. You may be facing less of them because you are not a cleric, but still once bite can be enough to really ruin your day. If you haven't got a ranged weapon then that one rust monster (instead of two because of the XP modifier) is still going to be chomping on your fine plate mail if he scores a hit. Problems could arise if you set the XP modifiers too high, effectively pricing certain monsters out of the bell curve, but that's part of the design process and something that needs to be gotten right for the game to be any fun.
Every game becomes an arms race between the player who is trying to beat the DM's tactics and the DM, who is trying to beat the player's tactics. As a game designer you have to think ahead to find ways to confound the player without just throwing random stuff at them.
As a games designer I could cheat and use the system to single out a player's weaknesses so that I could kill them off easily, but then who's going to want to play my game then? It's important to get the balance just right so the game is challenging to every player but not impossible.
-
I think that the situation is completely different with pen&paper RPGs. The main aim is to tell a story, but this is also a game -- which means that good decisions should be rewarded, and bad decisions should be punished. You know what you would do in the given situation, and you can compare the players' action to this. If your players do something bad or good, you should punish or reward it, in some meaningful way. If one of your players does something surprisingly stupid, they should face the consequences, and if they do something surprisingly ingenious, you should reward them. If they have some special powers, allow them to use these powers if they make sense. For example, if a player decides to learn the Climbing Trees skill before going to the quest, you should decide whether it makes sense from the story's point of view -- you should not put trees in the dungeon only because some player wanted this skill, but if the quest was in a forest, then make this useful.
Regarding HP: it is better to fight rats with 100 HP, than to fight dragons with 110 HP. As far as I know, that's mostly what happened in IVAN. Normally your strength and endurance would grow at some pace, say, at level N you would have endurance N and strength N. Monsters were based on your endurance, so their power would be also N, and match your strength. With a +E endurance-boosting item, your endurance would be N+E and thus monsters would be N+E too. They would be good match for you if you progressed normally, that is, you had endurance N+E and strength N+E. But you have only strength N. And thus, you die, only because you thought that the +E item would help you.
I don't believe in the mixed systems described. Bad players will be annoyed by boss battles being too tough, and good players will be annoyed by boss battles being too easy.
Regarding "the game challenging to every player": I do not agree that in an auto-tuning system "the game is challenging to every player". It is rather "the game tries to feel challenging to every player". I could play such a game without thinking, and still win, because the game would tune, and I would not feel challenged then. Also, I could not brag that "I have won XXX", because everyone can win XXX. I think it is better to have difficulty levels,and allow the players to switch them at any point of the game. This also solves the problem, because everyone will pick the difficulty level that feels good for them. If you can win as a Bard only on the Easy level, you are still better than those who cannot win as a Bard at all.
What I did in Hydra Slayer and HyperRogue is, I have played the game myself, and based the difficulty on that. So you will win the game if you play at least as good as me, and lose if you play worse. And do this for all reasonable playstyles (in Hydra Slayer, the game is very different depending on which race was chosen, so play all of them; it was not possible to on test all the combinations of artifacts you are likely to find, but I do not think that matters a lot). In your example, the developer would have to play as the Illusionist and Thief too to balance them, not only the Cleric.
The problem is when the player wants to try some playstyle that was not intended by the developer. For example, they want to win as a wizard without using spells. If the player wants to do this for a challenge, then let them have it, don't make it any easier to them by "auto-tuning", otherwise it defeats the purpose, IMO.
If the "wizard without spells" just wants to have fun, no challenge, then it is great for them if the game is able to tune its difficulty for this. But then, why not remove the difficulty altogether? I like to play campaigns in Battle for Wesnoth for the story, but I play them on the easiest difficulty level, because it seems to me that harder difficulty levels just force the player to save-scum more often (and thus "feel" more challenging), instead of giving real challenge. (Of course it is possible that I am just a bad player and experts do win these campaigns at hard levels without save scumming.)
-
Well, we'll just have to agree to disagree on that one. :)
EDIT:
I wrote a lot in support of the system I'll be using, but it doesn't really matter. If the OP wants to use it they will, and if it's not your cup of tea I doubt anything I can say is going to bring you over to the dark side. ;D
-
Gotta agree with Z on this -- it's generally the player's role to match one's level to the dungeons encountered, not the other way around. It seems unavoidable that one would either run into perverse side effects of attempting to gauge the player's "potential" if the game is even moderately complex.
-
I'll pile in on the side of Z as well. Auto-adjusting difficulty is the sort of thing that sounds great on paper, but is next to impossible to successfully implement for the simple reason that the computer has no way of distinguishing between a novice playing to the best of their ability and an expert who is coasting (while you, as a human DM, probably could). If an expert can get the same results by coasting as they can by playing well then they have no incentive to play well and by extension no incentive to become an expert (or possibly even play the game) in the first place.
-
Well how would we gauge a player's ability as a DM? By spiking difficulty at certain times and seeing their response to that. setting difficulty would simply be a matter of returning an xp pool that is bigger or smaller than the players. (0.7, 0.9, 1.1, 1.3 etc). If the player deals with the difficulty spike with out any problem you can set this as the new base level. If on the other hand they nearly die you can ramp back down to the earlier level. If they do actually die you could save info about that event in a config file for next time they play. The game would keep on spiking the player trying to equal but not exceed their ability threshold.
Games, like stories should have a narrative, development, progression. There needs to be time for you to develop your character. Just because the game is procedurally generated shouldn't mean that it is totally random. I'd like to challenge the player and yes they should die if they don't play well. But they shouldn't die even though they played well (or not that often).
Rogue was a very difficult game because all games were like that then. Go and play even a Disney game from the 1980s. They were hard as hell. Certain people, like me, were prepared to play them even so. Why? Because I was a kid, because I had lots of free time and not much to do but fight with my brother. Because getting to the end of Black belt would be the ultimate in bragging rights. But these days things are different. Who's got time to play a game with only 10 minutes of actual game play (http://www.youtube.com/watch?v=zXlNS3biABY), but which takes months of hours a day practice to beat?
Sure, a difficult game will force you to improve so you can beat it. You can't just coast through. But there has to be a better way than save die reload or die restart infinite loop. At the end of the day we want people to play our games (I assume) and there aren't many people with the patience to put up with that kind of thing any more.
EDIT: Just been reading on the news about flappy bird (http://news.cnet.com/8301-17938_105-57618745-1/you-cant-actually-sell-your-flappy-bird-phone-on-ebay/). It seems that people do want to play that kind of game after all. However, I'm not really thinking of making games for the mobile market. That whole demographic is something I can't identify with anymore.
-
If they do actually die you could save info about that event in a config file for next time they play. The game would keep on spiking the player trying to equal but not exceed their ability threshold.
That sounds like just about the worst thing you could ever possibly do. I'll come back to why in a moment.
Games, like stories should have a narrative, development, progression.
Well, that depends on what you mean by 'games'. Games can be like stories, but games are not stories. Let's delve into that a little bit because it might make it a bit clearer why we're so sceptical about this.
Imagine a line. At one end we'll put 'gamey games' - games which are totally abstract and are basically rigid rulesets within which the player(s) have to optimise their actions as best they can. Things like Go, Tetris, Football etc. The appeal of these comes from their challenge and a sense of personal progression as you gradually increase your skill and knowledge.
At the other end, we'll put 'narrative experiences' - games which some people might not even call 'games' but which are still very much 'play'. These have no fixed rules - everything that happens is controlled either by a god-like author or by the players themselves. The purest form of this would be something like a playground game of 'let's pretend' or just simple bog-standard fantasising. The appeal here comes from a number of things, but mainly wanting to see how the narrative develops and a bit of wish-fulfillment.
In between these two extremes you have most actual games. Chess is more towards the gamey end, but with a slight aspect of fictional flavour in that the pieces represent kings, knights etc. Art games like Dear Esther go more towards the other extreme. D&D would be more towards the fantasy end, but can shift along the spectrum depending on how you decide to play it - you can choose to follow the rules exactly or to bend them to suit the narrative you're trying to create.
Roguelikes traditionally fall very much on the gamey-game end - they have clear and rigid rules with strong penalties for failing to observe them. Their stories are usually minimal or non-existant and more often than not their fictional universe is cribbed directly from some other work. (To complicate things; they do have their own kind of narrative experience, but it is one that arises directly from the rules of the game and not one imposed by an author or the player - it's a subtle but important difference).
It sounds like what you want to create is a game that is far more towards the fantasy/role-play/make-up-your-own-adventure/anything-goes end of the spectrum. You want the player to be able to play the game the way they want to and for the rules of the game not to get in the way of that. I don't, for the record, think there is anything wrong with that.
But what's important to understand is that the reason there is a spectrum at all is that there is a tension between these two poles. They are two very different masters and serving them both at the same time is difficult - design measures that you take to enhance one can often (not always, but often) harm the other. Consequently, trying to take a set of design characteristics engineered towards one end (such as the basic roguelike formula) and trying to drag it down wholesale towards the other is probably not the best way to approach the problem - the end result is likely to come out a little bit confused about what it's trying to be.
Which brings me back to why
If they do actually die you could save info about that event in a config file for next time they play. The game would keep on spiking the player trying to equal but not exceed their ability threshold.
is a bad idea. Well, there are several reasons; people don't tend to enjoy sudden difficulty spikes, for one. But putting that aside, the chief reason is that it undermines the whole purpose behind death (and more specifically, permadeath). In gamey-games, death is a lesson. It's the game saying 'don't do that'. Ideally, every death should teach the player something and leave them a little better prepared to face similar challenges in the future.
But your system would poop all over that. In your case, the player might learn something from death, but it would be the wrong lesson, because the aim of the whole thing isn't to teach the player how to adapt to the game, it's to teach the game how to adapt to the player. They could make exactly the same mistake next time and come out OK because the game has shifted the goalposts. Worse, the player isn't even being punished this time because they did something wrong - they're being punished because the game did something wrong.
From a narrative perspective, player death is usually a bad thing because it interrupts - and perhaps ends forever - that narrative. A lot of adventure games, for example, have no death states because they don't want to have to pull the player out of the story by cutting it off and making them go back a bit. The 2008 Prince of Persia also - rather bravely - did away with death altogether (which I personally hated, but then I'm a gamey-game kinda guy).
So your system ends up trying to be all things to all people but succeeding in being nothing to anybody. Gamey-gamers won't like feeling like their actions have no consequence and narrative-role-playey people will just be annoyed that they've died a death that served no purpose and don't have that unicorn they wished for.
Rogue was a very difficult game because all games were like that then. Go and play even a Disney game from the 1980s. They were hard as hell. Certain people, like me, were prepared to play them even so. Why? Because I was a kid, because I had lots of free time and not much to do but fight with my brother. Because getting to the end of Black belt would be the ultimate in bragging rights. But these days things are different. Who's got time to play a game with only 10 minutes of actual game play (http://www.youtube.com/watch?v=zXlNS3biABY), but which takes months of hours a day practice to beat?
Sure, a difficult game will force you to improve so you can beat it. You can't just coast through. But there has to be a better way than save die reload or die restart infinite loop. At the end of the day we want people to play our games (I assume) and there aren't many people with the patience to put up with that kind of thing any more.
None of this is really relevant, though - we're not talking about absolute difficulty, we're talking about auto-adjusting difficulty, which is a separate issue. If you want to make a game easier then you can just go ahead and balance it so it's easier. Likewise, if you want to make classes other than clerics viable then you can balance the classes to make them all equally difficult. If you want a game with more than 10 minutes of actual game-play then make a game with more than 10 minutes of actual game-play. There are other solutions to these problems which don't involve compromising a core mechanic of the game. By far and away the best option would seem to be to provide a suitable range of difficulty levels so that the players themselves can choose what it is they want to get out of the game.
-
Oh well, different horses for different courses.
in my case I'm sure this will be a useful system but in other cases it probably wouldn't work.
-
Oh well, different horses for different courses.
in my case I'm sure this will be a useful system but in other cases it probably wouldn't work.
I agree with all the criticisms of your idea to shape the game around the player - I've seen it tried a few times before and there were always counter-intuitive consequences. But don't let that discourage you. Make a prototype and let people try it. Prove everyone wrong. Maybe a 7DRL in March?
-
Oh well, different horses for different courses.
in my case I'm sure this will be a useful system but in other cases it probably wouldn't work.
I agree with all the criticisms of your idea to shape the game around the player - I've seen it tried a few times before and there were always counter-intuitive consequences. But don't let that discourage you. Make a prototype and let people try it. Prove everyone wrong. Maybe a 7DRL in March?
thanks,
I'm certain it'll work in my own long term game project, and I know that it will work in certain situations, so it would be fun to show other situation where it could be used. Now that ive got a lot of the basic code worked out it wouldnt be impossible to do such a thing.
-
just to add my 2 cents, I use a probability distribution class. It basically takes in an array of objects each having a chance and a value like so:
javascript:
var test = new jsRL.probabiltyArray([
{'value':'orc','chance':30}, //50% chance
{'value':'gnoll','chance':15}, //25% chance
{'value':'wizard','chance':10}, // 16.666% chance
{'value':'dragon','chance':5} // 8.333% chance
]);
then I can just call 'test.getRandomValue()' and it will pass a value back with a probability distribution exactly like that denoted in the total sum of the chance properties.
here is the javascript class below: (jsRL is the namespace for my game's library (https://github.com/gregbillings/jsRogue/blob/master/js/probabilityArray.js))
jsRL.probabilityArray = function(arr){
this.items = arr;
this.totProbabilities = [];
this.maxChance = 0;
for(var i = 0; i < this.items.length;i++) {
this.maxChance += this.items[i].chance;
this.totProbabilities.push(this.maxChance);
}
};
jsRL.probabilityArray.prototype.getRandomValue = function(){
var choice = jsRL.rand(this.maxChance);
for(var i = 0; i < this.items.length;i++){
if(this.totProbabilities[i] >= choice){
return this.items[i].value;
}
}
};
-
I dunno yet how well this'll work, but I've put the infrastructure in place to do it this way just in the last couple of days....
I have a "generation table manager" that allows initialization functions to create and populate a set of named tables. There are predefined tables that the standard map generators use to generate things; the map generators choose their tables based on depth and theme. (Yes, there are themed map generators: some of them make themed areas such as The Ant Hill, The Diggle Dens, Dwarf Fortresses, Dragonkeep caverns, Goblin cities, the Stinking Swamp, etc). An Init function could also create a new table and register a new themed map generator to use it.
Each entry in a generation table has an oddment (what you've been calling a "weight") and a generation function that it can call. The generation function gets some general information like the depth (dungeon level) and the total strength of monsters already generated on that level, when it's called.
The generation function is then responsible for actually creating things to put into the dungeon, and it can generate different things depending on its arguments. So the same generation function can generate a "scout ant" if called on level 1-6, or "2 soldier ants" on level 2-7, or "7 soldier and 4 forager ants" if called on level 5-9, etc. Or a different generation function could create a runaway goblin slave in the early levels, or a hunting party of goblins a bit deeper, or a detachment of goblin cavalry, complete with armor, lances, and saddled Battle Swine, if you're getting close to their main city.
As I see it, it gives me a lot of flexibility to make fine adjustments in monster balance working at the level of the generators (different mixes of probabilities depending on depth etc, without messing with the probabilities of anything generated by anything else), at the level of tables (different oddments for generation functions), and at the level of dungeon generators (use different mix of tables). But it won't be at all simple to say what the likelihood of meeting a particular monster on a given level will be, and it's not at all clear that this was the best way to do it - it might turn out to have been a better idea to try to control probabilities in a more central way that's easier to comprehend.
Like most of the things I do, it's insane overengineering. ::)
-
I've thought this when planning Teemu's new more dynamic monster generation and decided to try the simplest possible way by giving a threat value to each monster based on generic feel about how difficult the monster is. Then each level has a max value for threat and monsters are generated randomly until max reached. Monster packs could represent a collective threat amount. There could be some random variations in threat amount and/or the amount of monsters which could exceed the threat max or sometimes create much less monsters than usual. I also believe that the player should adapt to the situation rather than the game trying to create more or less monsters based on player's stats etc.