Adding a little loot randomness with the Random Distribution System (RDS) Library
Today's project from Mike Barthold is one that will be of interest to those thinking about writing a game. His two articles go into some good depth on providing random loot, monsters and maps... I mean who doesn't like loot!
This is something that ran through my head for a long time, until I finally sat down and started thinking logical about it. While I thought through the requirements such a system has to fulfill, it became clear, that almost everything that happens in those games, from the items a vendor offers for sale, to the loot a monster drops when it dies, and even the spawn of monsters (is it a rare mob or a normal monster, any kind of elite creature, whatever) can be put under the same hood: it is a kind of random generated ... thing. Even random generated maps are nothing else than a "drop of map segments".
Almost every game has things that happen "sometimes". If you are happy with code like "
if (new Random().Next(1,10) < 5) ..." then you should probably stop reading - But if you want more than that, if you want to be able to just "design" probabilities for things to happen, if you do not want if...else if...else if... constructs running through random values, then this one here will be a pearl for you. I am quite confident. Go ahead. Read on!
I will show you my all-in-one solution to the "loot-problem", a class library called "RDS" (Random Distribution System) that creates recursive loot-tables, probabilities, result sets and has a set of properties that allows you to control its behavior. To my own surprise, the library was way smaller than I initially thought it would be when finished. The classes are slim, fast and easy to understand; somehow the RDS seems to be more than the sum of its parts. With only a few lines of code you will be able to create fantastic randomized content!
No fancy graphics, no designers, just the core code creating loot for you. It's totally up to you and your imagination, what those classes can do for you and which designers you might want to write to create those tables (SQL based, File based, whatever). Would be great if you'd share some of your ideas with those classes here, too.
We created a RDS that allows us to do these things:
- Drop any number of... things with given probabilities in a recursive structure
- Drop nothing
- React on events (or overrides) when certain things happen
- Simulate loot behavior of big players in the game industry
- Add values or references, re-create instances of living objects
- The option to replace the default.net Randomizer with something more sophisticated
- Basically you can delegate every random decision and chance in your game to RDS
All we need to have fun while making and playing our games is there. The only thing you have not seen so far is, how that all comes to live. Fortunately there is a Part II, which will exactly do that!
So you have gone all the long path of dry theory through Part I and want to see, how it runs? Welcome to Part II of the RDS article!
I will show you now the "how-to's" with a group of small demos (you find all the demo code in the downloadable source code) - it's a simple Console Application as I promised in Part I "no fancy graphics, no designers, just code" that will output the results of RDS.
Demo 4 - Creating (spawning) a group of Monsters, maybe even with a rare mob?
Ok, we need a group of Goblins. Urgent! Shamans, Warriors and, with luck, the almighty BOB! The Goblin the world fears since it heard of him the first time! . Let's find out how we can create a random set up group of Monsters. This demo shows the usage of the
The preparation for this demo includes creating a "Goblin" base class (which is basically just the same as the "
MyItem" class from the other Demos) , from which we derive the Warrior and the Shaman. The allmighty BOB, our rare mob will be of course a Warrior, so we derive BOB : Warrior. We then set up a
RDSTablethat will contain 5 Shamans, 5 Warriors... and BOB.
Why 5 of each class? As I want to show in the demo one possible way to have monsters spawn with different levels. For the Demo we set a variable "AreaLevel = 10" as the level of the Area where we want to spawn our group of monsters. We then add 1 Shaman with AreaLevel-2, 1 with AreaLevel-1, one at par with AreaLevel, and one with +1 and +2. Same for the Warriors. The +2/-2 mobs have a lower probability to spawn, and the even level mobs have the highest probability.
And last but not least, we add BOB with a significantly lower probability to spawn. BOB is
rdsUniqueof course... there can be only one BOB.
Play around with this demo, run it over and over again, until you finally hit BOB. See how the group of monsters looks like in their distribution of levels and types (Shaman, Warrior) and you will see, that this spawns totally random groups of 10 shamans each.
Maybe you want to enhance this demo to make the count of Goblins spawned random, too. Try to add a
NullValueor set up another table of
RDSValue<T>objects (or just roll a dice) to determine the
rdsCountfor the table.
Demo 6 - Random generating a simple map.
Short demo of selecting 25 map pieces randomly to create a 5x5 map. You can create any map size with this system, of course.
The setup here is to demonstrate a new technique: Dynamically enabling and disabling entries of one single table in the
PreResultoverride, based on the exits a map segment has.
We create a class named
MiniDungeon : RDSTable. This table contains lots of
MapSegmentobjects, that derive from
RDSObject. Each Segment has four exits: North, East, South and West. Those boolean flags represent the possible exits of a Segment and are used to modify the states of the contents of the
MapSegmentgets a constructor that takes four boolean parameters, each one describing one of the possible exits. We want to loot only Segments, that can fulfill the needs of the map (i.e. have the desired exits).
In the PreResult override, each
MapSegmentdisables/enables itself based on the requested exits, so that only those Segments stay active that can fulfill the desired exits.
The algorythm of the Map is clearly not the most high sophisticated you have ever seen, but thats not the point of the demo. A demo output of a 5x5 map could look like this, in simple semigraphic console output:
I hope you have now a good idea of what RDS can do for you. I think it is a library with very high value that takes away lots of decision work from you if you agree to really implement (inherit) the RDS classes. It all works together fine and you have almost every thinkable freedom with lots of virtual methods to override.
So you want to see it in action? Here's another demo of his RDS library;
I thought about another use case for the library and came up with the idea of creating a simple card deck class with a shuffle and deal method.
Supported are standard deck sizes (at least what is considered "standard" here in the middle of europe ... ), which are:
- Ace to Ten 20 card deck
- Ace to Seven 32 card deck
- Ace to Deuce Standard 52 card deck (Poker deck)
- Rummy (104 cards + jokers)
- Canasta (156 cards + jokers)
And here are some of the outputs to show:20 card deck: Dealing 4 x 5 cardsT♥ T♦ T♠ K♥ T♣J♠ A♦ A♥ K♣ J♥Q♦ Q♠ K♦ K♠ J♣J♦ A♣ A♠ Q♣ Q♥Poker Deck: Dealing 10x Holdem hands (2 cards)T♦ J♦2♣ 6♦K♣ 4♠5♦ 7♠Q♣ 4♦5♥ 8♣T♣ A♦8♦ 2♠8♥ 9♠K♠ 4♥Dealing Flop : 3♠ Q♠ J♠Dealing Turn : Q♦Dealing River: 6♣
OK, this was another short example of what you can do with RDS and there may be a time when you do some card (or: "stack-of-things") simulation and then maybe you will remember this one here and pick up some ideas.
Have fun with it!
As Mike says, it might not be perfect, but it sure beats starting for scratch, doesn't it?