Links

Custom Loot Generation Algorithm

v4.0
As development on your project progresses, you may wish to augment the chances a player has of discovering specific loot, or higher quality loot. To do this, you'll likely want to adjust the loot generation algorithm. The Party Based RPG demo game demo scene has an example of this, in the "Lucky" treasure box.
In my own game, "Legend of the Stones", players will have a "Luck" Stat, and I want this value to affect the chances loot being spawned and of ItemAttributes being added to spawned loot.
In the demo, notice that Lucky Treasure Box has a new component which Small Treasure Box does not have. The GenerateItemModificaitonDemo.cs script is where the custom code lives. Open that up to check it out. It implements IHandleLootBoxes. (If you'd like, duplicate this script to make your own version!
For the purposes of the demo, I've included a slider for a float luckValue, valued between 0.1f and 1f. I started by copying the following methods from LootBox.cs into this new script:
  • GenerateItems()
  • SpawnChance()
  • GenerateAttributes()
And I created a new method, PlayerIsLucky(), which returns true if a random float value between 0f and 1f is less than the set luckValue.
The methods were modified in only a few places, to take into account whether the PlayerIsLucky().
// ORIGINAL
if (spawnChance < 1f && RandomFloat() > spawnChance) // If a random value is > our chances, do not spawn
{
.....
// LUCKY VERSION
var spawnChance = SpawnChance(i, totalItemsToSpawn, itemsSetting); // Compute spawn chancesif (spawnChance < 1f
&& (PlayerIsLucky() ? RandomFloatBestOf() : RandomFloat()) > spawnChance // If a random value is > our chances, do not spawn
{
.....
The original version, if the spawnChance is not 100%, checks to see if a RandomFloat() -- a value between 0f and 1f -- is greater than the spawnChance. If true, it means we will not spawn the item. If stopAfterFirstFailure is true, then we will return the current list of generatedItems, otherwise we'll continue, and the script will move on to the next potential item.
The modified version first only checks random values if spawnChance is not 100%, like the original. If the PlayerIsLucky(), then the script will return a float value using the method RandomFloatBestOf(false), which will give the lower of two random float values between 0f and 1f. Otherwise, the script will act as the original does, with a single float.
In essence this gives a lucky player a slightly better chance of having the item spawned. There are similar modifications to the other methods. Have a look at the code to see those changes.
RandomFloatBestOf() is a method in the Utilities class, under the InfinityPBR.Modules namespace. The default options are to provide the higher value of two rolls between 0f and 1f. However, you can adjust the number of rolls, the min and max values, as well as whether higher is better.
For example, this code will provide the highest value between 1f and 10f, out of 4 random value rolls.
RandomFloatBestOf(true, 4, 1f, 10f);

How does this custom code get used?

GameLootBox.cs has this field, which will take in an IHandleLootBoxes component, such as GenerateItemModificaitonDemo.cs , which is populated on Awake(). As long as a script which implements IHandleLootBoxes is attached to the same object as GameLootBox, it will be used here.
GameLootBox.cs
private IHandleLootBoxes _customLootBoxHandler;
private void Awake()
{
_customLootBoxHandler = GetComponent<IHandleLootBoxes>();
}
When GenerateLoot() is called, it will pass along IHandleLootBoxes, which may be null, or may be popualted with a custom script.
GameLootBox.cs
public void GenerateLoot(bool overwrite = true)
{
if (!overwrite && _generated) return;
itemObjectList = LootBox.GenerateLoot(_customLootBoxHandler).Clone();
_generated = true;
HandleLoot();
}
The LootBox Scriptable Object is used to compute the final items generated with the method GenerateItems(). However, if the handler is not null, it will call handler.GenerateItems() instead.
LootBox.cs
public virtual GameItemObjectList GenerateLoot(IHandleLootBoxes handler)
{
GameItemObjectList newList = new GameItemObjectList();
if (handler != null)
{
newList.list.AddRange(itemsSettings.SelectMany(handler.GenerateItems).ToList());
}
else
{
newList.list.AddRange(itemsSettings.SelectMany(GenerateItems).ToList());
}
return newList;
}