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