Applying Luck Multiplier

Yeah, there’s a lot of topics about this, but I can’t seem to wrap my head around the explanation for this. How do you implement it, and, how does it work like that?

Here’s my code for the luck system if you’re wondering, its pretty simple but trustworthy nonetheless.

local weight = {
}	orb = {
		{'Speed', 1}
	},
	speed = {
		{'CommonSpeed', 1/2},
		{'UncommonSpeed', 1/4},
		{'RareSpeed', 1/8},
		{'EpicSpeed', 1/16},
		{'LegendarySpeed', 1/32},
		{'MythicalSpeed', 1/64},
		{'EtherealSpeed', 1/128},
		{'UltimateSpeed', 1/256}
	,
	boost = {
		{'Boost1', 0.75},
		{'Boost2', 0.25},
	}
}

function ChanceRoll(table: table, luck: number)
	local Chance = Random.new():NextNumber()
	local Counter = 1
	for _, v in ipairs(table) do
		Counter -= v[2]
		if Chance >= Counter then
			return {v[1], Chance}
		end
	end
	if Counter > 0 then
		return {table[#table][1], Chance}
	end
end
1 Like

Depends.
Are you wanting to know more about ones where the luck multiplier is a integer (whole number), or one that supports decimals? The ones with integers are a lot simpler (and is the one I actually know about)

1 Like

About the integers, I guess. So as long as I can easily wrap my head around.

1 Like

With whole integers (at least the way I do it), you can basically just roll n times, then choose the best
(typically rarest) result out of the rolls.

One implementation of this could be something like this:

local weight = {...} -- however you defined it earlier

function ChanceRoll(table: table, luck: number)
	local Best = weight[speed][1] -- just a default value in the event they have 0 luck
	for i = 1, luck do
		local Chance = Random.new():NextNumber()
		local Counter = 1
		for _, v in table do
			Counter -= v[2]
			if Chance >= Counter then
				if Chance < Best then -- if it's rarer than the previous pick
					Best =  {v[1], Chance}
				end
				break
			end
		end
		if Counter > 0 then
			if Chance < Best then -- if it's rarer than the previous pick
				Best =  {table[#table][1], Chance}
			end
		end
	end
	return Best
end
1 Like

This could actually be simplified a bit, because the weightings you have are in decreasing chance, so a higher value of “chance” would always correlate with something equally rare, or more rare. Given that, the code could instead be:

function ChanceRoll(table: table, luck: number)
	local Chance = 0 
	for i = 1, luck do
		Chance = math.max(math.random(), Chance)
		-- Set the Chance value to the best option out of the rolls
	end

	local Counter = 1
	for _, v in ipairs(table) do
		Counter -= v[2]
		if Chance >= Counter then
			return {v[1], Chance}
		end
	end
	if Counter > 0 then
		return {table[#table][1], Chance}
	end
end
1 Like

yeah that ideally works for a mechanic of my game. this is the finalized code by the way, but how would you make decimals though? i mean, there are those tutorials out there, but its hard to adapt it to my code given that i cant understand it.

function ChanceRoll(table: table, luck: number)
	local Chance = 0 
	for i = 1, luck do
		Chance = math.max(Random.new():NextNumber(), Chance)
		-- Set the Chance value to the best option out of the rolls
	end

	local Counter = 1
	for _, v in ipairs(table) do
		Counter -= v[2]
		if Chance >= Counter then
			return {v[1], Chance}
		end
	end
	if Counter > 0 then
		return {table[#table][1], Chance}
	end
end
1 Like

bump

random character text to fill in this gap

1 Like

you can get much better performance and also use decimals if you use some kind of mathematical transformation on the luck value

for example, taking math.pow(a, b) makes the value closer to 1 the lower the value b

then you can adjust b with a sigmoid function so it levels off over time as luck increases

local function sigmoid(n)
  -- math.exp(x) = e^x
  -- put this into desmos for visualisation
  return 1- (1/(1 + math.exp(-n)))
end

local rng = math.random()
local adjustedRng = Math.pow(rng, sigmoid(luck))
1 Like

i dont understand what this means, is there maybe a simpler approach using the other system instead?

1 Like

i dont think its a good idea to use the other one because for high luck values like 1000 your game wont have enough resources to compute that, and even for low luck values your server will be struggling which causes high ping

doing math.pow(luck, number) just makes the luck higher when number is smaller

and then number is found using the sigmoid function to translate between a readable integer luck value that scales linearly and the actual luck value

1 Like

does it do the same thing or what?

1 Like

i prefer to use 1 / chance by the way, because it is much more reliable as an rng. i dont think this will solve anything

1 Like

bump

random text to fill out this character limit thingy

1 Like

yes its basically the same thing but much bettter because you dont have to repeat calculations thousands of times

1/chance is a completely unrelated thing

the sigmoid function just changes your luck number so that it fits with the formula i gave
this code is only for generating a random number like math.random() and then you can use whatever you want to generate values with something like 1/chance

1 Like

just for clarification (which i missed and apologize about):
i’m trying to apply luck multiplier to an rng system, which is the chance roll function
but thanks for your effort though

1 Like

That’s exactly what he suggested. A method of doing that.

1 Like

here is a really simple method i used in my game back then:

function Roll.RollGlider(Luck)
	
	local GliderTbl = deepCopy(Rarities.Gliders);
	
	table.sort(GliderTbl, function(A, B)
		return A.Rarity > B.Rarity;
	end)
	
	local LuckMulti = Luck or 1;
	
	warn(LuckMulti);
	
	for Index, Tbl in GliderTbl do
		if Tbl.Unobtainable then continue end
		local RarityWithLuck = math.round(Tbl.Rarity / LuckMulti);
		local Rarity = math.max(1, RarityWithLuck);
		
		if Random.new():NextInteger(1, Rarity) == 1 or Index == #GliderTbl then
			return Tbl;
		end
	end
end