Module:BubbleGraph
From IdleOn MMO Wiki
Documentation for this module may be created at Module:BubbleGraph/doc
local BubbleGraph = {}
function round(num, numDecimalPlaces)
local mult = 10 ^ (numDecimalPlaces or 0)
return math.floor(num * mult + 0.5) / mult
end
function round_format(num)
local str = tostring(num)
return str:find("%.") and str:gsub("0+$", ""):gsub("%.$", "") or str
end
function BubbleGraph.calculate_bonus(func, level, x1, x2)
local result = 0
if func == "add" then
if x2 ~= 0 then
result = (((x1 + x2) / x2 + 0.5 * (level - 1)) / (x1 / x2)) * level * x1
else
result = level * x1
end
elseif func == "decay" then
result = (level * x1) / (level + x2)
elseif func == "intervalAdd" then
result = x1 + math.floor(level / x2)
elseif func == "decayMulti" then
result = 1 + (level * x1) / (level + x2)
elseif func == "bigBase" then
result = x1 + x2 * level
elseif func == "addLower" then
result = x1 + x2 * (level + 1)
elseif func == "decayLower" or func == "decayMultiLower" then
result = x1 * (level + 1) / (level + 1 + x2) - x1 * level / (level + x2)
elseif func == "bigBaseLower" then
result = x2
elseif func == "intervalAddLower" then
result = math.max(math.floor((level + 1) / x2), 0) - math.max(math.floor(level / x2), 0)
elseif func == "reduce" then
result = x1 - x2 * level
elseif func == "reduceLower" then
result = x1 - x2 * (level + 1)
else
result = 0
end
return round(result, 3)
end
function BubbleGraph.generate_graph(x1, x2, func, bubble_color, bubble_name, bubble_number)
local max_levels = 1000
local step = 20
local graph_width = 1330.8
local graph_height = 250
local points = {}
local threshold_level = x2 * 90 / (100 - 90)
local max_bonus = 0
for level = 1, max_levels do
if level == threshold_level then
local bonus = BubbleGraph.calculate_bonus(func, level, x1, x2)
table.insert(points, {level = level, bonus = bonus})
elseif level == 1 or level % step == 0 then
local bonus = BubbleGraph.calculate_bonus(func, level, x1, x2)
table.insert(points, {level = level, bonus = bonus})
if bonus > max_bonus then
max_bonus = bonus
end
end
end
local column_color_start, column_color_end, background_color_start, background_color_end, bubble_image_name, displayVal
if bubble_color == "orange" then
column_color_start = "#ffe787"
column_color_end = "#690e0e"
background_color_start = "#ffffff"
background_color_end = "#ef7500"
bubble_image_name = "OrangeBubble" .. bubble_number
elseif bubble_color == "green" then
column_color_start = "#bfffab"
column_color_end = "#09591a"
background_color_start = "#f7f7ff"
background_color_end = "#3fe855"
bubble_image_name = "GreenBubble" .. bubble_number
elseif bubble_color == "purple" then
column_color_start = "#fcc1ff"
column_color_end = "#350b6a"
background_color_start = "#fffdfa"
background_color_end = "#ca51ee"
bubble_image_name = "PurpleBubble" .. bubble_number
elseif bubble_color == "yellow" then
column_color_start = "#f7ffbd"
column_color_end = "#714200"
background_color_start = "#f7fffa"
background_color_end = "#ecc200"
bubble_image_name = "YellowBubble" .. bubble_number
end
local container =
mw.html.create("div"):css(
{
width = graph_width .. "px",
height = graph_height + 100 .. "px",
position = "relative",
["font-family"] = "'Idleon'",
["text-align"] = "center",
["margin-bottom"] = "50px"
}
)
if bubble_name and bubble_number == nil then
displayVal = none
else
displayVal = initial
end
local bubble_header =
mw.html.create("div"):css(
{
position = "absolute",
top = "-10px",
left = "50%",
width = graph_width + 8 .. "px",
["font-size"] = "35px",
["transform"] = "translateX(-50%)",
["display"] = displayVal
}
):wikitext(string.format("[[File:%s.png|link=]]%s", bubble_image_name, bubble_name))
container:node(bubble_header)
local y_axis =
mw.html.create("div"):css(
{
position = "absolute",
top = "20%",
left = "-2%",
height = graph_height .. "px",
["writing-mode"] = "sideways-lr"
}
):wikitext("Bonus")
container:node(y_axis)
local root =
mw.html.create("div"):css(
{
width = graph_width .. "px",
height = graph_height .. "px",
display = "flex",
["align-items"] = "flex-end",
position = "absolute",
top = "60px",
["background-image"] = string.format(
"linear-gradient(%s, %s)",
background_color_start,
background_color_end
),
border = "1px solid #000",
["border-radius"] = "3px",
padding = "3px"
}
)
for _, point in ipairs(points) do
local height = math.floor((point.bonus / max_bonus) * graph_height)
if func == "decay" or func == "decayMulti" then
if point.level >= threshold_level then
local reduction_factor = math.exp(-((point.level - threshold_level) / 100))
height = height * reduction_factor
end
end
local gradient_ratio
local color
if point.level <= threshold_level then
gradient_ratio = (point.level - 1) / (threshold_level - 1)
color =
string.format(
"#%02x%02x%02x",
math.floor(
tonumber(column_color_start:sub(2, 3), 16) +
gradient_ratio *
(tonumber(column_color_end:sub(2, 3), 16) - tonumber(column_color_start:sub(2, 3), 16))
),
math.floor(
tonumber(column_color_start:sub(4, 5), 16) +
gradient_ratio *
(tonumber(column_color_end:sub(4, 5), 16) - tonumber(column_color_start:sub(4, 5), 16))
),
math.floor(
tonumber(column_color_start:sub(6, 7), 16) +
gradient_ratio *
(tonumber(column_color_end:sub(6, 7), 16) - tonumber(column_color_start:sub(6, 7), 16))
)
)
else
gradient_ratio = (point.level - threshold_level) / (max_levels - threshold_level)
color =
string.format(
"#%02x%02x%02x",
math.floor(
tonumber(column_color_end:sub(2, 3), 16) +
gradient_ratio *
(tonumber(column_color_start:sub(2, 3), 16) - tonumber(column_color_end:sub(2, 3), 16))
),
math.floor(
tonumber(column_color_end:sub(4, 5), 16) +
gradient_ratio *
(tonumber(column_color_start:sub(4, 5), 16) - tonumber(column_color_end:sub(4, 5), 16))
),
math.floor(
tonumber(column_color_end:sub(6, 7), 16) +
gradient_ratio *
(tonumber(column_color_start:sub(6, 7), 16) - tonumber(column_color_end:sub(6, 7), 16))
)
)
end
local column =
mw.html.create("div"):css(
{
flex = "1",
height = height .. "px",
["background-color"] = color,
position = "relative"
}
):attr("title", string.format("Level: %d\nBonus: %s", point.level, round_format(round(point.bonus, 3))))
local bonus_value = round(point.bonus, 3)
local height_DN = 60
local padding_top = 5
local padding_bottom = 5
local top_value, bottom_value
if height >= height_DN + padding_top then
top_value = padding_top
else
bottom_value = padding_bottom
end
column:tag("span"):css(
{
position = "absolute",
top = top_value and string.format("%dpx", top_value) or nil,
bottom = bottom_value and string.format("%dpx", bottom_value) or nil,
["writing-mode"] = "sideways-lr",
color = "#fff",
["text-shadow"] = "-.065em 0 #000, 0 .065em #000, .065em 0 #000, 0 -.065em #000",
left = "50%",
["transform"] = "translateX(-50%)"
}
):wikitext(round_format(bonus_value))
column:tag("span"):css(
{
position = "relative",
bottom = "-100%",
["font-size"] = "10px"
}
):wikitext(point.level)
root:node(column)
end
container:node(root)
local x_axis =
mw.html.create("div"):css(
{
position = "absolute",
top = "95%",
width = graph_width + 8 .. "px"
}
):wikitext("Level")
container:node(x_axis)
return tostring(container)
end
function BubbleGraph.render(frame)
local args = frame.args or frame:getParent().args
local x1 = tonumber(args.x1)
local x2 = tonumber(args.x2)
local func = args.func
local bubble_color = args.bubble_color or args.color
local bubble_name = args.bubble_name or args.name or ''
local bubble_number = args.bubble_number or args.number or ''
return BubbleGraph.generate_graph(x1, x2, func, bubble_color, bubble_name, bubble_number)
end
return BubbleGraph