Controlling RNG in Treasure Chests
hiddenone
by
hiddenone
on
October 2, 2022
September 20, 2022

Controlling RNG in Treasure Chests

Controlling RNG in Treasure Chests
by

hiddenone

on

October 2, 2022

Randomized rewards from solving puzzles or finding hidden treasures can be a lot of fun for our players, but when the reward is the same item over and over while our players need something else the RNG doesn’t feel fair. So let’s take a look at how we could control RNG to help out our players.

Making a Randomized Treasure Chest

For this tutorial let’s make a treasure chest that gives our player 1 of 4 possible items. Before we jump into controlling the RNG, we should try out a fully random chest. We’ll start by generating a treasure chest by right-clicking our map and choosing Quick Event Creation. It doesn’t matter what we tell the treasure chest to contain since we’ll be deleting the Change Items and Message right away and replacing it with Control Variable and Conditional Branches commands.

Since we’ll have 4 possible items, we can set our variable to a random number between 1 and 4. Then we just need a conditional branch for each number that gives one of the items and our first page is ready to go!

While we’re testing our treasure chests, let’s set page 2 to turn self-switch A Off so that we can open the chest repeatedly.

That’s all our chest needs, so now we can playtest and see it in action! I opened it 4 times and ended up with a different item each time, which is perfect! Unfortunately RNG rarely works that well when actually playing, so it’s possible that when you test it or a player tries it they’d get the same item 4 times.

Controlling RNG with Some Eventing

A randomized treasure chest works, but what if our player needed Magic Water in the middle of a dungeon and they were only getting Stimulants? It can feel a bit unfair for our player, so why don’t we make our chests less likely to give an item if our player already has a bunch of that item. We’ll start with our treasure chest from above, and add in some extras to help give the RNG a nudge. The first thing we’ll add is a Label right before we set our variable to a random number. That gives our event a spot to jump back to, which is a possibility we’ll add to each item’s conditional branch.

Inside each branch, we’ll set a new variable to the number of that item (using the Game Data option) and run another conditional branch that checks if our player has 5 or more of that item. If they do, then we can randomize that second variable to be a number between 1 and 4, that is checked by a third condition to see if the random number is more than 1. If the variable is more than 1, then our Jump to Label command will send the event back to the start and make it more likely to choose a different item. If it is 1 (or if our player has less than 5 of the item), then our player will receive the item.

We want to include a chance at getting the item to avoid having the event lock up if our player has 5 of every item. There are a few ways we could avoid that besides this method, so experiment to see what works best for your game.

Once we set up our other item’s conditional branches like above, we’ll end up with an event that looks like this:

When we playtest, we should see an increase in the items our party is lacking. Of course, since we left the chance for any of the items to be picked we could still get an item we already have 10 of, but it’s a lot less likely. While testing I ended up getting items I needed 3 out of the 4 times, which definitely would help if I was in a dungeon.

Now, this works to help our player get an item they need, but there are limits. Since we put in 5 as the item possession count, if our player has more than 5 of each then no item will be more likely to get picked than another, even if three items have more than 10 and one only has 5. So why don’t we see if we can adjust the drop probability for the item that our player has the least amount of?

Scripting Increased Chances for Item Drops

Let’s use script calls to code this event, since while it is doable with event commands it would be a lot of extra work to get it to work just how we want. It’s easier to code when we have more of a plan to work from beyond “let’s help out our player”, so we should take some time to figure out that plan. For this tutorial, let’s make an array that we’ll fill with our items’ ids and another array with each of those items’ inventory count, and use the inventory array to de,termine the order of item counts from fewest to most items. Once that array is sorted, we can remove the highest count and then check if each item count is still in the array. If it is, then we add that items’ id to our first array. Then we can randomly choose an item’s id from that array to give it to our player. If we did it right then our player should have a better chance of getting an item that they don’t have as many of compared to the items with higher counts.

With that plan figured out, we can get started on the actual coding. We need to set up the array for our items’ ids first, setting it to items so we can call back to it later. For this example our Potion id is 7, Magic Water is 10, Stimulant is 11, and Elixir is 15, so those are the numbers we’ll use. If we don’t want any chance for the item with the highest count to be found, then we could leave this array blank as []. But since we want there to still be a chance at any item, we’ll write our array as:

const items = [7, 10, 11, 15]

Now we need our second array that we can call inventory, where we’ll get the counts for each of the items. We can get those counts using the $gameParty.numItems($dataItems[ID]) script call, where we replace ID with the item’s database id. Since we’re checking 4 items, our array looks like this:

const inventory = [$gameParty.numItems($dataItems[7]), $gameParty.numItems($dataItems[10]), $gameParty.numItems($dataItems[11]), $gameParty.numItems($dataItems[15])]

Our two arrays are ready to go, so now we need to sort our inventory array in numerical order. Luckily, JavaScript has an easy way to do that with the sort method, so we can sort our array with this:

inventory.sort(function(a, b){return a-b});

Now that the item with the highest count is at the end of the array, we can remove it from the array with the splice method, inventory.splice(-1);. Then we can run conditions to see if each of our $gameParty.numItems($dataItems[ID])s are still in the array, and if it is then add that item’s id to our items array using push. So for our Potion, our condition will be:

if (inventory.includes($gameParty.numItems($dataItems[7]))) {items.push(7);}

This would work fine, but it only decreases the chance of the highest count item from being picked. If we want to give our lowest count item more chances in our items array, then we need to repeat the splice and push sections a few times. Let’s do that by making the code loop with the script call while (Condition) { }. For our Condition, we can check how many items are left in the inventory array with length and if it is equal to 1 (meaning we only have our lowest count item left) then the loop stops.

while (inventory.length > 1) { }

All together, this section comes together as:

while (inventory.length > 1) {
inventory.splice(-1);
if (inventory.includes($gameParty.numItems($dataItems[7]))) {items.push(7);}
if (inventory.includes($gameParty.numItems($dataItems[10]))) {items.push(10);}
if (inventory.includes($gameParty.numItems($dataItems[11]))) {items.push(11);}
if (inventory.includes($gameParty.numItems($dataItems[15]))) {items.push(15);}
}

Our items array is now filled with more item ids, so we can randomly pick one with this:

const pick = Math.randomInt(items.length);

We can find our chosen item using items[pick] (which shows the chosen number from the items array), so we can give our player that item with $gameParty.gainItem($dataItems[items[pick]], 1);. That’s all we really need for this to work, but I’m a fan of giving a message saying exactly what was found so we’ll need 2 more script calls to set 2 variables to our chosen item’s icon number and name. So our last part of the Script command is:

$gameVariables.setValue(1, $dataItems[items[pick]].iconIndex);
$gameVariables.setValue(2, $dataItems[items[pick]].name);
$gameParty.gainItem($dataItems[items[pick]], 1);

Since we set both the item’s icon and name to variables, we can use those to cover any of our items in a single message and then set those variables back to 0 so they’re ready to be used elsewhere. With that, we should end up with a treasure chest event that looks like this:

Now that our event is ready, we can playtest and see if it works. Out of 4 treasure chests opened, we ended up with 3 items being ones we didn’t have many of. We could go back and tweak the script to add more chances for items or remove the items from the items array we added at the start to make it so the item that the player has the most of never drops, but that depends on your game and how much you want to help your players out when it comes to finding certain items.

With that, we can help our players out while still leaving some randomness to our treasure chests! How have you added some control over your game’s RNG to help out your players?

Recommended Posts