For a roguelike I was working on, I implemented a pretty flexible data-driven system for generating drops. I've documented it here. It's essentially a little DSL for selecting a number of randomly chosen items.
A simple drop looks like:
1-10 copper coin
It just says to drop a random number of copper coins between 1 and 10. Things get more flexible when you add branches:
one of
turquoise stone (50%)
onyx stone (25%)
malachite stone (15%)
jade stone (10%)
A "one of" selects one of its child branches based on the given probabilities and then evaluates that. Drops can drop more than one item:
any of
turquoise stone (50%)
onyx stone (25%)
malachite stone (15%)
jade stone (10%)
This will evaluate all subbranches and drop them if a roll against their probability passes. There are also some other branches for selecting an item based on dungeon and player level.
Because these can get complex, it also allows you to define named macros, essentially functions that expand a branch expression and can be reused in multiple drops. That way if, for example, all dwarves drop the same kind of loot, you can make a single macro for that and use it across all of those monster types instead of copying and pasting huge drop tables.
An example of one monster's drop:
:: ancient dragon
glyph = D
groups = dragon
drops
(coins)
2-3(1:8) one of
(any-weapon)
(any-armor)
Here, (coins)
, (any-weapon)
, and (any-armor)
are all macro calls:
(any-armor)
one of
(shield)
(helm)
(boots)
(gloves)
(cloak)
(robe)
(soft-armor)
(hard-armor)
which in turn calls things like:
(cloak)
one near level
cloak (10)
velvet cloak (20)
fur-lined cloak (50)
You can nest drop expressions arbitrarily deeply like a real programming language. This gives you the composability that a simple table-based approach won't give.
Like all data-driven systems, you can overwhelm yourself by building impenetrably complex drops, but it meets my goals:
- Be able to specify what things are dropped completely outside of code.
- Simple to implement the core system in code.
- Be able to tune what specific monsters drop so that the player can do goal-oriented exploration. ("I need a necklace. I'll look for dwarves since they tend to drop them.")
The C# code that implements this is here.