Project Hikari Development Blog

Dropping Random Bonus Items

| Comments

Since at this point in time the player is able to shoot and destroy enemies it has become necessary to spawn bonus items for the player to collect. In Mega Man games there aren’t very many bonus items to choose from so this is a pretty simple task. The original games used a simple “bonus probability table” and a random number to pick which item to spawn. I basically wanted to do the same thing so I wrote a small program to test it out. The idea is you simply store a “range” paired with an item identifier. Then you pick a random number, determine which range it falls into, and spawn whatever item was identified by the range.

So, let’s say we have the following chances:

  • Extra Life (1%)
  • Large Health Energy (2%)
  • Large Weapon Energy (2%)
  • Small Health Energy (15%)
  • Small Weapon Energy (15%)
  • Nothing (65%)

Given those chances, the following program allows us to “pick” a single item randomly with the correct percentage chance.

(RandomDrop.cpp) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <string>
#include <utility>
#include <vector>

std::string pickRandom(const std::vector< std::pair<int, std::string> > & chances, int roll) {
  std::string result("Nothing");

  if(chances.size() > 0) {
    int lowerBound = 0;
    int upperBound = 0;

    for(auto it = std::begin(chances), end = std::end(chances); it != end; it++) {
      const auto & chance = *it;

      upperBound = lowerBound + chance.first;

      if(roll >= lowerBound && roll < upperBound) {
        result = chance.second;
        break;
      }

      // Advance the lower bound
      lowerBound = upperBound;
    }
  }

  return result;
}

int main() {

  std::vector< std::pair<int, std::string> > chances;

  chances.push_back(std::make_pair(1,  "Extra Life"));
  chances.push_back(std::make_pair(2,  "Large Health Energy"));
  chances.push_back(std::make_pair(2,  "Large Weapon Energy"));
  chances.push_back(std::make_pair(15, "Small Health Energy"));
  chances.push_back(std::make_pair(15, "Small Weapon Energy"));

  for(int i = 0; i < 50; ++i) {
    std::cout << i << " = " << pickRandom(chances, i) << std::endl;
  }

  return 0;

}

When it runs, we get the expected output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
0 = Extra Life
1 = Large Health Energy
2 = Large Health Energy
3 = Large Weapon Energy
4 = Large Weapon Energy
5 = Small Health Energy
6 = Small Health Energy
7 = Small Health Energy
8 = Small Health Energy
9 = Small Health Energy
10 = Small Health Energy
11 = Small Health Energy
12 = Small Health Energy
13 = Small Health Energy
14 = Small Health Energy
15 = Small Health Energy
16 = Small Health Energy
17 = Small Health Energy
18 = Small Health Energy
19 = Small Health Energy
20 = Small Weapon Energy
21 = Small Weapon Energy
22 = Small Weapon Energy
23 = Small Weapon Energy
24 = Small Weapon Energy
25 = Small Weapon Energy
26 = Small Weapon Energy
27 = Small Weapon Energy
28 = Small Weapon Energy
29 = Small Weapon Energy
30 = Small Weapon Energy
31 = Small Weapon Energy
32 = Small Weapon Energy
33 = Small Weapon Energy
34 = Small Weapon Energy
35 = Nothing
36 = Nothing
37 = Nothing
38 = Nothing
39 = Nothing
40 = Nothing
41 = Nothing
42 = Nothing
43 = Nothing
44 = Nothing
45 = Nothing
46 = Nothing
47 = Nothing
48 = Nothing
49 = Nothing

It’s simple. Each std::pair<int, std::string> stores a drop percentage and an item identifier. Storing these in a vector creates ranges between the entries, then you just sequentially check each of the ranges until you find a match. If you don’t find a match then nothing is spawned. I’ve implemented this into the game already and it’s working as intended.

Like in most cases, the simple approach is the best.

Comments