Module:BubbleGraph: Difference between revisions

From IdleOn MMO Wiki
mNo edit summary
mNo edit summary
 
(37 intermediate revisions by the same user not shown)
Line 4: Line 4:
     local mult = 10 ^ (numDecimalPlaces or 0)
     local mult = 10 ^ (numDecimalPlaces or 0)
     return math.floor(num * mult + 0.5) / mult
     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
end


Line 43: Line 48:
     local max_levels = 1000
     local max_levels = 1000
     local step = 20
     local step = 20
     local graph_width = 1200
     local graph_width = 1330.8
     local graph_height = 250
     local graph_height = 250
     local points = {}
     local points = {}
    local threshold_level = x2 * 90 / (100 - 90)
     local max_bonus = 0
     local max_bonus = 0
     for level = 1, max_levels do
     for level = 1, max_levels do
         if level == 1 or level % step == 0 then
         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)
             local bonus = BubbleGraph.calculate_bonus(func, level, x1, x2)
             table.insert(points, {level = level, bonus = bonus})
             table.insert(points, {level = level, bonus = bonus})
Line 57: Line 67:
     end
     end


     local column_color_start, column_color_end, background_color_start, background_color_end, bubble_image_name
     local column_color_start, column_color_end, background_color_start, background_color_end, bubble_image_name, displayVal


     if bubble_color == "orange" then
     if bubble_color == "orange" then
Line 88: Line 98:
         mw.html.create("div"):css(
         mw.html.create("div"):css(
         {
         {
             width = graph_width + 50 .. "px",
             width = graph_width .. "px",
             height = graph_height + 100 .. "px",
             height = graph_height + 100 .. "px",
             position = "relative",
             position = "relative",
Line 96: Line 106:
         }
         }
     )
     )
 
   
    if bubble_name and bubble_number == '' then
    displayVal = "none"
    else
    displayVal = "initial"
    end
   
     local bubble_header =
     local bubble_header =
         mw.html.create("div"):css(
         mw.html.create("div"):css(
Line 103: Line 119:
             top = "-10px",
             top = "-10px",
             left = "50%",
             left = "50%",
             width = "100%",
             width = graph_width + 8 .. "px",
             ["font-size"] = "35px",
             ["font-size"] = "35px",
             ["transform"] = "translateX(-50%)"
             ["transform"] = "translateX(-50%)",
            ["display"] = displayVal
         }
         }
     ):wikitext(string.format("[[File:%s.png|link=]]%s", bubble_image_name, bubble_name))
     ):wikitext(string.format("[[File:%s.png|link=]]%s", bubble_image_name, bubble_name))
Line 116: Line 133:
             position = "absolute",
             position = "absolute",
             top = "20%",
             top = "20%",
             left = "30px",
             left = "-2%",
            width = "50px",
             height = graph_height .. "px",
             height = graph_height .. "px",
             ["writing-mode"] = "sideways-lr"
             ["writing-mode"] = "sideways-lr"
Line 132: Line 148:
             ["align-items"] = "flex-end",
             ["align-items"] = "flex-end",
             position = "absolute",
             position = "absolute",
            left = "50px",
             top = "60px",
             top = "60px",
             ["background-image"] = string.format(
             ["background-image"] = string.format(
Line 144: Line 159:
         }
         }
     )
     )
   
    local threshold_bonus = 0.9 * max_bonus


     for _, point in ipairs(points) do
     for _, point in ipairs(points) do
Line 151: Line 164:


         if func == "decay" or func == "decayMulti" then
         if func == "decay" or func == "decayMulti" then
             if point.bonus >= threshold_bonus then
             if point.level >= threshold_level then
                 local reduction_factor = math.exp(-((point.bonus - threshold_bonus) / (max_bonus - threshold_bonus)))
                 local reduction_factor = math.exp(-((point.level - threshold_level) / 100))
                 height = height * reduction_factor
                 height = height * reduction_factor
             end
             end
         end
         end


         local gradient_ratio = (point.level - 1) / (max_levels - 1)
         local gradient_ratio
        local color =
        local color
            string.format(
 
            "#%02x%02x%02x",
        if point.level <= threshold_level then
            math.floor(
            gradient_ratio = (point.level - 1) / (threshold_level - 1)
                tonumber(column_color_start:sub(2, 3), 16) +
            color =
                    gradient_ratio *
                string.format(
                        (tonumber(column_color_end:sub(2, 3), 16) - tonumber(column_color_start:sub(2, 3), 16))
                "#%02x%02x%02x",
            ),
                math.floor(
            math.floor(
                    tonumber(column_color_start:sub(2, 3), 16) +
                tonumber(column_color_start:sub(4, 5), 16) +
                        gradient_ratio *
                    gradient_ratio *
                            (tonumber(column_color_end:sub(2, 3), 16) - tonumber(column_color_start:sub(2, 3), 16))
                        (tonumber(column_color_end:sub(4, 5), 16) - tonumber(column_color_start:sub(4, 5), 16))
                ),
            ),
                math.floor(
            math.floor(
                    tonumber(column_color_start:sub(4, 5), 16) +
                tonumber(column_color_start:sub(6, 7), 16) +
                        gradient_ratio *
                     gradient_ratio *
                            (tonumber(column_color_end:sub(4, 5), 16) - tonumber(column_color_start:sub(4, 5), 16))
                         (tonumber(column_color_end:sub(6, 7), 16) - tonumber(column_color_start:sub(6, 7), 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 =
         local column =
Line 186: Line 225:
                 position = "relative"
                 position = "relative"
             }
             }
         )
         ):attr("title", string.format("Level: %d\nBonus: %s", point.level, round_format(round(point.bonus, 3))))
        :addClass("chart-column")


         local bonus_value = string.format("%.3f", point.bonus)
         local bonus_value = round(point.bonus, 3)
         local height_DN = 65
         local height_DN = 60
         local padding_top = 5
         local padding_top = 5
         local padding_bottom = 5
         local padding_bottom = 5
Line 211: Line 251:
                 ["transform"] = "translateX(-50%)"
                 ["transform"] = "translateX(-50%)"
             }
             }
         ):wikitext(bonus_value)
         ):wikitext(round_format(bonus_value))


         column:tag("span"):css(
         column:tag("span"):css(
Line 231: Line 271:
             position = "absolute",
             position = "absolute",
             top = "95%",
             top = "95%",
            left = "50px",
             width = graph_width + 8 .. "px"
             width = graph_width .. "px"
         }
         }
     ):wikitext("Level")
     ):wikitext("Level")
     container:node(x_axis)
     container:node(x_axis)
     return tostring(container)
     return tostring(container)
end
end
Line 246: Line 285:
     local func = args.func
     local func = args.func
     local bubble_color = args.bubble_color or args.color
     local bubble_color = args.bubble_color or args.color
     local bubble_name = args.bubble_name or args.name
     local bubble_name = args.bubble_name or args.name or ''
     local bubble_number = args.bubble_number or args.number
     local bubble_number = args.bubble_number or args.number or ''
    local max_levels = tonumber(args.max_levels) or 1000
local step = tonumber(args.step) or 20


     return BubbleGraph.generate_graph(x1, x2, func, bubble_color, bubble_name, bubble_number)
     return BubbleGraph.generate_graph(x1, x2, func, bubble_color, bubble_name, bubble_number)

Latest revision as of 05:24, 12 December 2024

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 == '' 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))))
        :addClass("chart-column")

        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 ''
    local max_levels = tonumber(args.max_levels) or 1000
	local step = tonumber(args.step) or 20

    return BubbleGraph.generate_graph(x1, x2, func, bubble_color, bubble_name, bubble_number)
end

return BubbleGraph