Last active
August 1, 2024 04:16
-
-
Save AzzaDeveloper/0bd2d718b659c5ab1cf9c60c0ee3d980 to your computer and use it in GitHub Desktop.
Shrine of order algorithm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local MAXIMUM_REDUCTION = 25 | |
-- Table noting racial stats | |
local racialStats = { | |
Khan = { | |
Strength = 2, | |
Agility = 2, | |
}, | |
} | |
-- This is the stat table that will be modified by Shrine of Order. | |
local startingStats = { | |
Strength = 52, | |
Fortitude = 90, | |
Agility = 30, | |
Intelligence = 12, | |
Willpower = 31, | |
Charisma = 60, | |
-- | |
Flamecharm = 30, | |
Frostdraw = 0, | |
Thundercall = 35, | |
Galebreathe = 0, | |
Shadowcast = 0, | |
Ironsing = 0, | |
-- | |
LightWeapon = 0, | |
MediumWeapon = 88, | |
HeavyWeapon = 0, | |
} | |
local attunements = { "Flamecharm", "Frostdraw", "Thundercall", "Galebreathe", "Shadowcast", "Ironsing" } | |
-- For this example, I'll assume that the player is a Khan | |
local playerStats = { | |
Race = "Khan", | |
PointsSpent = 0, | |
} | |
-- | |
function order(stats) | |
-- Calculate the points spent so far in the build, not counting racials | |
for statName, v in stats do | |
if racialStats[playerStats.Race][statName] then | |
v -= racialStats[playerStats.Race][statName] | |
end | |
playerStats.PointsSpent += v | |
end | |
-- Save the points spent so far as point start | |
local pointsStart = playerStats.PointsSpent | |
-- Shallow copy the current stats as pre-shrine | |
local preshrineBuild = table.clone(stats) | |
-- Find out which stats are invested, so shrine knows to | |
-- reinvest into these stas. | |
local affectedStats = {} | |
for statName, statValue in stats do | |
-- Checks if this should be divided into | |
if statValue > 0 then | |
-- Check whether this is a stat affected by | |
-- racial stats. | |
if racialStats[playerStats.Race][statName] then | |
if racialStats[playerStats.Race][statName] > 0 then | |
if statValue - racialStats[playerStats.Race][statName] == 0 then | |
-- Skipping if stat - racialStat equals to 0 (have not invested) | |
continue | |
end | |
end | |
end | |
-- Add the affected stat to the affected stats table | |
table.insert(affectedStats, statName) | |
end | |
end | |
-- Initial division of points to every affected stats | |
for statName, _ in stats do | |
if table.find(affectedStats, statName) then | |
stats[statName] = pointsStart / #affectedStats | |
end | |
end | |
-- Prepare the bottlenecking step. For this algo, | |
-- bottlenecking refers to making sure stat reduction | |
-- is not larger than 25. | |
local bottleneckedDivideBy = #affectedStats | |
local bottlenecked = {} | |
local bottleneckedStats = false | |
local previousStats = table.clone(stats) | |
-- | |
repeat | |
local bottleneckedPoints = 0 | |
bottleneckedStats = false | |
for statName, statValue in stats do | |
-- Only bottleneck non-attunement affected stats | |
if not table.find(attunements, statName) and table.find(affectedStats, statName) then | |
local prevStat = previousStats[statName] | |
local shrineStat = preshrineBuild[statName] | |
if shrineStat - statValue > MAXIMUM_REDUCTION then | |
-- If the stat difference is larger than 25, set the stat to preshrine value - 25 | |
-- This is refer to as the bottlenecked stat. | |
stats[statName] = shrineStat - MAXIMUM_REDUCTION | |
-- Add the difference to bottlenecked points, which will be used to redistribute to | |
-- other, non-bottenecked stats. | |
bottleneckedPoints += stats[statName] - prevStat | |
-- Add this stat a table of bottlencked stats. | |
table.insert(bottlenecked, statName) | |
-- Reduce the division counter by 1 (no longer take this stat into account) | |
bottleneckedDivideBy = bottleneckedDivideBy - 1 | |
end | |
end | |
end | |
-- Averaging out bottlenecked points | |
for statName, statValue in stats do | |
if table.find(affectedStats, statName) and not table.find(bottlenecked, statName) then | |
-- Reduce from this stat the bottlenecked points divided | |
-- by the number of bottlenecked stats | |
-- Essentially, it takes away the same amount | |
-- from every stat to make up for the bottlenecked | |
-- stat. | |
stats[statName] = statValue - bottleneckedPoints / bottleneckedDivideBy | |
-- Check if after this step, any stat reduction is still | |
-- larger than 25. | |
if not table.find(attunements, statName) then | |
if preshrineBuild[statName] - statValue > 25 then | |
-- If so, set the bottlenecked stats to true, so the | |
-- loop will repeat. | |
bottleneckedStats = true | |
end | |
end | |
end | |
end | |
previousStats = table.clone(stats) | |
-- Repeat this step until no stat difference is larger than 25 | |
until not bottleneckedStats | |
-- Round down all the stats. | |
-- The way this algorithm works makes sure that | |
-- all stats by the end is always equal, so we round down | |
-- and refund players the extra points. | |
for statName, statValue in stats do | |
stats[statName] = math.floor(statValue) | |
end | |
-- Calculate the points spent so far in the build after shrining | |
local pointsSpentAfterShrine = 0 | |
for statName, v in stats do | |
if racialStats[playerStats.Race][statName] then | |
v -= racialStats[playerStats.Race][statName] | |
end | |
pointsSpentAfterShrine += v | |
end | |
-- Refund the points by subtracting the points spent after shrine | |
-- from the points spent before shrine. | |
local sparePoints = pointsStart - pointsSpentAfterShrine; | |
-- This step ensures that the spare points does not exceed the number of affected stats | |
if sparePoints > #affectedStats then | |
for _, statName in affectedStats do | |
stats[statName] = stats[statName] + 1; | |
end | |
end | |
return stats, sparePoints | |
end | |
-- This function returns the end stats and spare points. | |
print(order(startingStats)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment