I had trouble finding a system that let me buff player’s odds in RNG, so I spent a few hours making this solution:
local function GetRandomFromWeightedTable(Table,PropertyName, Rng, Luck)
PropertyName = PropertyName or "Weight"
Luck = math.max(Luck or 0, 0)
Rng = Rng or Random.new()
local Items = {}
local Total = 0
local ItemAmount = 0
local FalloffStrength = 0.33 -- Inverse Base for the distance log
local BiggestItem = 0
for Index,Item in pairs(Table) do
if Item[PropertyName] and type(Item[PropertyName]) == "number" then
Items[Index] = Item[PropertyName]
Total += Item[PropertyName]
ItemAmount += 1
if Item[PropertyName] > BiggestItem then
BiggestItem = Item[PropertyName]
end
end
end
if Total <= 0 then
return nil
elseif ItemAmount == 1 then
for i,v in pairs(Table) do
return v
end
end
-- Luck Adjustment
local InverseLuck = 1 / Luck
local LogBase = 1 / FalloffStrength
for Index,Value in pairs(Items) do
Value = Value / BiggestItem
local Distance = math.log(Value / InverseLuck, LogBase)
if Distance >= 0 then
Value = Value / ((LogBase) + 2) ^ (Distance)
else
end
Items[Index] = Value
end
Total = 0
for Index,Value in pairs(Items) do
Total += Value
end
for Index,Value in pairs(Items) do
Value = Value / Total
Items[Index] = Value
end
local Roll = Rng:NextNumber()
local LastIndex = nil -- Prevent edge case nil returns
for Index,Value in pairs(Items) do
Roll -= Value
if Roll <= 0 then
return Table[Index],Value,Items
end
LastIndex = Index
end
return Table[LastIndex],Items[LastIndex],Items
end
return GetRandomFromWeightedTable
Normally, weighted random selection works by giving each item a weight, where higher weights make an item more common and lower weights make it rarer. The problem is that simply multiplying rare item weights by a luck value often breaks the balance, common items become too weak, rare items become too common, or extremely high luck values make the results feel unrealistic.
This function takes a different approach.
Instead of directly multiplying weights, it gradually compresses the gap between common and rare items using a logarithmic falloff. As luck increases, common items slowly lose some of their dominance while rarer items become progressively more likely to appear. Because the adjustment follows a curve rather than a straight multiplier, the effect stays smooth and predictable, even at high luck values.
Some other details:
- A luck value of 1 behaves identically to the original weighted table.
- Higher luck values increasingly favor rarer entries without completely eliminating common ones.
- After all adjustments are made, the weights are normalized back into probabilities before performing the random roll.
The function returns:
- The selected item.
- Its final normalized probability after luck has been applied.
- The full adjusted probability table (useful for debugging or displaying odds).
Feel free to modify this as I just wanted to help other people with finding one. It works up to 1e100 luck.