Difference between revisions of "Module:Monsters stats table"

From Idlescape Wiki
Jump to navigation Jump to search
(Used img module instead of img template)
(Fix crit reduction value and spelling of affinities)
 
(13 intermediate revisions by the same user not shown)
Line 2: Line 2:
 
local findId = require("Module:FindId")
 
local findId = require("Module:FindId")
 
local img = require("Module:Img")
 
local img = require("Module:Img")
 +
local dataModuleNames = {
 +
    normal = "Module:Monsters stats normal/data",
 +
    dungeon = "Module:Monsters stats dungeon/data",
 +
}
  
local monsters_stats = mw.loadData("Module:Monsters stats/data")
 
 
local defaultAffinities = {
 
local defaultAffinities = {
     {Melee = 1},
+
     { Melee = 1 },
     {Magic = 1},
+
     { Magic = 1 },
     {Range = 1},
+
     { Range = 1 },
     {Piercing = 1},
+
     { Piercing = 1 },
     {Blunt = 1},
+
     { Blunt = 1 },
     {Slashing = 1},
+
     { Slashing = 1 },
     {Fire = 1},
+
     { Fire = 1 },
     {Ice = 1},
+
     { Ice = 1 },
     {Nature = 1},
+
     { Nature = 1 },
     {Chaos = 1},
+
     { Chaos = 1 },
     {Posion = 1},
+
     { Posion = 1 },
     {Lightning = 1}
+
     { Lightning = 1 }
 
}
 
}
 +
 +
local loadedDataModules = {}
 +
 +
--
 +
-- Loads data modules
 +
--
 +
-- @param dataType {string}
 +
-- @return {data table}
 +
--
 +
function p.loadData(dataType)
 +
    local moduleName = dataModuleNames[dataType]
 +
    if loadedDataModules[moduleName] == nil then
 +
        loadedDataModules[moduleName] = mw.loadData(moduleName)
 +
    end
 +
    return loadedDataModules[moduleName]
 +
end
  
 
-- Convert from CSV string to table (converts a single line of a CSV file)
 
-- Convert from CSV string to table (converts a single line of a CSV file)
Line 92: Line 111:
 
     local armorThreatFinal = (targetArmorRating + monster.defense * 10) * armorThreat
 
     local armorThreatFinal = (targetArmorRating + monster.defense * 10) * armorThreat
 
     local baseThreat = (potentialDamageThreatFinal + weaponThreatFinal) * attackSpeedThreatFinal + armorThreatFinal
 
     local baseThreat = (potentialDamageThreatFinal + weaponThreatFinal) * attackSpeedThreatFinal + armorThreatFinal
     return baseThreat
+
     return math.floor(baseThreat)
 
end
 
end
  
local function genDefensiveTable(monsters)
+
local function genDefensiveTable(monsters, collapsed)
     local defensiveAffinties = {}
+
     local defensiveAffinities = {}
     local s = "{|class=\"wikitable sortable hover-highlight\"\n"
+
     local s = "{|class=\"wikitable sortable mw-collapsible " .. collapsed .. "\" style=\"text-align:center;width:100%\"\n"
 +
    s = s .. "|+ style=\"white-space:nowrap\" |Defensive Affinities\n"
 
     s = s .. "!rowspan=2 colspan=2|Monster\n"
 
     s = s .. "!rowspan=2 colspan=2|Monster\n"
 
     s = s .. "!rowspan=2|Base<br/>Threat\n"
 
     s = s .. "!rowspan=2|Base<br/>Threat\n"
Line 121: Line 141:
 
             for affinity, affinityValue in pairs(value) do
 
             for affinity, affinityValue in pairs(value) do
 
                 if monster.defensiveDamageAffinity[affinity] then
 
                 if monster.defensiveDamageAffinity[affinity] then
                     defensiveAffinties[index] = {affinity = monster.defensiveDamageAffinity[affinity]}
+
                     defensiveAffinities[index] = { affinity = monster.defensiveDamageAffinity[affinity] }
 
                 else
 
                 else
                     defensiveAffinties[index] = {affinity = affinityValue}
+
                     defensiveAffinities[index] = { affinity = affinityValue }
 
                 end
 
                 end
 
             end
 
             end
 
         end
 
         end
         s = s .. img._img({monster["name"],42}) .. "\n"
+
         s = s .. "|" .. img._img({ monster["name"], 60 }) .. "\n"
         s = s .. "|[[" .. monster["name"] .. "]]\n"
+
         s = s .. "|style=\"text-align:left\"|[[" .. monster["name"] .. "]]\n"
 
         s = s .. "| " .. calcThreat(monster) .. "\n"
 
         s = s .. "| " .. calcThreat(monster) .. "\n"
 
         s = s .. "|" .. tostring(monster.defensiveCritical.chance * 100) .. "%\n"
 
         s = s .. "|" .. tostring(monster.defensiveCritical.chance * 100) .. "%\n"
         s = s .. "|" .. tostring(monster.defensiveCritical.damageMultiplier * 100) .. "%\n"
+
         s = s .. "|" .. tostring(100 - (monster.defensiveCritical.damageMultiplier * 100)) .. "%\n"
         for _, value in pairsByKeys(defensiveAffinties) do
+
         for _, value in pairsByKeys(defensiveAffinities) do
 +
            for _, affinityValue in pairs(value) do
 +
                if affinityValue > 1 then
 +
                    s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 +
                elseif affinityValue == 1 then
 +
                    s = s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
 +
                else
 +
                    s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 +
                end
 +
            end
 +
        end
 +
        s = s .. "|-\n"
 +
    end
 +
    s = s .. "|}"
 +
    return s
 +
end
 +
 
 +
local function genOffensiveTable(monsters, collapsed)
 +
    local offensiveAffinities = {}
 +
    local s = "{|class=\"wikitable sortable mw-collapsible " .. collapsed .. "\" style=\"text-align:center;width:100%\"\n"
 +
    s = s .. "|+ style=\"white-space:nowrap\" |Offensive Affinities\n"
 +
    s = s .. "!rowspan=2 colspan=2|Monster\n"
 +
    s = s .. "!rowspan=2|Crit<br/>Chance\n"
 +
    s = s .. "!rowspan=2|Crit<br/>Multiplier\n"
 +
    s = s .. "!colspan=12 style=\"text-align:center\"|Affinities\n"
 +
    s = s .. "|-\n"
 +
    s = s .. "![[File:Melee splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Magic splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Range splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Stab splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Crush splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Slash splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Fire_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Ice_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Nature_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Chaos_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Poison_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Lightning_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "|-\n"
 +
    for _, monster in ipairs(monsters) do
 +
        for index, value in pairsByKeys(defaultAffinities) do
 
             for affinity, affinityValue in pairs(value) do
 
             for affinity, affinityValue in pairs(value) do
 +
                if monster.offensiveDamageAffinity[affinity] then
 +
                    offensiveAffinities[index] = { affinity = monster.offensiveDamageAffinity[affinity] }
 +
                else
 +
                    offensiveAffinities[index] = { affinity = affinityValue }
 +
                end
 +
            end
 +
        end
 +
        s = s .. "|" .. img._img({ monster["name"], 60 }) .. "\n"
 +
        s = s .. "|style=\"text-align:left\"|[[" .. monster["name"] .. "]]\n"
 +
        s = s .. "|" .. tostring(monster.offensiveCritical.chance * 100) .. "%\n"
 +
        s = s .. "|" .. tostring(monster.offensiveCritical.damageMultiplier * 100) .. "%\n"
 +
        for _, value in pairsByKeys(offensiveAffinities) do
 +
            for _, affinityValue in pairs(value) do
 
                 if affinityValue > 1 then
 
                 if affinityValue > 1 then
 
                     s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 
                     s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 
                 elseif affinityValue == 1 then
 
                 elseif affinityValue == 1 then
                     s =s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
+
                     s = s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
 
                 else
 
                 else
 
                     s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 
                     s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
Line 149: Line 222:
 
end
 
end
  
local function genOffensiveTable(monsters)
+
local function genCombinedTable(monsters, collapsed)
     local offensiveAffinties = {}
+
     local offensiveAffinities = {}
     local s = "{|class=\"wikitable sortable hover-highlight\"\n"
+
    local defensiveAffinities = {}
 +
     local s = "{|class=\"wikitable sortable mw-collapsible " .. collapsed .. "\" style=\"text-align:center;width:100%\"\n"
 +
    s = s .. "|+ style=\"white-space:nowrap\" |Affinities\n"
 
     s = s .. "!rowspan=2 colspan=2|Monster\n"
 
     s = s .. "!rowspan=2 colspan=2|Monster\n"
 
     s = s .. "!rowspan=2|Crit<br/>Chance\n"
 
     s = s .. "!rowspan=2|Crit<br/>Chance\n"
 
     s = s .. "!rowspan=2|Crit<br/>Multiplier\n"
 
     s = s .. "!rowspan=2|Crit<br/>Multiplier\n"
 +
    s = s .. "!colspan=12 style=\"text-align:center\"|Affinities\n"
 +
    s = s .. "!rowspan=2|Base<br/>Threat\n"
 +
    s = s .. "!rowspan=2|Crit<br/>Avoidance\n"
 +
    s = s .. "!rowspan=2|Crit<br/>Reduction\n"
 
     s = s .. "!colspan=12 style=\"text-align:center\"|Affinities\n"
 
     s = s .. "!colspan=12 style=\"text-align:center\"|Affinities\n"
 
     s = s .. "|-\n"
 
     s = s .. "|-\n"
 +
    s = s .. "![[File:Melee splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Magic splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Range splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Stab splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Crush splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Slash splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Fire_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Ice_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Nature_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Chaos_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Poison_splash.png|20px|link=Combat#Affinities]]\n"
 +
    s = s .. "![[File:Lightning_splash.png|20px|link=Combat#Affinities]]\n"
 +
    -- sep
 
     s = s .. "![[File:Melee splash.png|20px|link=Combat#Affinities]]\n"
 
     s = s .. "![[File:Melee splash.png|20px|link=Combat#Affinities]]\n"
 
     s = s .. "![[File:Magic splash.png|20px|link=Combat#Affinities]]\n"
 
     s = s .. "![[File:Magic splash.png|20px|link=Combat#Affinities]]\n"
Line 174: Line 266:
 
             for affinity, affinityValue in pairs(value) do
 
             for affinity, affinityValue in pairs(value) do
 
                 if monster.offensiveDamageAffinity[affinity] then
 
                 if monster.offensiveDamageAffinity[affinity] then
                     offensiveAffinties[index] = {affinity = monster.offensiveDamageAffinity[affinity]}
+
                     offensiveAffinities[index] = { affinity = monster.offensiveDamageAffinity[affinity] }
 +
                else
 +
                    offensiveAffinities[index] = { affinity = affinityValue }
 +
                end
 +
                if monster.defensiveDamageAffinity[affinity] then
 +
                    defensiveAffinities[index] = { affinity = monster.defensiveDamageAffinity[affinity] }
 
                 else
 
                 else
                     offensiveAffinties[index] = {affinity = affinityValue}
+
                     defensiveAffinities[index] = { affinity = affinityValue }
 
                 end
 
                 end
 
             end
 
             end
 
         end
 
         end
         s = s .. img._img({monster["name"],42}) .. "\n"
+
         s = s .. "|" .. img._img({ monster["name"], 60 }) .. "\n"
         s = s .. "|[[" .. monster["name"] .. "]]\n"
+
         s = s .. "|style=\"text-align:left\"|[[" .. monster["name"] .. "]]\n"
 
         s = s .. "|" .. tostring(monster.offensiveCritical.chance * 100) .. "%\n"
 
         s = s .. "|" .. tostring(monster.offensiveCritical.chance * 100) .. "%\n"
 
         s = s .. "|" .. tostring(monster.offensiveCritical.damageMultiplier * 100) .. "%\n"
 
         s = s .. "|" .. tostring(monster.offensiveCritical.damageMultiplier * 100) .. "%\n"
         for _, value in pairsByKeys(offensiveAffinties) do
+
         for _, value in pairsByKeys(offensiveAffinities) do
             for affinity, affinityValue in pairs(value) do
+
             for _, affinityValue in pairs(value) do
 +
                if affinityValue > 1 then
 +
                    s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 +
                elseif affinityValue == 1 then
 +
                    s = s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
 +
                else
 +
                    s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 +
                end
 +
            end
 +
        end
 +
        s = s .. "| " .. calcThreat(monster) .. "\n"
 +
        s = s .. "|" .. tostring(monster.defensiveCritical.chance * 100) .. "%\n"
 +
        s = s .. "|" .. tostring(100 - (monster.defensiveCritical.damageMultiplier * 100)) .. "%\n"
 +
        for _, value in pairsByKeys(defensiveAffinities) do
 +
            for _, affinityValue in pairs(value) do
 
                 if affinityValue > 1 then
 
                 if affinityValue > 1 then
 
                     s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 
                     s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 
                 elseif affinityValue == 1 then
 
                 elseif affinityValue == 1 then
                     s =s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
+
                     s = s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
 
                 else
 
                 else
 
                     s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
 
                     s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
Line 209: Line 320:
 
     local monsters = {}
 
     local monsters = {}
 
     local style = _args[2]
 
     local style = _args[2]
 +
    local collapsed
 
     local s = ""
 
     local s = ""
 +
    if _args["collapsed"] then
 +
        collapsed = "mw-collapsed"
 +
    else
 +
        collapsed = ""
 +
    end
  
 
     for _, value in ipairs(monstersName) do
 
     for _, value in ipairs(monstersName) do
         table.insert(monsters, monsters_stats[tostring(findId._findId({ value, "monster" }))])
+
         table.insert(monsters, p.loadData("normal")[tostring(findId._findId({ value, "monster" }))])
 +
        table.insert(monsters, p.loadData("dungeon")[tostring(findId._findId({ value, "monster" }))])
 +
    end
 +
    if monsters == nil then
 +
        return "no monster match"
 
     end
 
     end
 
     if style == "defensive" then
 
     if style == "defensive" then
         return genDefensiveTable(monsters)
+
         return genDefensiveTable(monsters, collapsed)
 
     elseif style == "offensive" then
 
     elseif style == "offensive" then
         return genOffensiveTable(monsters)
+
         return genOffensiveTable(monsters, collapsed)
 +
    elseif style == "combined" then
 +
        return genCombinedTable(monsters, collapsed)
 
     else
 
     else
 
         return "wrong style"
 
         return "wrong style"

Latest revision as of 02:54, 5 December 2024


local p = {}
local findId = require("Module:FindId")
local img = require("Module:Img")
local dataModuleNames = {
    normal = "Module:Monsters stats normal/data",
    dungeon = "Module:Monsters stats dungeon/data",
}

local defaultAffinities = {
    { Melee = 1 },
    { Magic = 1 },
    { Range = 1 },
    { Piercing = 1 },
    { Blunt = 1 },
    { Slashing = 1 },
    { Fire = 1 },
    { Ice = 1 },
    { Nature = 1 },
    { Chaos = 1 },
    { Posion = 1 },
    { Lightning = 1 }
}

local loadedDataModules = {}

--
-- Loads data modules
--
-- @param dataType {string}
-- @return {data table}
--
function p.loadData(dataType)
    local moduleName = dataModuleNames[dataType]
    if loadedDataModules[moduleName] == nil then
        loadedDataModules[moduleName] = mw.loadData(moduleName)
    end
    return loadedDataModules[moduleName]
end

-- Convert from CSV string to table (converts a single line of a CSV file)
local function fromCSV(s)
    s = s .. ',' -- ending comma
    local t = {} -- table to collect fields
    local fieldstart = 1
    repeat
        local nexti = string.find(s, ',', fieldstart)
        table.insert(t, string.sub(s, fieldstart, nexti - 1))
        fieldstart = nexti + 1
    until fieldstart > string.len(s)
    return t
end

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 calcThreat(monster)
    local damageThreat = 2
    local weaponThreat = 3
    local armorThreat = 5
    local attackSpeedThreat = 3
    local attackSpeedThreatLevel = 2.4 / monster.attackSpeed
    local attackSpeedThreatFinal = attackSpeedThreatLevel * attackSpeedThreat
    local potentialDamageThreatFinal =
        (monster.attack +
            monster.strength +
            monster.magic +
            monster.range) *
        damageThreat
    local weaponThreatFinal =
        (monster.weapon.dexterity +
            monster.weapon.intellect +
            monster.weapon.strength) *
        weaponThreat
    local targetArmorRating =
        monster.armor.protection +
        monster.armor.resistance +
        monster.armor.agility * 1.5
    local armorThreatFinal = (targetArmorRating + monster.defense * 10) * armorThreat
    local baseThreat = (potentialDamageThreatFinal + weaponThreatFinal) * attackSpeedThreatFinal + armorThreatFinal
    return math.floor(baseThreat)
end

local function genDefensiveTable(monsters, collapsed)
    local defensiveAffinities = {}
    local s = "{|class=\"wikitable sortable mw-collapsible " .. collapsed .. "\" style=\"text-align:center;width:100%\"\n"
    s = s .. "|+ style=\"white-space:nowrap\" |Defensive Affinities\n"
    s = s .. "!rowspan=2 colspan=2|Monster\n"
    s = s .. "!rowspan=2|Base<br/>Threat\n"
    s = s .. "!rowspan=2|Crit<br/>Avoidance\n"
    s = s .. "!rowspan=2|Crit<br/>Reduction\n"
    s = s .. "!colspan=12 style=\"text-align:center\"|Affinities\n"
    s = s .. "|-\n"
    s = s .. "![[File:Melee splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Magic splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Range splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Stab splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Crush splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Slash splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Fire_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Ice_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Nature_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Chaos_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Poison_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Lightning_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "|-\n"
    for _, monster in ipairs(monsters) do
        for index, value in pairsByKeys(defaultAffinities) do
            for affinity, affinityValue in pairs(value) do
                if monster.defensiveDamageAffinity[affinity] then
                    defensiveAffinities[index] = { affinity = monster.defensiveDamageAffinity[affinity] }
                else
                    defensiveAffinities[index] = { affinity = affinityValue }
                end
            end
        end
        s = s .. "|" .. img._img({ monster["name"], 60 }) .. "\n"
        s = s .. "|style=\"text-align:left\"|[[" .. monster["name"] .. "]]\n"
        s = s .. "| " .. calcThreat(monster) .. "\n"
        s = s .. "|" .. tostring(monster.defensiveCritical.chance * 100) .. "%\n"
        s = s .. "|" .. tostring(100 - (monster.defensiveCritical.damageMultiplier * 100)) .. "%\n"
        for _, value in pairsByKeys(defensiveAffinities) do
            for _, affinityValue in pairs(value) do
                if affinityValue > 1 then
                    s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
                elseif affinityValue == 1 then
                    s = s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
                else
                    s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
                end
            end
        end
        s = s .. "|-\n"
    end
    s = s .. "|}"
    return s
end

local function genOffensiveTable(monsters, collapsed)
    local offensiveAffinities = {}
    local s = "{|class=\"wikitable sortable mw-collapsible " .. collapsed .. "\" style=\"text-align:center;width:100%\"\n"
    s = s .. "|+ style=\"white-space:nowrap\" |Offensive Affinities\n"
    s = s .. "!rowspan=2 colspan=2|Monster\n"
    s = s .. "!rowspan=2|Crit<br/>Chance\n"
    s = s .. "!rowspan=2|Crit<br/>Multiplier\n"
    s = s .. "!colspan=12 style=\"text-align:center\"|Affinities\n"
    s = s .. "|-\n"
    s = s .. "![[File:Melee splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Magic splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Range splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Stab splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Crush splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Slash splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Fire_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Ice_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Nature_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Chaos_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Poison_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Lightning_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "|-\n"
    for _, monster in ipairs(monsters) do
        for index, value in pairsByKeys(defaultAffinities) do
            for affinity, affinityValue in pairs(value) do
                if monster.offensiveDamageAffinity[affinity] then
                    offensiveAffinities[index] = { affinity = monster.offensiveDamageAffinity[affinity] }
                else
                    offensiveAffinities[index] = { affinity = affinityValue }
                end
            end
        end
        s = s .. "|" .. img._img({ monster["name"], 60 }) .. "\n"
        s = s .. "|style=\"text-align:left\"|[[" .. monster["name"] .. "]]\n"
        s = s .. "|" .. tostring(monster.offensiveCritical.chance * 100) .. "%\n"
        s = s .. "|" .. tostring(monster.offensiveCritical.damageMultiplier * 100) .. "%\n"
        for _, value in pairsByKeys(offensiveAffinities) do
            for _, affinityValue in pairs(value) do
                if affinityValue > 1 then
                    s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
                elseif affinityValue == 1 then
                    s = s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
                else
                    s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
                end
            end
        end
        s = s .. "|-\n"
    end
    s = s .. "|}"
    return s
end

local function genCombinedTable(monsters, collapsed)
    local offensiveAffinities = {}
    local defensiveAffinities = {}
    local s = "{|class=\"wikitable sortable mw-collapsible " .. collapsed .. "\" style=\"text-align:center;width:100%\"\n"
    s = s .. "|+ style=\"white-space:nowrap\" |Affinities\n"
    s = s .. "!rowspan=2 colspan=2|Monster\n"
    s = s .. "!rowspan=2|Crit<br/>Chance\n"
    s = s .. "!rowspan=2|Crit<br/>Multiplier\n"
    s = s .. "!colspan=12 style=\"text-align:center\"|Affinities\n"
    s = s .. "!rowspan=2|Base<br/>Threat\n"
    s = s .. "!rowspan=2|Crit<br/>Avoidance\n"
    s = s .. "!rowspan=2|Crit<br/>Reduction\n"
    s = s .. "!colspan=12 style=\"text-align:center\"|Affinities\n"
    s = s .. "|-\n"
    s = s .. "![[File:Melee splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Magic splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Range splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Stab splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Crush splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Slash splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Fire_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Ice_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Nature_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Chaos_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Poison_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Lightning_splash.png|20px|link=Combat#Affinities]]\n"
    -- sep
    s = s .. "![[File:Melee splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Magic splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Range splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Stab splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Crush splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Slash splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Fire_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Ice_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Nature_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Chaos_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Poison_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "![[File:Lightning_splash.png|20px|link=Combat#Affinities]]\n"
    s = s .. "|-\n"
    for _, monster in ipairs(monsters) do
        for index, value in pairsByKeys(defaultAffinities) do
            for affinity, affinityValue in pairs(value) do
                if monster.offensiveDamageAffinity[affinity] then
                    offensiveAffinities[index] = { affinity = monster.offensiveDamageAffinity[affinity] }
                else
                    offensiveAffinities[index] = { affinity = affinityValue }
                end
                if monster.defensiveDamageAffinity[affinity] then
                    defensiveAffinities[index] = { affinity = monster.defensiveDamageAffinity[affinity] }
                else
                    defensiveAffinities[index] = { affinity = affinityValue }
                end
            end
        end
        s = s .. "|" .. img._img({ monster["name"], 60 }) .. "\n"
        s = s .. "|style=\"text-align:left\"|[[" .. monster["name"] .. "]]\n"
        s = s .. "|" .. tostring(monster.offensiveCritical.chance * 100) .. "%\n"
        s = s .. "|" .. tostring(monster.offensiveCritical.damageMultiplier * 100) .. "%\n"
        for _, value in pairsByKeys(offensiveAffinities) do
            for _, affinityValue in pairs(value) do
                if affinityValue > 1 then
                    s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
                elseif affinityValue == 1 then
                    s = s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
                else
                    s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
                end
            end
        end
        s = s .. "| " .. calcThreat(monster) .. "\n"
        s = s .. "|" .. tostring(monster.defensiveCritical.chance * 100) .. "%\n"
        s = s .. "|" .. tostring(100 - (monster.defensiveCritical.damageMultiplier * 100)) .. "%\n"
        for _, value in pairsByKeys(defensiveAffinities) do
            for _, affinityValue in pairs(value) do
                if affinityValue > 1 then
                    s = s .. "|<span style=\"color:#4caf50\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
                elseif affinityValue == 1 then
                    s = s .. "|" .. tonumber((affinityValue - 1) * 100) .. "%\n"
                else
                    s = s .. "|<span style=\"color:#f44336\">" .. tonumber((affinityValue - 1) * 100) .. "%<span/>\n"
                end
            end
        end
        s = s .. "|-\n"
    end
    s = s .. "|}"
    return s
end

function p.monsters_stats_table(frame)
    return p._monsters_stats_table(frame:getParent().args)
end

function p._monsters_stats_table(_args)
    local monstersName = fromCSV(_args[1])
    local monsters = {}
    local style = _args[2]
    local collapsed
    local s = ""
    if _args["collapsed"] then
        collapsed = "mw-collapsed"
    else
        collapsed = ""
    end

    for _, value in ipairs(monstersName) do
        table.insert(monsters, p.loadData("normal")[tostring(findId._findId({ value, "monster" }))])
        table.insert(monsters, p.loadData("dungeon")[tostring(findId._findId({ value, "monster" }))])
    end
    if monsters == nil then
        return "no monster match"
    end
    if style == "defensive" then
        return genDefensiveTable(monsters, collapsed)
    elseif style == "offensive" then
        return genOffensiveTable(monsters, collapsed)
    elseif style == "combined" then
        return genCombinedTable(monsters, collapsed)
    else
        return "wrong style"
    end
end

return p