From Dark and Darker Wiki

(Created page with "local p = {} local AD = mw.loadJsonData("Data:Armor.json") local is_special = {["Move Speed"]=true, ["Armor Rating"]=true, ["Magical Resistance"]=true} local is_primitive = {["Strength"]=true, ["Vigor"]=true, ["Agility"]=true, ["Dexterity"]=true, ["Will"]=true, ["Knowledge"]=true, ["Resourcefulness"]=true} -- local color_rarity = {"cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", "cr8"} local function get_type(item) --Assumes that the item only has one type local key,...")
 
(Table header is made redundant and unnecessary by the tab toggles)
 
(12 intermediate revisions by the same user not shown)
Line 1: Line 1:
local p = {}
local p = {}
local AD = mw.loadJsonData("Data:Armor.json")
local AD = mw.loadJsonData("Data:Armor.json")
local insert = table.insert
local concat = table.concat
local is_special = {["Move Speed"]=true, ["Armor Rating"]=true, ["Magical Resistance"]=true}
local is_special = {["Move Speed"]=true, ["Armor Rating"]=true, ["Magical Resistance"]=true}
local is_primitive = {["Strength"]=true, ["Vigor"]=true, ["Agility"]=true, ["Dexterity"]=true, ["Will"]=true, ["Knowledge"]=true, ["Resourcefulness"]=true}
local is_primitive = {["Strength"]=true, ["Vigor"]=true, ["Agility"]=true, ["Dexterity"]=true, ["Will"]=true, ["Knowledge"]=true, ["Resourcefulness"]=true}
-- local color_rarity = {"cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", "cr8"}




---Get the type of the armor as a string
--- @param item table
--- @return string
local function get_type(item)
local function get_type(item)
--Assumes that the item only has one type
--Assumes that the item only has one type
local key,_ = next(item.types)
for key,_ in pairs(item.types) do
return key or ""
--Scribunto uses meta tables so we can't use next() here
return key
end
return ""
end
end




---Get the size of the armor in pixels
--- @param item table
--- @return string
local function size(item)
local function size(item)
return tostring(item.invwidth*45).."x"..tostring(item.invheight*45).."px"
return tostring(item.invwidth*45).."x"..tostring(item.invheight*45).."px"
end
---Counts number of keys. Tables from Misc.json don't work with next() or the # operator
--- @param t table
--- @return number
local function count(t)
local c = 0
for _ in pairs(t) do c = c + 1 end
return c
end
end


Line 22: Line 42:
---@return string
---@return string
local function iconbox(item)
local function iconbox(item)
local wikitext = "<div class='iconbox' style='display:inline-flex;width:max-content;max-width:initial;flex-direction:column;align-items:center;flex-wrap:wrap;white-space:pre-wrap'>"
local color = "2"
local image = "[[File:"..item.name..".png|"..size(item).."|link="..item.name.."]]"
local amount = "<span class='iconamount' style='pointer-events:none;color:#EEEA;font-size:16px'>"..get_type(item).."</span>"
local caption = "[["..item.name
local caption = "[["..item.name
local color = "2"
if count(item.rarities) == 1 then
if #item.rarities == 1 then
color = item.rarities[1]
color = item.rarities[1]
caption = caption.."|<b class=cr"..color..">"..item.name.."</b>"
caption = caption.."|<b class=cr"..color..">"..item.name.."</b>"
end
end
image = "<div class='rarity-"..color.." rounded relative'>"..image..amount.."</div>"
return "<div class='iconbox' style='display:inline-flex;width:max-content;max-width:initial;flex-direction:column;align-items:center;flex-wrap:wrap;white-space:pre-wrap'>"
return wikitext..image..caption.."]]</div>"
.."<div class='rarity"..color.." rounded relative'>"
.."[[File:"..item.name..".png|"..size(item).."|link="..item.name.."]]"
.."<span class='iconamount' style='pointer-events:none;color:#EEEA;font-size:16px'>"..get_type(item).."</span></div>"
..caption.."]]</div>"
end


---Turns a list of classes into a wikitext string with line break separators
--- @param list table
--- @return string
local function classes(list)
local wikitext = "" --classes are few enough that we can just concatenate them without worrying about performance
local newline = ""
for i, class in ipairs(list) do
wikitext = wikitext..newline..class
if i == 1 then newline = "<br>" end
end
return wikitext
end
end




local function color_values(values)
---Marks up a list of values with color based on rarity
if type(values)=="string" then return values end
---@param values string|table
---@param armor table
---@return string
local function color_values(values,armor)
if values == nil then return "" end
if type(values)=="string" then
if count(armor.rarities) == 1 then return "<span class='cr"..armor.rarities[1].."'>"..values.."</span>" end
return values
end


--TODO this assumes i is the same as the color rating, this may result in a bug
--TODO this assumes i is the same as the color rating, this may result in a bug
Line 47: Line 88:
if i == 1 then newline = "<br>" end
if i == 1 then newline = "<br>" end
end
end
return wikitext
return wikitext
end
end




local function inline_block(stat, values)
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment
if values == nil then return "" end
--- @param stat string
return "<div style='display:inline-block; margin:0 15px 0 15px'><b style='color:#eee8'>"..stat..":</b><br>"..color_values(values).."</div>"
--- @param armor table
--- @return string
local function inline_block(stat, armor)
if armor.stats[(stat):lower()] == nil then return "" end
return "<div style='display:inline-block;vertical-align:top;margin:0 15px 0 15px'><b style='color:#eee8'>"..stat.."</b><br>"..color_values(armor.stats[(stat):lower()],armor).."</div>"
end
end


Line 62: Line 106:
--- @return string
--- @return string
local function row(armor)
local function row(armor)
local wikitext = "<tr><td>"
local wikitext = {}
..iconbox(armor)
insert(wikitext,"<tr><td>")
.."</td><td>"
insert(wikitext,iconbox(armor))
insert(wikitext,"</td><td>")


if armor.slottype ~= "Back" then
if armor.slottype ~= "Back" then
wikitext = wikitext
insert(wikitext,classes(armor.classes))
.."[["
insert(wikitext,"</td><td>")
..table.concat(armor.classes,"]]<br>[[")
insert(wikitext,color_values(armor.stats["move speed"],armor))
.."]]</td><td>"
insert(wikitext,"</td><td>")
..color_values(armor.stats["move speed"])
-- elseif armor.stats["move speed"] then
.."</td><td>"
-- insert(wikitext,"<span style='color:red;'>Error: Back "..armor.name.." has unpresented move speed.</span>")
-- elseif next(armor.classes) then
-- insert(wikitext,"<span style='color:red;'>Error: Back "..armor.name.." has unpresented class requirements.</span>")
end
end


wikitext = wikitext
insert(wikitext,inline_block("Armor Rating",armor))
..inline_block("Armor Rating", armor.stats["armor rating"])
insert(wikitext,inline_block("Magical Resistance",armor))
..inline_block("Magical Resistance", armor.stats["magical resistance"])
insert(wikitext,"</td><td>")
.."</td><td>"


-- create the table data cell for attributes
-- create the table data cell for attributes
for _,stat in ipairs(armor.stats.order) do
for _,stat in ipairs(armor.stats.order) do
if is_primitive[stat] then
if is_primitive[stat] then
wikitext = wikitext..inline_block(stat, armor.stats[(stat):lower()])
insert(wikitext,inline_block(stat,armor))
end
end
end
end
wikitext = wikitext.."</td><td>"
insert(wikitext,"</td><td>")


-- create the table data cell for the rest of the stats
-- create the table data cell for the rest of the stats
for _, stat in ipairs(armor.stats.order) do
for _, stat in ipairs(armor.stats.order) do
if not is_primitive[stat] and not is_special[stat] then
if not is_primitive[stat] and not is_special[stat] then
wikitext = wikitext..inline_block(stat, armor.stats[(stat):lower()])
insert(wikitext,inline_block(stat,armor))
end
end
end
end
return wikitext.."</td></tr>"
insert(wikitext,"</td></tr>")
return concat(wikitext)
end
end


Line 101: Line 148:
--- @param frame table
--- @param frame table
--- @return table - strings
--- @return table - strings
local function get_armor_list(frame)
local function get_list(frame)
--TODO add minor string validation and correction, e.g. "DeMoN" -> "demon"  ("DeMoN"):lower()
--TODO add minor string validation and correction, e.g. "DeMoN" -> "demon"  ("DeMoN"):lower()
return AD[frame.args[1] or ""] or {}
return AD[frame.args[1]][frame.args[2]] or {}
end
end


Line 111: Line 158:
--- @return string
--- @return string
local function table_header(frame)
local function table_header(frame)
local wikitext = [=[<tr><th style="width:5%">Name</th>]=]
local wikitext = [=[<table cellspacing="0" class="wikitable sortable jquery-tablesorter" style="width:95%;color:#eee;background:transparent;text-align:center;vertical-align:middle;"><tr><th style="width:5%">Name</th>]=]
 
if (frame.args[1] or ""):lower() ~= "back" then
if (frame.args[1] or ""):lower() ~= "back" then
wikitext = wikitext..[=[<th style="width:5%">Class Requirements</th><th style="width:5%">Movement Speed</th>]=]
wikitext = wikitext..[=[<th style="width:5%">Class Requirements</th><th style="width:5%">Movement Speed</th>]=]
end
end
return wikitext..[=[<th style="width:10%">Armor/magical Rating</th><th style="width:10%">Attributes</th><th style="width:25%">Other</th></tr>]=]
return wikitext..[=[<th style="width:10%">Armor/magical Rating</th><th style="width:10%">Attributes</th><th style="width:25%">Other</th></tr>]=]
end
end


---Create monster table wikitext
---Create monster table wikitext
---- Test on wiki with: mw.log(p.draw_table({args={"bosses"}})) or mw.log(p.draw_table({args={"Demon","true"}}))
--- @param frame table
--- @param frame table
--- @return string
--- @return string
Line 127: Line 171:
if not AD then return "<span style='color:red;'>Error: Could not load Armor data in [[Module:Armor]]</span>" end
if not AD then return "<span style='color:red;'>Error: Could not load Armor data in [[Module:Armor]]</span>" end


local wikitext = table_header(frame) or "<table class='wikitable sortable'>"
local wikitext = {}
insert(wikitext, table_header(frame))
for _, key in ipairs(get_list(frame)) do
insert(wikitext, row(AD.Armor[key]))
end
insert(wikitext, "</table>")
return concat(wikitext)
end
 
 
--[=[
################################################################################
                        Functions for individual armor pages
################################################################################
]=]
 
local function tab_toggle(title,content,active,tabid)
local class = ""
if active then
class = " class='selected-tab tab-toggle tab'"
else
class = " class='tab-toggle tab'"
end
return string.format("<div style='display:inline-block' %s data-tabid='%s' data-tab='%s' title='%s'>%s</div>",class,tabid,title,title,content)
end
 
local function reference_table(wt,table_type)
local types_order = {"Head","Chest","Legs","Hands","Foot","Back"}
local separator = "]] • [["
 
wt[#wt+1] = "<table cellspacing='0' class='wikitable sortable jquery-tablesorter' style='width:100%;text-align:center;vertical-align:middle;margin-top:15px'>"
 
for _,armor_type in ipairs(types_order) do
wt[#wt+1] = string.format("<tr><td style='font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%'>[[Armors#%s|%s]]</td><td style='text-align-last:left;padding:10px 25px'>",armor_type,armor_type)
if AD[armor_type][table_type] then
wt[#wt+1] = "[["
for i,v in ipairs(AD[armor_type][table_type]) do
if i~=1 then wt[#wt+1] = separator end
wt[#wt+1] = v
end
wt[#wt+1] = "]]"
end
wt[#wt+1] = "</td></tr>"
end
wt[#wt+1] = "</table>"
end
 
function p.references()
local table_types = {"Uncraftable","Craftable"}


for _, key in ipairs(get_armor_list(frame)) do
local wt = {}
wikitext = wikitext..row(AD.armor[key])
wt[#wt+1] = "<br><h2>References</h2>"
wt[#wt+1] = "<div style='display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px'>"
for i,table_type in ipairs(table_types) do
wt[#wt+1] = tab_toggle(table_type,table_type.." Armors",i==1,"")
end
wt[#wt+1] = "</div>"
for i,table_type in ipairs(table_types) do
wt[#wt+1] = "<div class='"..table_type.."-data'"
if i==1 then wt[#wt+1] = ">" else wt[#wt+1] = "style='display:none'>" end
reference_table(wt,table_type)
wt[#wt+1] = "</div>"
end
end


return wikitext.."</table>"
return concat(wt)
end
end




return p
return p
-- Test on wiki with
-- mw.log(p.draw_table({args={"Back","Craftable"}}))
-- mw.log(p.draw_table({args={"Head","Uncraftable"}}))
-- mw.log(p.references())

Latest revision as of 07:31, 13 April 2026

Overview

Functions for making Armor table. Data comes from Data:Armor.json.

Functions

draw_table

Creates a table of armors

Parameters

  • 1 - <Slot Type>
  • 2 - Craftable or Uncraftable


draw_table examples

Back


{{#invoke:Armor|draw_table|Back|Craftable}}


NameArmor/magical RatingAttributesOther
Armor Rating
11
Will
2
Spell Casting Speed
3%
Armor Rating
10
Agility
3
Action Speed
1%
Armor Rating
15
Dexterity
1
Vigor
1
Will
1
Resourcefulness
1
Luck
40
All Attributes
1
Armor Rating
13
Knowledge
3
Projectile Damage Reduction
2%
Armor Rating
19
Strength
2
Dexterity
2
Will
2
Armor Rating
8
Vigor
3
Debuff Duration Bonus
2%


Head


{{#invoke:Armor|draw_table|Head|Uncraftable}}


NameClass RequirementsMovement SpeedArmor/magical RatingAttributesOther
Wizard
Warlock
Druid
Sorcerer
-2
Armor Rating
21
22
23
24~25
26~27
28
29
Will
1
2
2
3
4
5
5
Headshot Damage Reduction
8%
Armet.pngPlate
Armet
Fighter-5
Armor Rating
54
55
56~57
58~59
60~61
62
63
Strength
1
2
2
3
4
5
5
Projectile Damage Reduction
2.1%
Headshot Damage Reduction
23%
Fighter
Barbarian
-4
Armor Rating
26
27
28~29
30~31
32~33
34
35
Magical Resistance
30
Dexterity
1
2
2
3
4
5
5
Projectile Damage Reduction
1.2%
Headshot Damage Reduction
15%
Warlock-3
Armor Rating
33
34
35~36
37~38
39~40
41
42
Will
1
1
1
2
2
3
3
Projectile Damage Reduction
1.8%
Headshot Damage Reduction
15%
Headshot Damage Modifier
2.5%
3%
3%
3%
3.5%
3.5%
4%
Ranger-2
Armor Rating
24
25
26
27
28
29
30
Dexterity
1
1
1
2
2
3
3
Headshot Damage Reduction
8%
Headshot Damage Modifier
2.5%
3%
3%
3%
3.5%
3.5%
4%
Cleric
Sorcerer
-2
Armor Rating
21
22
23
24~25
26~27
28
29
Magical Resistance
20
Resourcefulness
1
2
2
3
4
5
5
Headshot Damage Reduction
8%
Fighter
Cleric
-3
Armor Rating
39
40
41~42
43~44
45~46
47
48
Magical Resistance
10
Agility
1
2
2
3
4
5
5
Projectile Damage Reduction
0.6%
Headshot Damage Reduction
13%
Cleric
Sorcerer
-2
Armor Rating
23
24
25
26~27
28~29
30
31
Magical Resistance
20
Will
1
2
2
3
4
5
5
Headshot Damage Reduction
8%
Coif.pngCloth
Coif
Wizard
Cleric
Druid
Sorcerer
-1
Armor Rating
17
18
19
20~21
22~23
24
25
Knowledge
1
2
2
3
4
5
5
Headshot Damage Reduction
8%
Fighter
Cleric
-5
Armor Rating
52
53
54~55
56~57
58~59
60
61
Strength
1
1
1
2
2
3
3
Vigor
0
1
1
1
2
2
2
Projectile Damage Reduction
1.8%
Headshot Damage Reduction
20%
Druid-2
Armor Rating
24
25
26
27~28
29~30
31
32
Strength
1
1
1
2
2
3
3
Will
0
1
1
1
2
2
2
Headshot Damage Reduction
8%
Dryad Mask.pngLeather
Dryad Mask
Warlock
Druid
-2
Armor Rating
26
27
28
29~30
31~32
33
34
Strength
1
1
1
2
2
2
2
Knowledge
1
1
1
1
2
2
2
Agility
0
1
1
1
1
2
2
Headshot Damage Reduction
10%
Druid-3
Armor Rating
31
32
33~34
35~36
37~38
39
40
Dexterity
0
1
1
1
2
2
2
Vigor
1
1
1
2
2
3
3
Headshot Damage Reduction
11%
Bard-2
Armor Rating
28
29
30
31~32
33~34
35
36
Knowledge
1
2
2
3
4
5
5
Headshot Damage Reduction
9%
Ranger
Druid
-2
Armor Rating
25
26
27
28~29
30~31
32
33
Magical Resistance
15
Strength
1
2
2
3
4
5
5
Headshot Damage Reduction
8%
Barbarian-4
Armor Rating
41
42
43~44
45~46
47~48
49
50
Magical Resistance
15
Vigor
1
2
2
3
4
5
5
Projectile Damage Reduction
1.2%
Headshot Damage Reduction
15%
Fighter
Cleric
-5
Armor Rating
55
56
57~58
59~60
61~62
63
64
Knowledge
1
2
2
3
4
5
5
Projectile Damage Reduction
2.1%
Headshot Damage Reduction
23%
Fighter
Cleric
-5
Armor Rating
58
59
60~61
62~63
64~65
66
67
Magical Resistance
-5
Vigor
1
2
2
3
4
5
5
Projectile Damage Reduction
2.1%
Headshot Damage Reduction
24%
Rogue
Bard
-2
Armor Rating
24
25
26
27
28
29
30
Dexterity
1
1
1
2
2
3
3
Headshot Damage Reduction
8%
Headshot Damage Modifier
2.5%
3%
3%
3%
3.5%
3.5%
4%
Fighter
Cleric
-3
Armor Rating
36
37
38~39
40~41
42~43
44
45
Magical Resistance
10
Knowledge
1
2
2
3
4
5
5
Projectile Damage Reduction
0.6%
Headshot Damage Reduction
13%
Bard-2
Armor Rating
29
30
31
32~33
34~35
36
37
Resourcefulness
1
2
2
3
4
5
5
Headshot Damage Reduction
10%
-3
Armor Rating
31
32
33~34
35~36
37~38
39
40
Vigor
1
2
2
3
4
5
5
Headshot Damage Reduction
11%
Mitre.pngLeather
Mitre
Cleric-2
Armor Rating
28
29
30
31
32
33
34
Will
1
1
1
2
2
3
3
Headshot Damage Reduction
11%
Undead Race Damage Bonus
5%
6%
6%
6%
7%
7%
8%
Barbarian-3
Armor Rating
37
38
39~40
41~42
43~44
45
46
Strength
0
1
1
1
2
2
2
Vigor
1
1
1
2
2
3
3
Projectile Damage Reduction
0.9%
Headshot Damage Reduction
13%
Warlock-2
Armor Rating
25
26
27
28~29
30~31
32
33
Headshot Damage Reduction
8%
Outgoing Magical Healing Add
1
1
1
2
2
3
3
Magical Power
0
1
1
1
2
2
2
Fighter
Ranger
Cleric
Bard
-3
Armor Rating
35
36
37~38
39~40
41~42
43
44
Will
1
1
1
2
2
2
2
Knowledge
1
1
1
1
2
2
2
Resourcefulness
0
1
1
1
1
2
2
Projectile Damage Reduction
0.9%
Headshot Damage Reduction
14%
Ranger-2
Armor Rating
25
26
27
28~29
30~31
32
33
Agility
1
2
2
3
4
5
5
Headshot Damage Reduction
8%
Cleric-2
Armor Rating
28
29
30
31
32
33
34
Will
1
1
1
2
2
3
3
Headshot Damage Reduction
11%
Demon Race Damage Bonus
5%
6%
6%
6%
7%
7%
8%
Rogue-2
Armor Rating
25
26
27
28~29
30~31
32
33
Agility
1
2
2
3
4
5
5
Headshot Damage Reduction
8%
Sallet.pngPlate
Sallet
Fighter
Cleric
-3
Armor Rating
33
34
35~36
37~38
39~40
41
42
Strength
1
1
1
2
2
3
3
Vigor
0
1
1
1
2
2
2
Projectile Damage Reduction
0.9%
Headshot Damage Reduction
13%
Rogue
Warlock
-2
Armor Rating
18
19
20
21~22
23~24
25
26
Magical Resistance
15
Strength
1
2
2
3
4
5
5
Headshot Damage Reduction
8%
Rogue
Warlock
-1
Armor Rating
5
6
7
8
9
10
11
Agility
1
1
1
2
2
2
2
Dexterity
1
1
1
1
2
2
2
Strength
0
1
1
1
1
2
2
Fighter
Ranger
Cleric
-4
Armor Rating
40
41
42~43
44~45
46~47
48
49
Agility
1
1
1
2
2
3
3
Dexterity
0
1
1
1
2
2
2
Projectile Damage Reduction
1.2%
Headshot Damage Reduction
16%
Rogue
Bard
-2
Armor Rating
21
22
23
24
25
26
27
Strength
1
1
1
2
2
3
3
Dexterity
0
1
1
1
2
2
2
Headshot Damage Reduction
8%
Bard
Druid
-2
Armor Rating
26
27
28
29~30
31~32
33
34
Strength
1
2
2
3
4
5
5
Headshot Damage Reduction
8%
Fighter
Cleric
-5
Armor Rating
57
58
59~60
61~62
63~64
65
66
Vigor
1
1
1
2
2
3
3
Will
0
1
1
1
2
2
2
Projectile Damage Reduction
2.1%
Headshot Damage Reduction
24%
Ranger
Barbarian
-3
Armor Rating
33
34
35
36
37
38
39
Strength
1
1
1
2
2
3
3
Headshot Damage Reduction
11%
Headshot Damage Modifier
2.5%
3%
3%
3%
3.5%
3.5%
4%
Barbarian-3
Armor Rating
37
38
39~40
41~42
43~44
45
46
Agility
1
2
2
3
4
5
5
Projectile Damage Reduction
2%
Headshot Damage Reduction
15%
Fighter-4
Armor Rating
48
49
50~51
52~53
54~55
56
57
Will
1
2
2
3
4
5
5
Projectile Damage Reduction
1.8%
Headshot Damage Reduction
16%
Action Speed
1.4%
1.4%
1.4%
1.6%
1.8%
2%
2.2%
Barbarian-5
Armor Rating
50
51
52~53
54~55
56~57
58
59
Agility
1
1
1
2
2
3
3
Dexterity
0
1
1
1
2
2
2
Projectile Damage Reduction
2.1%
Headshot Damage Reduction
18%
Wizard
Warlock
-2
Armor Rating
20
21
22
23~24
25~26
27
28
Headshot Damage Reduction
8%
Magical Power
1
2
2
3
4
5
5
Memory Capacity Add
3
-2
Armor Rating
20
21
22
23
24
25
26
Vigor
1
1
1
2
2
2
2
Dexterity
1
1
1
1
2
2
2
Agility
0
1
1
1
1
2
2
Headshot Damage Reduction
5%

local p = {}
local AD = mw.loadJsonData("Data:Armor.json")
local insert = table.insert
local concat = table.concat
local is_special = {["Move Speed"]=true, ["Armor Rating"]=true, ["Magical Resistance"]=true}
local is_primitive = {["Strength"]=true, ["Vigor"]=true, ["Agility"]=true, ["Dexterity"]=true, ["Will"]=true, ["Knowledge"]=true, ["Resourcefulness"]=true}


---Get the type of the armor as a string
--- @param item table
--- @return string
local function get_type(item)
	--Assumes that the item only has one type
	for key,_ in pairs(item.types) do
		--Scribunto uses meta tables so we can't use next() here
		return key
	end
	return ""
end


---Get the size of the armor in pixels
--- @param item table
--- @return string
local function size(item)
	return tostring(item.invwidth*45).."x"..tostring(item.invheight*45).."px"
end


---Counts number of keys. Tables from Misc.json don't work with next() or the # operator
--- @param t table
--- @return number
local function count(t)
	local c = 0
	for _ in pairs(t) do c = c + 1 end
	return c
end


---Create css/html wikitext for iconbox
---@param item table
---@return string
local function iconbox(item)
	local color = "2"
	local caption = "[["..item.name
	if count(item.rarities) == 1 then
		color = item.rarities[1]
		caption = caption.."|<b class=cr"..color..">"..item.name.."</b>"
	end
	return "<div class='iconbox' style='display:inline-flex;width:max-content;max-width:initial;flex-direction:column;align-items:center;flex-wrap:wrap;white-space:pre-wrap'>"
		.."<div class='rarity"..color.." rounded relative'>"
		.."[[File:"..item.name..".png|"..size(item).."|link="..item.name.."]]"
		.."<span class='iconamount' style='pointer-events:none;color:#EEEA;font-size:16px'>"..get_type(item).."</span></div>"
		..caption.."]]</div>"
end


---Turns a list of classes into a wikitext string with line break separators
--- @param list table
--- @return string
local function classes(list)
	local wikitext = "" --classes are few enough that we can just concatenate them without worrying about performance
	local newline = ""
	for i, class in ipairs(list) do
		wikitext = wikitext..newline..class
		if i == 1 then newline = "<br>" end
	end
	return wikitext
end


---Marks up a list of values with color based on rarity
---@param values string|table
---@param armor table
---@return string
local function color_values(values,armor)
	if values == nil then return "" end
	if type(values)=="string" then
		if count(armor.rarities) == 1 then return "<span class='cr"..armor.rarities[1].."'>"..values.."</span>" end
		return values
	end

	--TODO this assumes i is the same as the color rating, this may result in a bug
	local wikitext = ""
	local newline = ""
	for i, value in ipairs(values) do
		wikitext = wikitext..newline.."<span class='cr"..i.."'>"..value.."</span>"
		if i == 1 then newline = "<br>" end
	end
	return wikitext
end


---Marks up a stat block with the stat name and colored values and appropriate margins and alignment
--- @param stat string
--- @param armor table
--- @return string
local function inline_block(stat, armor)
	if armor.stats[(stat):lower()] == nil then return "" end
	return "<div style='display:inline-block;vertical-align:top;margin:0 15px 0 15px'><b style='color:#eee8'>"..stat.."</b><br>"..color_values(armor.stats[(stat):lower()],armor).."</div>"
end


---Return a table row of data cells containing armor information
--- @param armor table
--- @return string
local function row(armor)
	local wikitext = {}
	insert(wikitext,"<tr><td>")
	insert(wikitext,iconbox(armor))
	insert(wikitext,"</td><td>")

	if armor.slottype ~= "Back" then
		insert(wikitext,classes(armor.classes))
		insert(wikitext,"</td><td>")
		insert(wikitext,color_values(armor.stats["move speed"],armor))
		insert(wikitext,"</td><td>")
	-- elseif armor.stats["move speed"] then
	-- 	insert(wikitext,"<span style='color:red;'>Error: Back "..armor.name.." has unpresented move speed.</span>")
	-- elseif next(armor.classes) then
	-- 	insert(wikitext,"<span style='color:red;'>Error: Back "..armor.name.." has unpresented class requirements.</span>")
	end

	insert(wikitext,inline_block("Armor Rating",armor))
	insert(wikitext,inline_block("Magical Resistance",armor))
	insert(wikitext,"</td><td>")

	-- create the table data cell for attributes
	for _,stat in ipairs(armor.stats.order) do
		if is_primitive[stat] then
			insert(wikitext,inline_block(stat,armor))
		end
	end
	insert(wikitext,"</td><td>")

	-- create the table data cell for the rest of the stats
	for _, stat in ipairs(armor.stats.order) do
		if not is_primitive[stat] and not is_special[stat] then
			insert(wikitext,inline_block(stat,armor))
		end
	end
	insert(wikitext,"</td></tr>")
	return concat(wikitext)
end


--- Return a list of armor keys to be used in the table
--- @param frame table
--- @return table - strings
local function get_list(frame)
	--TODO add minor string validation and correction, e.g. "DeMoN" -> "demon"  ("DeMoN"):lower()
	return AD[frame.args[1]][frame.args[2]] or {}
end


--- Return a table row of appropriate headers cells
--- @param frame table
--- @return string
local function table_header(frame)
	local wikitext = [=[<table cellspacing="0" class="wikitable sortable jquery-tablesorter" style="width:95%;color:#eee;background:transparent;text-align:center;vertical-align:middle;"><tr><th style="width:5%">Name</th>]=]
	if (frame.args[1] or ""):lower() ~= "back" then
		wikitext = wikitext..[=[<th style="width:5%">Class Requirements</th><th style="width:5%">Movement Speed</th>]=]
	end
	return wikitext..[=[<th style="width:10%">Armor/magical Rating</th><th style="width:10%">Attributes</th><th style="width:25%">Other</th></tr>]=]
end

---Create monster table wikitext
--- @param frame table
--- @return string
function p.draw_table(frame)
	if not AD then return "<span style='color:red;'>Error: Could not load Armor data in [[Module:Armor]]</span>" end

	local wikitext = {}
	insert(wikitext, table_header(frame))
	for _, key in ipairs(get_list(frame)) do
		insert(wikitext, row(AD.Armor[key]))
	end
	insert(wikitext, "</table>")
	return concat(wikitext)
end


--[=[
################################################################################
                        Functions for individual armor pages
################################################################################
]=]

local function tab_toggle(title,content,active,tabid)
	local class = ""
	if active then
		class = " class='selected-tab tab-toggle tab'"
	else
		class = " class='tab-toggle tab'"
	end
	return string.format("<div style='display:inline-block' %s data-tabid='%s' data-tab='%s' title='%s'>%s</div>",class,tabid,title,title,content)
end

local function reference_table(wt,table_type)
	local types_order = {"Head","Chest","Legs","Hands","Foot","Back"}
	local separator = "]] • [["

	wt[#wt+1] = "<table cellspacing='0' class='wikitable sortable jquery-tablesorter' style='width:100%;text-align:center;vertical-align:middle;margin-top:15px'>"

	for _,armor_type in ipairs(types_order) do
		wt[#wt+1] = string.format("<tr><td style='font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%'>[[Armors#%s|%s]]</td><td style='text-align-last:left;padding:10px 25px'>",armor_type,armor_type)
		if AD[armor_type][table_type] then
			wt[#wt+1] = "[["
			for i,v in ipairs(AD[armor_type][table_type]) do
				if i~=1 then wt[#wt+1] = separator end
				wt[#wt+1] = v
			end
			wt[#wt+1] = "]]"
		end
		wt[#wt+1] = "</td></tr>"
	end
	wt[#wt+1] = "</table>"
end

function p.references()
	local table_types = {"Uncraftable","Craftable"}

	local wt = {}
	wt[#wt+1] = "<br><h2>References</h2>"
	wt[#wt+1] = "<div style='display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px'>"
	for i,table_type in ipairs(table_types) do
		wt[#wt+1] = tab_toggle(table_type,table_type.." Armors",i==1,"")
	end
	wt[#wt+1] = "</div>"
	for i,table_type in ipairs(table_types) do
		wt[#wt+1] = "<div class='"..table_type.."-data'"
		if i==1 then wt[#wt+1] = ">" else wt[#wt+1] = "style='display:none'>" end
		reference_table(wt,table_type)
		wt[#wt+1] = "</div>"
	end

	return concat(wt)
end



return p



-- Test on wiki with
-- mw.log(p.draw_table({args={"Back","Craftable"}}))
-- mw.log(p.draw_table({args={"Head","Uncraftable"}}))

-- mw.log(p.references())