19
\$\begingroup\$

I guess this question pertains more to games like MMO and Diablo-like games.

What are the usual designs for implementing a drop table, where a monster could drop different type of items depending on a percentage? I guess the simplest way there is to have a dictionary of 'percentage weightage' to item types, but this is hard to extend if we wish to introduce new item types (for example, when D2 expansion includes runes and new class items)

\$\endgroup\$
8
  • 5
    \$\begingroup\$ Why is it hard to extend percentage dictionaries when adding new items? I mean if you don't need to have all percentages to make a sum of 100% (and it is a case only if you want a monster always drop single item, which is pretty strange to me), I don't see a problem. \$\endgroup\$
    – n0rd
    Commented Nov 30, 2010 at 10:48
  • 2
    \$\begingroup\$ Say Orc => { 'dagger', 'sword' 'armor' }, and I have a new item type, say rune; I will have to update all the dictionaries associated with each monster type directly. Of course, this is nothing another layer of indirection can't solve. So the question is, how does that layer looks like? \$\endgroup\$
    – Extrakun
    Commented Nov 30, 2010 at 15:35
  • \$\begingroup\$ I'm not sure why you think updating a dictionary is hard. In Python, extending one dictionary with new values is done with a single method, for example. Could you clarify where you think the difficulty is? \$\endgroup\$
    – Kylotan
    Commented Nov 30, 2010 at 19:29
  • 3
    \$\begingroup\$ Extrakun, if you take a look at my answer below, there's an answer to your "another layer of indirection" question. Instead of treating drops as a flat table, you can build them out of nested expressions. If you allow named macros (i.e. functions), you can reuse chunks of a drop table across different entities. \$\endgroup\$
    – munificent
    Commented Dec 2, 2010 at 20:58
  • 3
    \$\begingroup\$ You've just stumbled upon the 'catch' of game development. Sure, you can make a game in two days, but then comes the five years of adding content. And adding content is grinding. GRINDING. \$\endgroup\$
    – Tor Valamo
    Commented Dec 3, 2010 at 12:07

4 Answers 4

24
\$\begingroup\$

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:

  1. Be able to specify what things are dropped completely outside of code.
  2. Simple to implement the core system in code.
  3. 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.

\$\endgroup\$
1
  • \$\begingroup\$ This is one implementation I have not see before. Thanks! \$\endgroup\$
    – Extrakun
    Commented Dec 4, 2010 at 15:47
13
\$\begingroup\$

In Stendhal our loot tables are lists. Each entry contains the name of the item, a min and max quantity and the probability. The internal structure is very similar to what we display on the creature's webpage.

It is important for us that game designers who have a great knowledge of the world can define such things. That is, without understanding complex logic at the program code level. So we don't have the definitions of creatures and items in the program code but moved them to .xml files such as elves.xml or club.xml. We do have a gui editor for them, but most game designers modify the .xml file directly.

In order to make creatures and items easily extendable, we use a building block system: There is no program class for "elf" or "archer elf". But there are a number of behavior related classes such as "coward", "patrol", "aggressive", "archer", "healer". Designers can define new creatures be selecting those behaviors without writing program code: For example to create an "archer elf" draw an elf sprite with a bow and define it as "offensive", "archer". Then define level and similar attributes and add some elvish items to the loot table.

For items we have a similar approach, but it is currently limited to one behavior: A designer can add a new item and define a behavior such as "ConsumableItem", "KeyItem" or "AttackItem", "Spell", "Scroll" without having to program logic.

\$\endgroup\$
9
\$\begingroup\$

In D&D tabletop gaming there is a concept of loot types. Most monsters will drop from one or more of the tables and these tables would be what you would update in your expansion. The monsters would still drop "65% common, 10% gems, 15% art, 10% tools" but you'd update what was in each of these tables.

e.g. Gems contains slots with random ranges that return "1 gem(25%)2 gems(50%)5 gems(75%)100 gems". and when you want to add special rune gems, update the table to "1 gem(25%)2 gems(50%)5 gems(75%)100 gems(95%)1 runegem".

But, on the other hand, if you've already got percentage weighting, why not just update all the monster tables in your expansion? Surely tables like this are small payload compared to textures and meshes. Also you don't need to keep the percentages tallying up to 100 either, that's just an assumption you made to start with and you can tally up the real total before generating your random value. If the weightings add to up 120, then just generate a value from 1-120 instead of 1-100.

\$\endgroup\$
4
\$\begingroup\$

On the surface this seems the same as the "weighted random selection" problem.

Algorithm for determining random events

Allocate relative probabilities to each event, add them up, then pick a random number within that range to decide which event you want.

Even if you prefer to use percentages - which is the same system, just scaled to 100 - you're overestimating how hard it is to add stuff. If you have 100% and then add 20% in an expansion, just divide all the values by (120/100) and you're back down to a total of 100%.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged .