Module:Infobox Monster
Jump to navigation
Jump to search
local p = {} local findId = require("Module:FindId") local infoboxModule = require('Module:Infobox') local monstersData = mw.loadData("Module:Monsters/data") local normalMonsters = mw.loadData("Module:Monsters stats normal/data") local dungeonMonsters = mw.loadData("Module:Monsters stats dungeon/data") local headerCount = 1 local labelCount = 1 local dataCount = 1 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 affinitiesIcon = { [1] = "[[File:Melee splash.png|20px|link=Combat#Affinities]]", [2] = "[[File:Magic splash.png|20px|link=Combat#Affinities]]", [3] = "[[File:Range splash.png|20px|link=Combat#Affinities]]", [4] = "[[File:Stab splash.png|20px|link=Combat#Affinities]]", [5] = "[[File:Crush splash.png|20px|link=Combat#Affinities]]", [6] = "[[File:Slash splash.png|20px|link=Combat#Affinities]]", [7] = "[[File:Fire_splash.png|20px|link=Combat#Affinities]]", [8] = "[[File:Ice_splash.png|20px|link=Combat#Affinities]]", [9] = "[[File:Nature_splash.png|20px|link=Combat#Affinities]]", [10] = "[[File:Chaos_splash.png|20px|link=Combat#Affinities]]", [11] = "[[File:Poison_splash.png|20px|link=Combat#Affinities]]", [12] = "[[File:Lightning_splash.png|20px|link=Combat#Affinities]]" } -- Convert from CSV string to table (converts a single line of a CSV file) local function fromCSV(s) if string.sub(s, -1) ~= ',' then s = s .. ',' -- ending comma end s = string.gsub(s, "[%[%]]", ""):gsub(",%s", ",") 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 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 local function h() local s = "header" .. headerCount headerCount = headerCount + 1 labelCount = headerCount dataCount = headerCount return s end local function sbreak() local s = "sbreak" .. headerCount headerCount = headerCount + 1 labelCount = headerCount dataCount = headerCount return s end local function l() local s = "label" .. labelCount dataCount = labelCount labelCount = labelCount + 1 headerCount = labelCount return s end local function d() local s = "data" .. dataCount dataCount = dataCount + 1 headerCount = dataCount labelCount = dataCount return s end local function sl() local s = "s" .. l {} return s end local function sd() local s = "s" .. d {} return s end local function rc() local s = "rowclass" .. labelCount return s end ---fetches an monster object from monsters/data module ---@param id number ---@return table|nil local function getMonster(id) local mosnter = monstersData[tostring(id)] if mosnter then return mosnter end return nil end local function getMonsterStats(id) local mosnterStats = normalMonsters[tostring(id)] if not mosnterStats then mosnterStats = dungeonMonsters[tostring(id)] if not mosnterStats then return nil end end return mosnterStats 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 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 createImgTag(monster) local url = fullUrl(monster.image) local attrs = { src = fullUrl(monster.image), alt = monster.name, width = "150px" } local img = mw.html.create('img'):attr(attrs) return tostring(img) end local function createOffAffinity(args, monsterStats) for index, value in pairsByKeys(defaultAffinities) do for affinity, affinityValue in pairs(value) do if monsterStats.offensiveDamageAffinity[affinity] then affinityValue = monsterStats.offensiveDamageAffinity[affinity] args[sl()] = affinitiesIcon[index] if affinityValue > 1 then args[sd()] = "<span style=\"color:#4caf50\">" .. (affinityValue - 1) * 100 .. "%<span/>" elseif affinityValue < 1 then args[sd()] = "<span style=\"color:#f44336\">" .. (affinityValue - 1) * 100 .. "%<span/>" else args[sd()] = (affinityValue - 1) * 100 .. "%" end else args[sl()] = affinitiesIcon[index] args[sd()] = (affinityValue - 1) * 100 .. "%" end if index % 3 == 0 then args["bodyclass"] = "equal-space" args[sbreak()] = "yes" end end end return args end local function createDeffAffinity(args, monsterStats) for index, value in pairsByKeys(defaultAffinities) do for affinity, affinityValue in pairs(value) do if monsterStats.defensiveDamageAffinity[affinity] then affinityValue = monsterStats.defensiveDamageAffinity[affinity] args[sl()] = affinitiesIcon[index] if affinityValue > 1 then args[sd()] = "<span style=\"color:#4caf50\">" .. (affinityValue - 1) * 100 .. "%<span/>" elseif affinityValue < 1 then args[sd()] = "<span style=\"color:#f44336\">" .. (affinityValue - 1) * 100 .. "%<span/>" else args[sd()] = (affinityValue - 1) * 100 .. "%" end else args[sl()] = affinitiesIcon[index] args[sd()] = (affinityValue - 1) * 100 .. "%" end if index % 3 == 0 then args["bodyclass"] = "equal-space" args[sbreak()] = "yes" end end end return args end local function getZonesLink(zones) local s = "" if zones then for index, value in ipairs(zones) do s = s .. "[[" .. capitalize(value) .. "]]" if tablelength(zones) > index then s = s .. ", " end end end return s end local function createInfobox(monster, monsterStats, zones, dps) local args = {} args.autoheaders = "y" args.subbox = "no" args.bodystyle = " " args.title = monster.name args.image = createImgTag(monster) args[l()] = "Zones" args[d()] = getZonesLink(zones) args[h()] = "Offensive Stats" args[sl()] = "Attack Speed" args[sd()] = tostring(monsterStats.attackSpeed) args[sl()] = "DPS" args[sd()] = tostring(dps) args[sl()] = "Crit Chance" args[sd()] = tostring(monsterStats.offensiveCritical.chance * 100) .. "%" args[sl()] = "Crit Multiplier" args[sd()] = tostring(monsterStats.offensiveCritical.damageMultiplier * 100) .. "%" args[h()] = "Offensive Affinities" createOffAffinity(args, monsterStats) args[h()] = "Defensive Stats" args[sl()] = "Threat" args[sd()] = calcThreat(monsterStats) args[sl()] = "Crit Avoidance" args[sd()] = tostring(monsterStats.defensiveCritical.chance * 100) .. "%" args[sl()] = "Crit Reduction" args[sd()] = tostring(monsterStats.defensiveCritical.damageMultiplier * 100) .. "%" args[h()] = "Defensive Affinities" createDeffAffinity(args, monsterStats) for key, data in pairs(args) do if string.find(key, "data") then args[key] = tostring(data) end end return infoboxModule.infobox(args) end function p.infoboxMonster(frame) local args = frame:getParent().args return p._infoboxMonster(args) end function p._infoboxMonster(_args) local dps local zones if _args["zones"] then zones = fromCSV(_args["zones"]) end if _args["DPS"] then dps = _args["DPS"] else dps = "-" end local name local id local monster local monsterStats local infobox if _args[1] then name = _args[1] else name = mw.title.getCurrentTitle().text end id = findId._findId({name, "monster"}) if not id then return "<div style=\"color:red\"> No monster named '" .. name .. "'</div>. The Module:Monsters Ids/data maybe outdated." end monster = getMonster(id) if not monster then return "<div style=\"color:red\"> No monster named '" .. name .. "'</div>. The Module:Monsters/data maybe outdated." end monsterStats = getMonsterStats(id) if not monsterStats then return "<div style=\"color:red\"> No monster named '" .. name .. "'</div>. Module:Monsters stats normal/data and Module:Monsters stats dungeon/data maybe outdated." end infobox = createInfobox(monster, monsterStats, zones, dps) return infobox end return p