• 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!

Legacy GM [Solved] Need ideas to improve item drop chance code

Phoebe Klim

Member
Hi all!

I'm making a game that has Diablo-styled color-coded loot.
I use this code to determine dropped item's rarity:
Code:
    var grade_chance = irandom(10000);
   
    if grade_chance > 9980                                  // Relic - 1 in 500
    {
    grade = 7;
    }
    else if grade_chance <= 9970 && grade_chance > 9920     // Etalon - 1 in 200
    {
    grade = 6;
    }
    else if grade_chance <= 9920 && grade_chance > 9720     // Mythical - 1 in 50
    {
    grade = 5;
    }
    else if grade_chance <= 9720 && grade_chance > 9320     // Fractal - 1 in 25
    {
    grade = 4;
    }
    else if grade_chance <= 9320 && grade_chance > 8320     // Advanced - 1 in 10
    {
    grade = 3;
    }
    else if grade_chance <= 8320 && grade_chance > 6320     // Optimized - 1 in 5
    {
    grade = 2;
    }
    else                                                    // Generic
    {
    grade = 1;
    }
It works fine, I think, but I need to improve it, so I could have items/locations/enemies with different drop chances, e.g.: Enemy that has "+10% Loot Quality", that increases relic drop chance to 1 in 450.

And I don't know how to do that yet.
Need ideas!
 

Miradur

Member
Hi, not tested, but maybe like this:

Code:
var grade_chance = irandom(10000)
loot_quality = 10

If (frac(grade_chance / (500 - loot_quality)) == 0)
{ grade = 7 }
else If (frac(grade_chance / (200 - loot_quality)) == 0)
{ grade = 6 }
else If (frac(grade_chance / (50 - loot_quality)) == 0)
{ grade = 5 }
and so on ...
loot_quality would really have to reflect the value in percent.

Miradur
 
D

Dracodino300

Guest
The system you have I think would work pretty well for what you want. The only change you'd need to make is to have your grade_chance thresholds (9980, 9920 etc) be written as a function of loot quality.

For example, 9980 would become 10000 - 20*(1+loot_quality)
That way, with 10% loot_quality, you'd get a threshold of 10000 - 22 = 9978, which gives you a 1 in 454 chance.

Edit: or to get exactly 1/450, have 10000 - 20/(1 - loot_quality) , though that has the downside of the math breaking at loot_quality >= 100%
 
Last edited by a moderator:

Phoebe Klim

Member
Hi, not tested, but maybe like this:

Code:
var grade_chance = irandom(10000)
loot_quality = 10

If (frac(grade_chance / (500 - loot_quality)) == 0)
{ grade = 7 }
else If (frac(grade_chance / (200 - loot_quality)) == 0)
{ grade = 6 }
else If (frac(grade_chance / (50 - loot_quality)) == 0)
{ grade = 5 }
and so on ...
loot_quality would really have to reflect the value in percent.

Miradur
Well, it kind of works, but it's confusing.
I tried doing (500 / loot_quality) and set loot_quality to 2, but it produced some weird results. It wasn't generating optimized and fractal parts.

Thanks anyway!
 

Miradur

Member
Why do you divide, you have to subtract it?
Here a more extensive example:

Code:
var grade_chance = irandom(10000)
loot_quality = 10       // 10%

If (frac(grade_chance / (500 - ((500 / 100) * loot_quality))) == 0)
{ grade = 7 }
else If (frac(grade_chance / (200 - ((200 / 100) * loot_quality))) == 0)
{ grade = 6 }
else If (frac(grade_chance / (50 - ((50 / 100) * loot_quality))) == 0)
{ grade = 5 }
and so on ...

Miradur
 

Phoebe Klim

Member
Why do you divide, you have to subtract it?
Here a more extensive example:

Code:
var grade_chance = irandom(10000)
loot_quality = 10       // 10%

If (frac(grade_chance / (500 - ((500 / 100) * loot_quality))) == 0)
{ grade = 7 }
else If (frac(grade_chance / (200 - ((200 / 100) * loot_quality))) == 0)
{ grade = 6 }
else If (frac(grade_chance / (50 - ((50 / 100) * loot_quality))) == 0)
{ grade = 5 }
and so on ...

Miradur
Right, you're reducing the drop chance number (500, 200, 50...) by percent. I just halved for testing purposes.
And it didn't work as intended.

Your code says that the more integers you get from dividing drop chance number by grade_chance, the more common item is, right?
And the higher number is, the lower amount of integers it can have?

Doesn't that also mean that some numbers can produce more integers when divided, even if it's lower?

E.g.: If (frac(grade_chance / (500) == 0) produces more integers than If (frac(grade_chance / (499) == 0) because more numbers can be divided to integers from 500?
 
Last edited:

Miradur

Member
To make it work with such small numbers (in your example only 1 as difference), you have to set the
random value to 1000000.

Code:
var grade_chance = irandom(1000000)

Miradur
 

Phoebe Klim

Member
The system you have I think would work pretty well for what you want. The only change you'd need to make is to have your grade_chance thresholds (9980, 9920 etc) be written as a function of loot quality.

For example, 9980 would become 10000 - 20*(1+loot_quality)
That way, with 10% loot_quality, you'd get a threshold of 10000 - 22 = 9978, which gives you a 1 in 454 chance.

Edit: or to get exactly 1/450, have 10000 - 20/(1 - loot_quality) , though that has the downside of the math breaking at loot_quality >= 100%
So like this:
Code:
    // Relic - 1 in 500
    if grade_chance > 10000 - 20 * (1 + loot_quality)                       
    {
    grade = 7;
    }

    // Etalon - 1 in 200
    else if grade_chance <= 10000 - 20 * (1 + loot_quality) && grade_chance > 10000 - 70 * (1 + loot_quality)
    {
    grade = 6;
    }

    // Mythical - 1 in 50
    else if grade_chance <= 10000 - 70 * (1 + loot_quality) && grade_chance > 10000 - 270 * (1 + loot_quality) 
    {
    grade = 5;
    }
Obviously, I would use the scripts or something to make the whole mess easier to read and edit.
 

sylvain_l

Member
As it's only run at kill time, you could even go with a more versatile system using FrostyCat snippet (it's more generic to pick randeomly a thing from a pool of things with different probability):
your call to add some modifier in the mix (if player can have a boost in quality dropping, or some monster have moddifier too, etc...)
Code:
///pick_weighted(item0, weight0, item1, weight1, ...)
// Example: var fruit = pick_weighted("Apple", 5, "Banana", 7, "Cranberry", 16);
{
if (argument_count mod 2 != 0 || argument_count == 0) {
  show_error("Expected an even number of arguments greater than 0, got " + string(argument_count) + ".", true);
}
var item_n = 0,
    items_count = argument_count >> 1,
    items = array_create(items_count),
    cmf = array_create(items_count),
    total = 0,
    i = 0;
repeat (items_count) {
  items[item_n] = argument[i++];
  total += argument[i++];
  cmf[item_n] = total;
  item_n++;
}
var rand = random(total);
for (var j = 0; j < items_count; j++) {
  if (rand < cmf[j]) {
    return items[j];
  }
}
return items[items_count-1];
}
 

NicoFIDI

Member
Well about that you can use obj_region to store all your region local values, souch as items and drop chances;
About the drop chances, you can make an array to set border values and then use a script like this;

this goes on the region obj
Code:
/// Initialize region default
drop_grade = 0;
drop_grade[0] = 6320;
drop_grade[1] = 8320
drop_grade[2] = 9320
drop_grade[3] = 9720
drop_grade[4] = 9920
drop_grade[5] = 9970
drop_grade[6] = 9980
this it's the code you use with the region to get the grade
Code:
/// scr_random_grade(obj_region_id)
var region = argument[0];
var grade_chance = irandom(10000);

for (var i = 0; i < array_length_1d(region.drop_grade); i++) {
    if (grade_chance > region.drop_grade[i])
        return i+1;
}
Change the names to match your code and your understanding for the game.
But i hope this helps :)
 
I'd do this:

Code:
roll = irandom(999 - loot_quality * 10)
if roll <= 1 grade = 7 // 1 in 500
else if roll <= 6 grade = 6 // 1 in 200
else if roll <= 26 grade = 5 // 1 in 50
else if roll <= 66 grade = 4 // 1 in 25
else if roll <= 166 grade = 3 // 1 in 10
else if roll <= 366 grade = 2 // 1 in 5
else grade = 1 // 633 in 1000
the loot_quality works a bit diffrent here tho, 100% means you get grade 7 every time.

also note the 999, irandom include the value you put in and also 0. so irandom(10) can have 11 values while irandom(9) can have 10 values.

edit: on second thought, that loot_quality implementation is quite bad :p
 
Last edited:

Phoebe Klim

Member
@Dracodino300's code in action:

upload_2018-6-17_14-42-19.png

Orange - Relics, base rarity 1 in 500
Red - Etalon parts, base rarity 1 in 200
Purple - Mythical parts, base rarity 1 in 50
Blue - Fractal parts, base rarity 1 in 25
Cyan - Advanced parts, base rarity 1 in 10
Yellow - Optimized parts, base rarity 1 in 5
White - Generic parts
LQ - Loot quality

I think I'm going to stick with this method, seems simple and effective.
I'll be able to tell players "+100% loot quality" and they will know that it will make relics twice as common.
 

Evanski

Raccoon Lord
Forum Staff
Moderator
idk how I would exactly go about doing this until i actually did but I would probably use, Switch(), random() and maybe a few if statments, Might look more into this later
 
Top