2
\$\begingroup\$

I have an RPG game where players can fight monsters limitless, and can get drops from these monsters.

Currently I store in my database a table called monster_drops with find_rate column which is a number between 1 to 10,000. Whenever a player kill a monster, a number is generated and if it's lower from the current number stored in the monster find_rate column, he gets the item.

For example, if the find_rate of the item will be 10k, he will get the item every monster. If the find_rate will be 1k, there is 10% chance he will get it.

My question- is there any better way, maybe simpler, or more accuarate, to calculate and set item drops?

\$\endgroup\$
3
  • 1
    \$\begingroup\$ You could tie the find rate with the item itself being dropped, then also monsters having a chance to drop item/s (bosses have higher chance of dropping item or maybe many items). Think about good old Diablo 2 (may apply to 1/3 not sure), every enemy has an equal chance[1] of dropping an item. Then there is a separate chance of what the item will be (sword, bow, armor etc.) and again another chance of its quality (magic, rare, unique). 1. It is more complicated than this but hopefully gives you more of an idea \$\endgroup\$
    – lozzajp
    Feb 10 '17 at 9:46
  • \$\begingroup\$ I also added an alternative approach, though it is much simpler to implement, it may not be what you want, see edits in my answer. Thanks. \$\endgroup\$ Feb 10 '17 at 13:51
  • \$\begingroup\$ What exactly is wrong with your method? \$\endgroup\$
    – Philipp
    Feb 10 '17 at 15:01
2
\$\begingroup\$

There are 2 scenarios you want to avoid, a player doesn't get a rare drop in 1500 kills and a player gets 5 rare drops in a row. Neither is fun™ to experience. Never getting a rare drop is frustrating and getting cheesed by your own luck robs you of the satisfaction that can only come after working for the reward.

So you'll want to set the drops in relation to longer periods of time. For example in a 1 hour grinding session the player should get between 2 and 5 medium rare drops, he has a 25% of getting a single super rare drop. (numbers are examples, this kind of thing needs very careful tuning).

Instead you can set counters on how often rare items get dropped. When a monster gets killed and you select the drops you can change the item pool based on previous drops the player received; as the last rare drop is further and further in the past you can increase the chance of the player getting another one.

\$\endgroup\$
2
  • \$\begingroup\$ Reminds me of the Guaranteed Gain System from Ultima Online. \$\endgroup\$
    – lozzajp
    Feb 10 '17 at 14:48
  • 2
    \$\begingroup\$ A good method to implement this is a shuffle bag system. Have a persistent array of drops for each player. Put 900 common drops, 90 uncommon drops, 9 rare drops and one legendary drop into that array, shuffle it, and hand out these rewards to the player in the shuffled order. That way you ensure that the player gets exactly that combination of rarities in 1000 reward-events, but because the player doesn't know when, you still have the surprise effect. \$\endgroup\$
    – Philipp
    Feb 10 '17 at 15:05
2
\$\begingroup\$

The problem with random drops, is that those are random! :).

You could get too much misses in a row, or you could be very very lucky. In my game I use an approach that allows both to keep some randomness, and also to avoid to avoid player frustration.

You need 3 columns in your table monster_drop_rates

drop_ID |  find_rate  |  entropy

Basically each enemy will have configured a couple of possible drops.

enemy_ID | drop_ID1 | drop_ID2 | drop_ID3

Note that each drop have its own entropy (the concept of entropy is the same we see in PathOfExile)

however notes that I improve a bit the PoE concept:

foreach(var drop in enemy.drops)
{
     drop.entroy += drop.find_rate*Random(0.75f,1.25f);
}

enemy.drops.SortByEntropy();

var first_drop = enemy.drops.GetFirst();

while(first_drop.entropy>100){

    first_drop.entropy -=100;
    SpawnDrop(first_drop.drop_ID);
    enemy.drops.SortByEntropy();
    first_drop = enemy.drops.GetFirst();
}
    

Basically for each possible drop of a enemy we add a slightly randomized chance, when we get over 100 treshold, we spawn that drop, note that we do that in a loop, so that in example we could spawn multiple items if we set a spawn chance greater than 100 (similiar to what happens in Diablo III).

The content of a drop is randomized too.


What is entropy?

It is just a way to get around very lucky or very unlucky rolls.

In example if you have a item that drops 90% of times, You could still have a 1/1000 chance that item won't be spawned 3 times in a row.

In my system, the % of success is fixed and slightly randomized between 0.75 and 1.25

So actually 90% of success means your success is randomized between

90*0.75 and 90*1.25 = 67,5 and 112,5%

That success rate is added directly to entropy

  • Assume you have entropy starting at 13.
  • You first roll makes the worst possible outcome 90% dropping chance 90*0.75 = 67,5%
  • You add to entropy so 13 + 67,5 = 80,5 (less than 100 => no drop)
  • Regardless of what you next roll is you will be sure you will spawn the item (because 80,5+65,5 > 100)
  • So if you actually make again a worst roll, you have 80,5+65,5 => greater than 100.

Basically with this system you are sure that items are spawned randomly but without too much misses in a row.

Actually this system will never miss more than twice in a row (and it miss twice only 1 out of 1000 cases, when dropping chance is 90%), and will actually spawn 2 items sometimes, but in average it will spawn items equals to 90% of killed enemies. So it is likely on 10 killed enemies:

  • 2 enemies don't spawn drop
  • 1 enemy spawn twice the drop

Note that actually it is impossible that an item spawn twice if its drop_rate is <= than 80%.


Alternative approach

Just set a countdown counter for each drop, but you have to use a "out_of" column instead of a "drop_ratio":

I.E:

drop_ID |  out_of |  counter

So if you have that drop once out of 100 cases, you just randomize it by a +/- 5%

You just subtract 1, when counter reaches 0, you randomize it again.

if( --drop.counter <=0)
{
    DropItem( drop.drop_ID);
    drop.counter = drop.out_of*Random(0.95,1.05),
}

While this approach is simpler, that does not allow to handle spanw chances greater than 100%.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ That's looking really good! But I couldn't figure out what does entroy means and does, if could be awesome if you add some more descriptions about how it works and give an example \$\endgroup\$
    – TheUnreal
    Feb 10 '17 at 13:12
  • \$\begingroup\$ Thanks, I edited the answer to explain the concept of entropy. It is just a "randomness accumulator" (which is by the way what entropy in universe is, apart the fact that we cannot "subtract 100" from entropy of universe :P ) \$\endgroup\$ Feb 10 '17 at 13:23
  • \$\begingroup\$ Don't forget to upvote and accept the answer if it suit your needs :) Actually I'm using it already successfully in my games, while it gives enough randomness it also avoid player frustration with rare drops never spawning or becoming OP because of too much drops. \$\endgroup\$ Feb 10 '17 at 13:27

You must log in to answer this question.

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