Sunday, December 7, 2014

Loot Drop Tables

Many games have loot. Usually this drops randomly. Loot drops are a pretty mundane topic, but one that almost every designer runs into at some point. Here are some best practices I've encountered over the years. Many thanks to everyone who contributed to these tips and tricks.

Your basic loot table

The goal is to drop some set of items at a given probability. Let’s say when you defeat an enemy, you have a chance of getting shield, a rare sword or nothing at all.

Example
lootTable
  item:
    name: sword
    weight: 10
  item:
    name: shield
    weight: 40
  item:
    name: null
    weight: 50

Setup
  • Item: An item is something you want give the player.
  • Loot Table: A set of items is put into a loot table. This is just a bucket of items. For example a loot table might include: Sword, Shield, Null.
  • Weight: An item has a drop weight: 1 to 10,000. For example a sword might have a drop rate of 10.
  • Null items: One of the items in the loot bucket is 'null' which means if that is rolled, no loot is given
Rolling for loot
  • Total probability: First, sum all the weights in the bucket. In the example above, that's 10+40+50 = 100. They don't need to add up to 100 since these aren't percentages.
  • Next assign each item a range. Sword = 1-10, Shield = 11 to 50, Null = 51 to 100
  • Generate a random number from 1 to 100.
  • Compare that number to the ranges. That's the item that drops.
  • Reroll: Generate multiple random numbers to simulate multiple rolls.
So what does this look like to the player? We've got a 10% chance of dropping a sword, a 40% chance of dropping a shield and a 50% chance of getting nothing.

As the designer, I could go in and change Null's weight to 100 and now I've got a 6.6% (10/150) chance of dropping a sword, a 26% (40/150) chance off dropping a shield and a 66% (100/150) chance of dropping nothing.

Mapping onto other common random systems

This system is a simple restating of many other familiar methods of randomness. It is a fun superpower to train your designer brain to be able to switch between understanding any randomness issue in terms of loot tables, cards or dice.

Cards
Imagine deck of cards that you can shuffle and draw from.
  • Each type of card in the deck is an item.
  • The number of cards of a given type is that item’s weight
  • Shuffling the deck is equivalent to assigning each item to a range and generating a random number.
  • Drawing a card is the equivalent of selecting the item that drops.
Now a normal deck of cards has 52 cards, but with loot tables, you don’t need to operate with that constraint. Your decks could have 1000's of cards and a vast array of types. Or they could have tiny decks that are the equivalent of a typical poker hand.

Dice
Dice also map onto loot tables.
  • Each individual die is a loot table.
  • The sides (1-N) are items (labeled 1 through N)
  • Each side gets a weight of ‘1’. (Unless you are using weighted dice!)
  • Multiple dice can be represented as rolling the same loot table multiple times. So 2D6 is the equivalent of sampling a 6 item loot table twice.

Variations

Now that we’ve defined a basic loot table, what else can we do with it?

Variation: Items sets
You can also drops sets of loot. An item doesn’t need to be a single thing. For example, I could extend it so that the players gets a shield and a health potion if that option is selected.

Example
lootTable
  item:
    name: sword
    weight: 10
  item:
    name: shield
    name: healthPotion number: 2
    weight: 40
  item:
    name: null
    weight: 50

Variation: Always drop
A common need is to flag an item so it always drops. One convention is that items with weight '-1' always drop.

Variation: Repeatable randomness
Sometimes you want to be able to repeat a random roll. For example, when a player saves a game and then is able to reload to avoid a bad loot drop, it can lead to very grindy player behavior. If there is an exploit that ruins the game for them, most will happily go for it.

Most contemporary pseudo random number generators use a seed value. As long as you can save that seed value, you can run the random number generator again and get the same result.

Variation: Rolling without replacement
The problem with the system above is that players may, through chance alone, always roll 'null'. This is a common complaint by players. “I played that encounter 3000 times and never got the MegaGoldenLootGun!” This can happen.

In statistics, there are two fundamental types of sampling:
  • Sampling with replacement: You pull the numbers out of the bucket and then after you've recorded what you got, you put them back in. So you have the same chance of getting the same thing again in the next draw.
  • Sampling without replacement: You pull the item out of the bucket and once you’ve recorded it, you set it aside. You have a lower chance of getting that item again and thus a higher chance of getting the remaining items.
Tetris uses sampling without replacement. Each set of Tetris pieces is in a loot table. Every time you get a specific piece, it is removed from the bucket. That way they guarantee that you’ll always get a long piece if you wait long enough.

Here’s how you implement rolling without replacement in a loot table.
  • When you roll an item, reduce its weight by 1. This shorten its range by 1 and shortens the max range by 1 as well.
  • Keep the player's modified loot table around for the next time you roll.
Variation: Guaranteeing specific drops
Sometimes even rolling without replacement isn’t fast enough and you want to guarantee a loot drop. Blizzard does this for certain rare drops so that players don’t grind for very long times.

You could just increase the weight, but a low chance of getting something with a guarantee can feel very different over multiple plays than a slowly increasing chance of getting an item.

Here’s how you implement guaranteed loot drops.
  • When you roll any non-guaranteed item, reduce all non-guaranteed items weight by X%
  • X = 100 / Max number of rolls you before the guaranteed items drop.
  • Keep the player's modified loot table around for the next time you roll.
Example
  • Suppose you want the sword to always drop after 5 turns even though it it only has a 10% chance of dropping.
  • So X = 100 / 5 or 20%.
  • So every time you don’t roll the Sword, the weight for the Shield drops 8 (40*0.2) and the weight for null drops 10 (50*0.2)
  • After 5 turns, the weight for all the other items will be 0 and the sword will have a 100% chance of dropping.
Variation: Hierarchical loot tables
Loot tables are generally source for new resources. However, you can easily run into situations where you are dropping too much or too little of a particular resource. Some sort of constraints would be helpful.

One solution is to use hierarchical loot tables without replacement. When a particular resource runs out, the player doesn’t get any more. We’ve used this for our daily coin awards. We want to give out 100 coins a day, but no more. But we want to do it as part of the loot system.
  • Create two tables: Rewards and DailyCoins.
  • Have the main loot table reference the Daily Coins bucket.
  • When Daily Coins get picked, roll that table and see how many coins you get.
Example
lootTable: Rewards
  item:
    name: sword
    weight: 10
  item:
    name: dailyCoins
    weight: 40
  item:
    name: null
    weight: 50

lootTable: dailyCoins
  type: noReplacement
  refreshRate: Daily
  item:
    name: coin, number: 1
    weight: 10
  item:
    name: coin, number 10
    weight: 4
  item:
    name: coin, number: 50
    weight: 1

In the example above, a player has a 40% chance of getting coins. Then we roll the dailyCoins table and see that they can win a maximum of 100 coins a day with 10 awards of 1 coins, 4 awards of 10 coins and 1 award of 50 coins.

When the dailyCoins loot table is emptied, they’ll get nothing until it refreshes after a day.

Variation: Conditional drops
Sometimes you want to test if you should drop the items base off some external variable. In Realm of the Mad God, we wanted to avoid free riders getting loot for a boss kill without doing at least some damage. So in the loot table, we added a check. If a valuable item in the loot table was rolled, then we'd check to see if the player had done more than X% of damage to the enemy.

You could also build in switches for which loot it valid based off player level or even enemy level. I tend to instead use multiple smaller loot tables, but the system is flexible enough that you can easily architect your data with a few large tables and use of conditionals.

Variation: Modifiers
You can also modify the quantity or weight of a drop based off some external logic. For example, a player with a skill in harvesting could yield 2x as many of a particular item drop compared to a player without that skill. Or you could modify the weight. A high level character might have a -50% weight for all items marked lower than their level. (Thanks to a Reddit commenter for this idea)

Other uses

Drop tables are commonly used for dropping loot. But I also find them useful in other areas.
  • Procedural generation: Use a table to build weapons or characters from components
  • AI: Use a table to select behaviors such as attacks or moves.
This may seem a little silly..surely there are better ways to model AI! However, one way to think about randomness is that it is a very rough first order model of any system. How does the human brain model a system? We make an observation about a system. We note the frequencies and tendencies for those observations to reoccur. It is only much, much later that we start to understand ‘why’ something happens or the causal relationship between parts.

In physics, we often joke that in order to model a cow, a complex biological organism, the first step is to ‘imagine a spherical cow’. By creating a simplistic, easy to work with model, we can often generate useful insights at a very low cost.

Many times, a drop table is a ‘good enough’ human-centric approximation of a complex system. For many systems, most players will never move beyond a basic probabilistic understanding so modeling more complexity is a waste of time. Efficient game design is an exercise in modeling elements only to the minimum level necessary to create the desired experience.

Consider: D&D modeled entire universes with what were essentially loot drop tables. That was a deliberate focus on minimizing systems that were in many ways just secondary flavoring to the core roleplaying.

A loot drop table isn’t the only tool you need, but in many scenarios, it is good enough.

Procedural generation thought experiment

Here’s a simple procedural generation system using drop tables. There are lots of other ways to do this, but this is more to get your brain thinking.

Let’s say you want to build a procedurally generated enemy
  • Start by making a list of unique enemy parts. Maybe your enemy is made up of a type of movement, a type of attack, a defensive buff and a type of treasure.
  • Make loot tables for each one of those parts.
  • For each item in the loot table, give it a power value based off how powerful you think it might be. for example, a knife attack might be weak so it only has a power of 5. But a large hammer attack might have a power of 15.
  • Create another loot table of buffs. These are modifiers to various attributes. For example, ‘Strong’ boost a value on an attack by 20%. You can have debuffs as well ‘Weak’ might diminish a value by -50%. These have reduce the power value of a part.
Now let’s generate an enemy
  • Set a target: Set a target power for your generated enemy. Say you want an enemy of power 40
  • Roll: Roll each of the parts once and add them into a list.
  • Score: Add up all the power values to get a score.
  • Adjust: If the sum of the parts is over the target, add a debuff or roll for a lower power part. If it is under, add a buff or roll for a higher power part.
  • Repeat: Repeat this process until you hit a desired error threshold (distance from power 40) or you've exhausted the number of iterations you are willing to spend.
You now have a procedurally generated enemy. There are tons of tweaks you can do to this basic system, but it works most of the time. As an exercise, think about:
  • Exclusion lists: If two parts are picked that are on the list, throw the enemy away and reroll.
  • Multiple constraints: Parts are scored on multiple criteria. Note, the more constraints you add, the less likely you are to converge on a viable result.

Conclusion

Any time there’s a discussion of randomness, there’s a huge number of secondary issues that come into play. I recommend the following for further reading:
Resist being dogmatic about randomness. Be a broadly educated designer whose aesthetic choices are based on hands on experimentation. A good rule of thumb is that you can't intelligently critique a design tool until you've made a couple games that use it successfully.

Anyway, this is just how I've done loot tables; a mundane part of any working designer's life. I'm curious if other folks have other ways of managing loot (and randomness) that they love and live by.

(And before I forget – I've recently freed up some time to do some games consulting. Ping me if you need help with your games!)

take care,
Danc.