Teeps

Designing a Loot System for Your Mobile Game

21 February 2015 by Terrence Donnelly

Hello people of the internet. As you may know (or probably don't), we've been hard at work on an iOS game for a few months now. The above screen shot is not from our game, it's from Diablo III.

Since this is atypical of us, I wanted to set the pace of today's blog post to match suit. I'm going to go over how to build a loot engine for a game, or at least how I went about building ours.

So let's take a moment to set ourselves up. Imagine, if you will, a world full of dragons and mythical creatures (or in our case, re-animated junk). As you lay waste to seemingly endless waves of minions, you happen to see an "Epic Sword of Fancy Pants" drop on the ground. Overwhelmed with delight, you pick it up. Your day just got a whole lot better.

What sorcery, you think. But I am here to tell you, it is not sorcery. It is simply some cleverly placed chunks of code.

I'm not going to go over everything involved, as the post would be far too long and boring and you may perish. However, diving right in, whenever an enemy dies, we call upon our loot engine to see if we should drop anything.

In our Objective-C Interface, LootEngine.h, it looks a little like this:

@interface LootEngine : NSObject

+ (id)rollForLootWithEnemy:(Enemy *)enemy forLevel:(GameLevel)level andWave:(GameWave)wave;

@end

You may be wondering what the level and wave parameters are for. As stated before, they are not sorcery. These are simply enumerations for restricting loot based on level and wave. (Dropping low level stuff earlier on in the game, etc).

In the actual implementation, we have Loot floors and ceilings. If you "raise the roof", you will be less likely to drop any rare or powerful items. These are used when rolling a random number to determine which item, if any, should drop. We have different loot tables for Bosses than we do for Normal enemies. This gives us the opportunity to have bosses drop special items, such as "Old School Game Boy" (which could serve as a shield).

static const NSInteger LOOT_LOWER_WEIGHT_LIMIT = 0;
static const NSInteger LOOT_UPPER_WEIGHT_LIMIT = 2000;

static const NSInteger LOOT_LOWER_WEIGHT_LIMIT_BOSS = 0;
static const NSInteger LOOT_UPPER_WEIGHT_LIMIT_BOSS = 2000;

static const NSInteger LOOT_LOWER_RARITY_LIMIT = 0;
static const NSInteger LOOT_UPPER_RARITY_LIMIT = 2000;

Note the rarity limits. We are actually rolling for rarity after rolling for an item, given that an item drops. Similar to World of Warcraft (Common, Uncommon, Rare, and Epic).

I won't bore you with all of the implementation details, but I will go over a high level of how rolling for the loot works.

  1. - First, we check what kind of enemy. (Normal, or Boss)
  2. - If it is a normal enemy, we roll for a random number between our normal loot floor and our normal loot ceiling
  3. - We then use a for loop to iterate through all of our normal loot, which is defined in our loot tables below
  4. - We grab the array of numbers (lower, and upper) for each item
  5. - We check if the number we rolled falls within the range for the current item
  6. - If it does, we create a new instance of the item using NSClassFromString
  7. - Now, we roll for rarity on the item, and depending on the rarity we roll, we apply any stat bonuses to the item
  8. - We return the item from the function, and voila, the game can now drop the item for our user

Here is what a simple loot table might look like:

+ (NSDictionary *)normalLoot {

    static NSDictionary *normalLoot;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // Key     -   Item Class Name
        // Value   -   Lower and Upper Limits
        normalLoot = @{@"VeryPowerfulItem" :    @[@0, @2],
                       @"ItemTwo" :             @[@3, @15],
                       @"ItemThree" :           @[@16, @30],
                       @"ItemFour" :            @[@31, @40],
                       @"ItemFive" :            @[@41, @50],
                       @"ItemSix" :             @[@51, @60],
                       @"ItemSeven" :           @[@61, @70],
                       @"ItemEight" :           @[@71, @80],
                       @"ItemNine" :            @[@81, @90],
                       @"ItemTen" :             @[@91, @100],
                       @"" :                    @[@101, @(LOOT_UPPER_WEIGHT_LIMIT)]};
    });
    return normalLoot;

}

And a sample rarity table:

+ (NSDictionary *)itemRarities {

    static NSDictionary *itemRarities;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // Key     -   Item Rarity
        // Value   -   Lower and Upper Limits
        itemRarities = @{@"Epic" :                @[@0, @3],
                         @"Rare" :                @[@4, @50],
                         @"Uncommon" :            @[@51, @1000],
                         @"Common" :              @[@1001, @(LOOT_UPPER_RARITY_LIMIT)]};
    });
    return itemRarities;

}

Bearing all of the above in mind, this should set you in the right direction to start dropping all kinds of Epic loot in your games as well.

I hope you found this post useful, and may plenty of loot be in your future.

Let's build your next big thing.

Contact Us