• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

(solved) Loot drop chance changing through the game

S

Smenkare

Guest
I saw here some drop% codes but they seem "static". My idea is:

I have 6 items which boss can drop : A, B, C, D, E, F (for example)
There is variable itemCount, every item has it. Through the game it changes, before a boss fight it looks like A x 1, B x 2, C x 1, D x 4, E x 5 (for example)

When Boss dies he drops items A, C, D from the current pool, cause we didnt make required things for other items he can drop through the game. So we have one item A, one item C and four items D. The game engine creates them all, random pick one which we can loot, the rest is destroyed. Chances are A 16%, C 16%, D 64%.
This system is not so accurate but its the one which would work for me. I need two variables with item multiplier and the current pool of items which can drop.

Now my question is, is there any better way to do system like this one, and as better i understand faster and simpler. My other question is what commands should i use, gml is not my strong side. My variable current pool is active for A, C, D. So i should use some command to create all items with true value is this variable and loop it for D? The rest of created items can be outside game window and i will just destroy them after picking item from the boss i think. Please help me with this idea and im sorry for my english, i did my best with trying to explain. Thank you for your help and time.
 

CloseRange

Member
It's a bit confusing what you're asking this is how I understand it so correct me if I'm wrong:
there is 1 pool of all items.
When the boss dies it selects some items from that global pool, creating it's own pool
from this new pool one item is selected and that's the item that gets dropped.
this item that was selected gets removed from the global pool so it can't be dropped again (or at least a smaller chance of it being selected)

So here are my tips
I assume you came from an object oriented background. You should know that the idea of classes is a bit different than that of creating objects in game maker.
In other languages having 1000 instances of a custom class is the same as 1000 variables. The game wont run any slower.
Having 1000 object instances active in game maker will cause slowdown. Because every object stores a bunch more variables and executes things behind the scenes.

So in general it's better that pooling systems don't use many instances of differant objects but instead use one object (like a obj_main or obj_world) and store an array that gets selected from.
I'm sure me and a lot of others could help further but again it's a bit unclear what you're idea is.
 
S

Smenkare

Guest
It's a good tip, i will definetly use classes. My problem is my gml syntax is poor. I need this system to be dynamic, to drop chance change through the game, through decisions player made. From the start every item has the same drop chance but in the end i want these values to change. And in my head if i want to have higher chance to loot some item i need more these items. Lets say i have 3 items - A, B, C if i pick random its 33% but if i have more B items its higher chance that i loot B item. My problem is to make it work, to make drop chance values dynamic. I hope somebody will understand my problem :(
 
S

Smenkare

Guest
And i dont think i need to use some percentage heavy math system to make it work but i just dont how to write my idea as a code...
 

CloseRange

Member
i will definetly use classes
You misunderstand. GML does not have classes I was just using it as a comparison.

As for dynamic chances that's just having each item have some weight and then modifying those weights later down the line. Create a script:
Code:
/// makeItem(index, object)
var i = argument[0];
item[i] = argument[1];
weight[i] = 10; // every object starts with a weight of 10
total_items = max(total_items, i); // this keeps track of how many items there are in total
then have some global object like obj_main or obj_core, something where you can have it control these objects (make sure it's persistant)
in the create event you can now add items like so:
Code:
makeItem(0, obj_sword);
makeItem(1, obj_potion);
makeItem(2, obj_armor);
makeItem(3, obj_something_else);
// note that the first argument must be different for all objects
create a script to allow you to select an object
Code:
/// getWeightedItem()
with(obj_main) { // whatever object you made the main object
     var n = 0;
     for(var i=0; i<total_items; i++)
          repeat(weight[i]) pool[n++] = item[i]; // create a 'pool' for every object (based on weights)
     return pool[irandom(0, n)];
}
then any time you need to select an object you can do this:
Code:
var item = getWeightedItem();
instance_create_depth(x, y, 0, item);
and lets say you wanted to make the potion have a smaller chance of being selected.
We made the index for the potion '1' (when we called makeItem(1, obj_potion))
so we hust change that weight:
Code:
obj_main.weight[1] = 5
 
If you store the probability of each item dropped as a weight in an array, what you can do is choose a random value between zero, and the sum of all item weights. Then loop through the array, until you've reached a point where the sum of all item weights up to that point is greater than the random value you've chosen.

for(var t = 0, i = -1, r = random(weight_sum); t < r; t += items[i,1]){i+=1;}
if (i > -1) { choice = items[i,0]; }

In that example, the class of object to drop is stored at items[n,0], and the weight is stored at items[n,1]. individual weights can be any real value greater than or equal to zero.

You will need to know the sum of the weights, which you can add up using a loop.
weight_sum = 0;
for(var i = 0; i < num_items_in_array; i += 1) { weight_sum += items[i,1]; }
 
Last edited:
S

Smenkare

Guest
3 items A, B,C.

A weight is 7
B weight is 15
C weight is 5

Sum is 27. Random 1-27 for example is 23. I loop with adding weights and after adding c weight variable is higher than 23 so it drops C Item. It works like that, am i correct?
 
the random value you select should actually be from 0 to 27, not 1 to 27. Neither the random number nor the weights need be integers. Make sure you use random(weight_sum) and not irandom(weight_sum)!

EDIT:

Important note.

There is a remote risk that what I posted above could fail, in the event that random returns a very small number. This risk increases with the use of very small (non-zero) weights. One way to mitigate this problem is to set the math epsilon to an extremely small value before performing the random selection. Afterward, you can return the epsilon to it's previous value if you wish.
 
Last edited:
S

Smenkare

Guest
I have some questions. Is it possible to store not values but variables in arrays?
This weight system works great in arrays but if pool of items is dynamic, it changes through the game, will it work in ds_list or ds_map in the same way?
I mean i would add items to a list, pick item, clear the list etc
 
H

Homunculus

Guest
I have some questions. Is it possible to store not values but variables in arrays?
This weight system works great in arrays but if pool of items is dynamic, it changes through the game, will it work in ds_list or ds_map in the same way?
I mean i would add items to a list, pick item, clear the list etc
Not sure what you mean by "variables in arrays" honestly. If you create the pool of items every time from scratch, it doesn't really matter if it's an array or a ds_list you pick from. If instead you intend to keep that structure for future loot picks, by emptying it instead, you have to use a list, since you can't remove or clear an array in GM.
 
S

Smenkare

Guest
for(var t = 0, i = -1, r = random(weight_sum); t < r; t += items[i,1]){i+=1;}
if (i > -1) { choice = items[i,0]; }

How can this loop work with ds_list. I cant imagine how it should look.
 
H

Homunculus

Guest
The above assumes you have items and weights stored in a 2d array, which may or may not be your case. You can't do the exact same thing with a ds_list, since a ds_list is a one dimensional structure. If however your weights are stored somewhere else (for example, in a ds_map with the items as key and the weight as value), you can definitely do it with a ds_list as well.

I still think you might need to review how you are currently storing the items and their weights. At some point, you'll have to create (or fill) the array / list of possible drops, and to do that you need to be able to access:

1. Which objects you can to add to the pool of possible outcomes
2. Their weight
3. Their availability (if the item has been "unlocked" as a drop)

This comes before anything that has to do with selecting a random item based on a weight. If you don't have this planned out, you are skipping a step.

Once you are able to print out all of the above to the console for a specific boss, you can then think about your array / ds_list where you need your random drop chance to happen, and that's when @flyingsaucerinvasion code becomes relevant.
 
Last edited by a moderator:
S

Smenkare

Guest
So if i understand - iteration through ds_list works the same way like iteration through the array?
 
H

Homunculus

Guest
Well yes, you just get the items out in a different way. A for loop is not tied to the data structure you are using.

Obviously, if you are iterating over a list you’ll use the ds_list accessor list[| i], if it’s an array array or array[i, j] in the 2d case.
 
Top