Module:Slottable

From Idlescape Wiki
Jump to navigation Jump to search

local p = {}
local itemsData = mw.loadData("Module:Items/data")
local enchantmentsData = mw.loadData("Module:Enchantment/data")
local img = require("Module:Img")
local affinities = {"Melee", "Magic", "Range", "Piercing", "Blunt", "Slashing", "Fire", "Ice", "Nature", "Chaos", "Posion", "Lightning"}
local weaponBonuses = {"strength", "intellect", "dexterity"}
local armorBonuses = {"protection", "resistance", "agility", "stamina"}
local affinitiesIcon = {
	"[[File:Melee splash.png|20px|link=Combat#Affinities]]",
	"[[File:Magic splash.png|20px|link=Combat#Affinities]]",
	"[[File:Range splash.png|20px|link=Combat#Affinities]]",
	"[[File:Stab splash.png|20px|link=Combat#Affinities]]",
	"[[File:Crush splash.png|20px|link=Combat#Affinities]]",
	"[[File:Slash splash.png|20px|link=Combat#Affinities]]",
	"[[File:Fire_splash.png|20px|link=Combat#Affinities]]",
	"[[File:Ice_splash.png|20px|link=Combat#Affinities]]",
	"[[File:Nature_splash.png|20px|link=Combat#Affinities]]",
	"[[File:Chaos_splash.png|20px|link=Combat#Affinities]]",
	"[[File:Poison_splash.png|20px|link=Combat#Affinities]]",
	"[[File:Lightning_splash.png|20px|link=Combat#Affinities]]",
}
local weaponBonusesIcon = {"Str.", "Int.", "Dex."}
local armorBonusesIcon = {"Prot.", "Res.", "Agi.", "Sta."}
local skills = {
    "mining",
    "foraging",
    "fishing",
    "farming",
    "enchanting",
    "runecrafting",
    "smithing",
    "cooking",
    "crafting",
    "constitution",
    "attack",
    "defense",
    "strength",
    "magic",
    "range",
}

local function pairsByKeys(t, f)
    local a = {}
    local orgi_key_type
    local orgi_key_numbered
    for n in pairs(t) do
        if tonumber(n) == nil then
            table.insert(a, n)
            orgi_key_type = "word"
        elseif type(n) == "number" then
            table.insert(a, n)
            orgi_key_type = "int"
        elseif type(n) == "string" and type(tonumber(n) == "number") then
            orgi_key_type = "number"
            table.insert(a, tonumber(n))
        end
    end
    table.sort(a, f)
    local key
    local value
    local i = 0             -- iterator variable
    local iter = function() -- iterator function
        i = i + 1
        if a[i] == nil then
            return nil
        elseif orgi_key_type == "word" or orgi_key_type == "int" then
            key = a[i]
            value = t[a[i]]
        elseif orgi_key_type == "number" then
            key = tostring(a[i])
            value = t[tostring(a[i])]
        end
        return key, value
    end
    return iter
end

local function shallowcopy(orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in pairs(orig) do
            copy[orig_key] = orig_value
        end
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end

local function tchelper(first, rest)
    return first:upper()..rest:lower()
end

local function capitalize(s)
	s = s:gsub("(%a)([%w_']*)", tchelper):gsub(" Of "," of "):gsub(" The "," the "):gsub("Ii","II")
	return s
end

local function tablelength(T)
    local count = 0
    for _ in pairs(T) do count = count + 1 end
    return count
end
---commentedText
---@param itemData table
---@return integer
local function getItemHigherRequiredlevel(itemData)
    local highestRequiredLevel = 0
    if itemData.requiredLevel then
        -- Highest level of the required skills
        for _, level in pairs(itemData.requiredLevel) do
            if level > highestRequiredLevel then
                highestRequiredLevel = level
            end
        end
    end
    return highestRequiredLevel
end

local function tierCheck(d1, d2)
    if d1.tier > d2.tier then
        return false
    elseif d1.tier < d2.tier then
        return true
    elseif d1.tier == d2.tier then
        if getItemHigherRequiredlevel(d1) > getItemHigherRequiredlevel(d2) then
            return false
        elseif getItemHigherRequiredlevel(d1) < getItemHigherRequiredlevel(d2) then
            return true
        elseif getItemHigherRequiredlevel(d1) == getItemHigherRequiredlevel(d2) then
            if d1.id > d2.id then
                return false
            elseif d1.id < d2.id then
                return true
            end
        end
    end
end

---mw.html th table
---@param s string
---@param cs string|number?
---@param rs string|number?
local function th(s, cs, rs)
    local c = mw.html.create('th')
    :attr('colspan', cs)
    :attr('rowspan', rs)
    :wikitext(s)
    :done()
    return c
end

---mw.html td table
---@param s string
---@param bColor string|number?
local function td(s, bColor)
    local c = mw.html.create('td')
    :css('background-color ', bColor)
    :wikitext(s)
    :done()
    return c
end
---commentedText
---@param id number|string
---@return table|string
local function getEnchantment(id)
    local e = enchantmentsData[tostring(id)]
    if e then
        return e
    else
        return "update Module:Enchantments/data"
    end
end

local function getItemTier(itemData)
    local highestRequiredLevel = getItemHigherRequiredlevel(itemData)
    local hRL
    if highestRequiredLevel == 0 then
        hRL = nil
    else
        hRL= tonumber(string.format("%.1f", highestRequiredLevel / 10))
    end
    local itemTier = itemData.overrideItemTier or hRL or itemData.enchantmentTier or 1
    return itemTier
end

local function getSlotItems(slot)
    local t = {}
    for id, data in pairsByKeys(itemsData) do
        if data.equipmentStats and data.equipmentStats.slot == slot then
            table.insert(t,{
                id = data.id,
                name = data.name,
                enchantmentTier = data.enchantmentTier,
                overrideItemTier = data.overrideItemTier,
                tier = getItemTier(data),
                itemImage = data.itemImage,
                itemIcon = data.itemIcon or nil,
                requiredLevel = data.requiredLevel,
                equipmentStats = {
                    offensiveAccuracyAffinityRating = shallowcopy(data.equipmentStats.offensiveAccuracyAffinityRating),
                    offensiveDamageAffinity = shallowcopy(data.equipmentStats.offensiveDamageAffinity),
                    weaponBonus = shallowcopy(data.equipmentStats.weaponBonus),
                    armorBonus = shallowcopy(data.equipmentStats.armorBonus),
                    defensiveDamageAffinity = shallowcopy(data.equipmentStats.defensiveDamageAffinity),
                    itemSet = shallowcopy(data.equipmentStats.itemSet)
                },
                tags = data.tags,
            })
        end
    end
    return t
end

local function getStyleItems(items, style)
    local t = {}
    for id, data in ipairs(items) do
        local _melee = false
        local _range = false
        local _magic = false
        local _gathering = false
        local _processing = false
        local _cosmetic = false
        for _, value in ipairs(data.tags) do
            if value == "melee" then
                _melee = true
            elseif value == "range" then
                _range = true
            elseif value == "magic" then
                _magic = true
            elseif value == "mining" then
                _gathering = true
            elseif value == "foraging" then
                _gathering = true
            elseif value == "fishing" then
                _gathering = true
            elseif value == "smithing" then
                _processing = true
            elseif value == "runecrafting" then
                _processing = true
            elseif value == "cooking" then
                _processing = true
            elseif value == "cosmetic" then
                _cosmetic =true
            end
        end
        if style == "melee" and _melee and not _cosmetic then
            table.insert(t, data)
        elseif style == "range" and _range and not _cosmetic then
            table.insert(t, data)
        elseif style == "magic" and _magic and not _cosmetic then
            table.insert(t, data)
        elseif style == "gathering" and _gathering and not _cosmetic then
            table.insert(t, data)
        elseif style == "processing" and _processing and not _cosmetic then
            table.insert(t, data)
        end
    end
    return t
end

local function getNeededColums(items)
    local offStat = {}
    local offAff = {}
    local acc = {}
    local defStat = {}
    local defAff = {}
    local skill= {}
    local setBonus = {}
    for _, data in pairsByKeys(items) do
        for i, v in ipairs(weaponBonuses) do
            if data.equipmentStats.weaponBonus and data.equipmentStats.weaponBonus[v] then
                if not offStat[i] then
                    offStat[i] = v
                end
            end
        end
        for i, v in ipairs(affinities) do
            if data.equipmentStats.offensiveDamageAffinity and data.equipmentStats.offensiveDamageAffinity[v] then
                if not offAff[i] then
                    offAff[i] = v
                end
            end
        end
        for i, v in ipairs(affinities) do
            if data.equipmentStats.offensiveAccuracyAffinityRating and data.equipmentStats.offensiveAccuracyAffinityRating[v] then
                if not acc[i] then
                    acc[i] = v
                end
            end
        end
        for i, v in ipairs(armorBonuses) do
            if data.equipmentStats.armorBonus and data.equipmentStats.armorBonus[v] and data.equipmentStats.armorBonus[v] ~= 0 then
                if not defStat[i] then
                    defStat[i] = v
                end
            end
        end
        for i, v in ipairs(affinities) do
            if data.equipmentStats.defensiveDamageAffinity and data.equipmentStats.defensiveDamageAffinity[v] and data.equipmentStats.defensiveDamageAffinity[v] ~= 1 then
                if not defAff[i] then
                    defAff[i] = v
                end
            end
        end
        for i, v in ipairs(skills) do
            if data.requiredLevel and data.requiredLevel[v] then
                if not skill[i] then
                    skill[i] = v
                end
            end
        end
        if data.equipmentStats.itemSet then
            setBonus[1] = 1
        end
    end
    local t = {offStat = offStat, offAff = offAff, acc = acc, defStat = defStat, defAff = defAff, skill =skill, setBonus = setBonus}
    for key, value in pairs(t) do
        if tablelength(value) == 0 then
            t[key] = nil
        end
    end
    return t
end

local function fullUrl(url)
	local newUrl = url
	if url:sub(1,5) == "https" then
		return newUrl
	end
	if url:sub(1,1) ~= "/" then
		newUrl = "/" .. newUrl
	end
	newUrl = "https://www.play.idlescape.com" .. newUrl
	return newUrl
end

local function genImgCell(data)
    local url = ""
    if data.itemIcon then
        url = data.itemIcon
    elseif data.itemImage then
        url = data.itemImage
    end
    local c = mw.html.create('td')
    :css('text-align', 'center')
    :tag('img')
    :attr('src', fullUrl(url))
    :attr('alt', capitalize(data.name))
    :attr('width', "40px")
    :done()
    return c
end

local function genNameCell(name)
    local c = mw.html.create('td')
    :css('text-align', 'left')
        :wikitext("[[" .. capitalize(name) .. "]]")
    :done()
    return c
end

local function genTierCell(data)
    local c = mw.html.create('td')
    :wikitext(tostring(data.tier))
    :done()
    return c
end

local function genRecLvlCells(tr, data, skill)
    for _, v in pairs(skill) do
        local s = ""
        if data.requiredLevel and data.requiredLevel[v] then
            s = tostring(data.requiredLevel[v])
        else
            s = "0"
        end
        tr:node(td(s))
    end
    return tr
end

local function genWeaponBonusCells(tr, data, offStat)
    for _, v in pairsByKeys(offStat) do
        local s = ""
        local bc = ""
        if data.equipmentStats.weaponBonus and data.equipmentStats.weaponBonus[v] then
            if data.equipmentStats.weaponBonus[v] > 0 then
                s = "+" .. tostring(data.equipmentStats.weaponBonus[v])
                bc = "#2e5e05"
            elseif data.equipmentStats.weaponBonus[v] < 0 then
                s = tostring(data.equipmentStats.weaponBonus[v])
                bc = "#801c13"
            else
                s = "+" .. tostring(data.equipmentStats.weaponBonus[v])
            end
        else
            s = "0"
        end
        tr:node(td(s, bc))
    end
    return tr
end

local function genOffensiveDamageAffinityCells(tr, data, offAff)
    for _, v in pairsByKeys(offAff) do
        local s = ""
        local bc = ""
        if data.equipmentStats.offensiveDamageAffinity and data.equipmentStats.offensiveDamageAffinity[v] then
            if data.equipmentStats.offensiveDamageAffinity[v] > 1 then
                s = "+" .. tostring((data.equipmentStats.offensiveDamageAffinity[v] -1) * 10) .. "%"
                bc = "#2e5e05"
            elseif data.equipmentStats.offensiveDamageAffinity[v] < 1 then
                s = "-" .. tostring((1 - data.equipmentStats.offensiveDamageAffinity[v]) * 100) .. "%"
                bc = "#801c13"
            else
                s = "+0"
            end
        else
            s = "+0"
        end
        tr:node(td(s, bc))
    end
    return tr
end

local function genAccuracyOffensiveDamageAffinityCells(tr, data, acc)
    for _, v in pairsByKeys(acc) do
        local s = ""
        local bc = ""
        if data.equipmentStats.offensiveAccuracyAffinityRating and data.equipmentStats.offensiveAccuracyAffinityRating[v] then
            if data.equipmentStats.offensiveAccuracyAffinityRating[v] > 1 then
                s = "+" .. tostring(data.equipmentStats.offensiveAccuracyAffinityRating[v])
                bc = "#2e5e05"
            elseif data.equipmentStats.offensiveAccuracyAffinityRating[v] < 1 then
                s = tostring(data.equipmentStats.offensiveAccuracyAffinityRating[v])
                bc = "#801c13"
            else
                s = "+0"
            end
        else
            s = "+0"
        end
        tr:node(td(s, bc))
    end
    return tr
end

local function genArmorBonusCells(tr, data, defStat)
    for _, v in pairsByKeys(defStat) do
        local s = ""
        local bc = ""
        if data.equipmentStats.armorBonus and data.equipmentStats.armorBonus[v] then
            if data.equipmentStats.armorBonus[v] > 0 then
                s = "+" .. tostring(data.equipmentStats.armorBonus[v])
                bc = "#2e5e05"
            elseif data.equipmentStats.armorBonus[v] < 0 then
                s = tostring(data.equipmentStats.armorBonus[v])
                bc = "#801c13"
            else
                s = "+" .. tostring(data.equipmentStats.armorBonus[v])
            end
        else
            s = "0"
        end
        tr:node(td(s, bc))
    end
    return tr
end

local function genDefensiveDamageAffinityCells(tr, data, defAff)
    for _, v in pairsByKeys(defAff) do
        local s = ""
        local bc = ""
        if data.equipmentStats.defensiveDamageAffinity and data.equipmentStats.defensiveDamageAffinity[v] then
            if data.equipmentStats.defensiveDamageAffinity[v] > 1 then
                s = "+" .. tostring((data.equipmentStats.defensiveDamageAffinity[v] -1) * 10) .. "%"
                bc = "#2e5e05"
            elseif data.equipmentStats.defensiveDamageAffinity[v] < 1 then
                s = "-" .. tostring((1 - data.equipmentStats.defensiveDamageAffinity[v]) * 100) .. "%"
                bc = "#801c13"
            else
                s = "+0"
            end
        else
            s = "+0"
        end
        tr:node(td(s, bc))
    end
    return tr
end

local function genSetBonusCell(data)
    local setBonus = data.equipmentStats.itemSet
    local s = ""
    if setBonus then
        for i, v in ipairs(setBonus) do
            local e = getEnchantment(v)
            s = s .. "[[" .. e.name .. "]]"
            if i < tablelength(setBonus) then
                s = s ..", "
            end
        end
    end
    local c = mw.html.create('td')
        :css('text-align', 'center')
        :wikitext(s)
        :done()
    return c
end

local function genTHead(t, cols)
    local tr = t:tag('tr')
        :node(th("", 2))
        :node(th(""))
    if cols.skill then
        tr:node(th("Req. lvl.", tablelength(cols.skill)))
    end
    if cols.offStat then
        if tablelength(cols.offStat) <= 2 then
            tr:node(th("Off. Sta.", tablelength(cols.offStat)))
        else
            tr:node(th("Offensive Stats", tablelength(cols.offStat)))
        end
    end
    if cols.offAff then
        if tablelength(cols.offAff) <= 2 then
            tr:node(th("Off. Aff.", tablelength(cols.offAff)))
        else
            tr:node(th("Offensive Affinities", tablelength(cols.offAff)))
        end
    end
    if cols.acc then
        if tablelength(cols.acc) <= 2 then
            tr:node(th("Acc.", tablelength(cols.acc)))
        else
            tr:node(th("Accuracies", tablelength(cols.acc)))
        end
    end
    if cols.defStat then
        if tablelength(cols.defStat) <= 2 then
            tr:node(th("Def. Sta.", tablelength(cols.defStat)))
        else
            tr:node(th("Defensive Stats", tablelength(cols.defStat)))
        end
    end
    if cols.defAff then
        if tablelength(cols.defAff) <= 2 then
            tr:node(th("Def. Aff.", tablelength(cols.defAff)))
        else
            tr:node(th("Defensive Affinities", tablelength(cols.defAff)))
        end
    end
    if cols.setBonus then
        tr:node(th("Set Bonus", 1, 2))
    end
    tr:done()
    local tr2 = t:tag('tr')
    :node(th("Image"))
    :node(th("Name"))
    :node(th("Tier"))
    if cols.skill then
        for i in pairsByKeys(cols.skill) do
            tr2:node(th(img._img({skills[i], 20})))
        end
    end
    if cols.offStat then
        for i in pairsByKeys(cols.offStat) do
            tr2:node(th(weaponBonusesIcon[i]))
        end
    end
    if cols.offAff then
        for i in pairsByKeys(cols.offAff) do
            tr2:node(th(affinitiesIcon[i]))
        end
    end
    if cols.acc then
        for i in pairsByKeys(cols.acc) do
            tr2:node(th(affinitiesIcon[i]))
        end
    end
    if cols.defStat then
        for i in pairsByKeys(cols.defStat) do
            tr2:node(th(armorBonusesIcon[i]))
        end
    end
    if cols.defAff then
        for i in pairsByKeys(cols.defAff) do
            tr2:node(th(affinitiesIcon[i]))
        end
    end
    tr2:done()
    return t
end

local function genTBody(t, items, cols)
    for id, data in pairsByKeys(items) do
        local tr = t:tag('tr')
        :node(genImgCell(data))
        :node(genNameCell(data.name))
        :node(genTierCell(data))
        if cols.skill then
            tr = genRecLvlCells(tr, data, cols.skill)
        end
        if cols.offStat then
            tr = genWeaponBonusCells(tr, data, cols.offStat)
        end
        if cols.offAff then
            tr = genOffensiveDamageAffinityCells(tr, data, cols.offAff)
        end
        if cols.acc then
            tr = genAccuracyOffensiveDamageAffinityCells(tr, data, cols.acc)
        end
        if cols.defStat then
            tr = genArmorBonusCells(tr, data, cols.defStat)
        end
        if cols.defAff then
            tr = genDefensiveDamageAffinityCells(tr, data, cols.defAff)
        end
        if cols.setBonus then
            tr:node(genSetBonusCell(data))
        end
        tr:done()
        end
    return t
end

local function genTable(items)
    local cols = getNeededColums(items)
    local t = mw.html.create('table')
    :addClass("wikitable sortable jquery-tablesorter")
    :css('text-align', 'right')
    t = genTHead(t, cols)
    t = genTBody(t, items, cols)
    return t
end

function p.slotTable(frame)
    return p._slotTable(frame:getParent().args)
end

function p._slotTable(_args)
    local slot = _args[1]
    local style = _args[2]
    local items = getStyleItems(getSlotItems(slot), style)
    table.sort(items, tierCheck)
    local t = genTable(items)
    return t
end
return p