<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://darkanddarker.wiki.spellsandguns.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Raw+Salad</id>
	<title>Dark and Darker Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://darkanddarker.wiki.spellsandguns.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Raw+Salad"/>
	<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/Special:Contributions/Raw_Salad"/>
	<updated>2026-04-21T10:02:57Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.0</generator>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=66307</id>
		<title>Module:Weapon</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=66307"/>
		<updated>2026-04-15T21:32:49Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Fixed bug in page blocking section.  The existence check wasn&amp;#039;t catching all cases.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local WD = mw.loadJsonData(&amp;quot;Data:Weapon.json&amp;quot;) --Global var holding tables from Data:Weapon.json&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
---Get the item&#039;s size in pixels&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Weapon.json don&#039;t work with next() or the # operator&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
local function color_values(weapon, values)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(weapon.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..weapon.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(weapon.rarities) do&lt;br /&gt;
		wt = wt..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment&lt;br /&gt;
local function inline_block(weapon,stat)&lt;br /&gt;
	if weapon.stats[(stat):lower()] == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:inline-block;vertical-align:top;margin:0 15px 0 15px&#039;&amp;gt;&amp;lt;b style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..stat..&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[(stat):lower()])..&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell for the iconbox&lt;br /&gt;
local function name(item, is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local color = &amp;quot;2&amp;quot;&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local icon_amount = &amp;quot;&amp;quot;&lt;br /&gt;
	if item.maxammocount &amp;gt; 0 then&lt;br /&gt;
		icon_amount = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxammocount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..icon_amount&lt;br /&gt;
			..&amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats the weapon&#039;s list of classes into a table cell of classes separated by linebreaks&lt;br /&gt;
local function classes(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Class&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, class in ipairs(weapon.classes) do&lt;br /&gt;
		wt = wt..newline..class&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end -- Subsequent loops will prefix a linebreak&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of slottype and handtype&lt;br /&gt;
local function slot_type(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Slot&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.slottype..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..weapon.handtype..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of move speeds&lt;br /&gt;
local function move_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Movement Speed&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[&amp;quot;move speed&amp;quot;])..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of Armor rating and Magical Resistance&lt;br /&gt;
local function armor_rating(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Armor Rating&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..inline_block(weapon,&amp;quot;Armor Rating&amp;quot;)..inline_block(weapon,&amp;quot;Magical Resistance&amp;quot;)..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function impact_zones(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Impact Zones + Impact Power&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt= &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then&lt;br /&gt;
			for j,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
				if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then&lt;br /&gt;
					wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..ability..&amp;quot; &amp;quot;..attack..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
					for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do&lt;br /&gt;
						if k~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
						wt = wt..impact_zone&lt;br /&gt;
					end&lt;br /&gt;
					wt = wt..&amp;quot; + &amp;quot;..weapon.abilities[ability][attack].impactpower..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.impactresistance~=&amp;quot;&amp;quot; then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Impact Resist&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..weapon.impactresistance&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helper function: Collects move mult and prepare move mult for display&lt;br /&gt;
local function collect_multipliers(ability)&lt;br /&gt;
	local move, prep_move = &amp;quot;&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack_name in ipairs(ability.order) do&lt;br /&gt;
		if ability[attack_name].movementmultiplier then&lt;br /&gt;
			if i ~= 1 then&lt;br /&gt;
				move = move..&amp;quot;/&amp;quot;&lt;br /&gt;
				prep_move = prep_move..&amp;quot;/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			move = move..(ability[attack_name].movementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			prep_move = prep_move..(ability[attack_name].preparemovementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return move,prep_move&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing speed penalties for each action&lt;br /&gt;
local function action_move_speed_penalty(weapon,is_header)&lt;br /&gt;
	if is_header then return [=[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]&amp;lt;/th&amp;gt;]=] end&lt;br /&gt;
&lt;br /&gt;
	local order = {} --reconstruct order to exclude the other ability group&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then order[#order+1] = ability end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability_name in ipairs(order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		if ability_name==&amp;quot;Other&amp;quot; or ability_name==&amp;quot;Block&amp;quot; then wt = wt..ability_name..&amp;quot; Actions&amp;quot; else wt = wt..ability_name..&amp;quot; Attacks&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		local movement_multiplier,prepare_movement_multiplier = collect_multipliers(weapon.abilities[ability_name])&lt;br /&gt;
		if #prepare_movement_multiplier &amp;gt; 3 then&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
			..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..prepare_movement_multiplier&lt;br /&gt;
		else&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing base Physical and Magical damage, if present.&lt;br /&gt;
local function damage_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:25%&amp;quot;&amp;gt;Damage on Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	wt = wt..inline_block(weapon,&amp;quot;Physical Base Weapon Damage&amp;quot;)&lt;br /&gt;
			..inline_block(weapon,&amp;quot;Magical Base Weapon Damage&amp;quot;)&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---These stats get their own table cells, as such they are excluded from the stats cell to avoid needless redundancy&lt;br /&gt;
local exclude_stat = {[&amp;quot;Physical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Magical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Armor Rating&amp;quot;]=true,[&amp;quot;Magical Resistance&amp;quot;]=true,[&amp;quot;Move Speed&amp;quot;]=true}&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing any stats not covered by the rest of the row&lt;br /&gt;
local function stats(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:12%&amp;quot;&amp;gt;Stats&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,stat in ipairs(weapon.stats.order) do&lt;br /&gt;
		if not exclude_stat[stat] then&lt;br /&gt;
			wt = wt..inline_block(weapon,stat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing animation times for ranges weapons&lt;br /&gt;
local function animation_time(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Animation Times&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local windup = &amp;quot;&amp;lt;br&amp;gt;Windup:&amp;quot;&lt;br /&gt;
	local finish = &amp;quot;&amp;lt;br&amp;gt;Finish:&amp;quot;&lt;br /&gt;
	local reload = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Reload&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Attack 1&amp;quot;] then&lt;br /&gt;
		-- windup = windup..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Windup&lt;br /&gt;
		-- finish = finish..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Finish&lt;br /&gt;
	else&lt;br /&gt;
		windup = &amp;quot;&amp;quot;&lt;br /&gt;
		finish = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Other&amp;quot;] then&lt;br /&gt;
		-- reload = reload..weapon.animationtimes[&amp;quot;Other&amp;quot;].Reload&lt;br /&gt;
	else&lt;br /&gt;
		reload = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if #windup == 0 and #finish == 0 and #reload == 0 then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attack&amp;lt;/span&amp;gt;&amp;quot;..windup..finish..reload..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing hitslow for each attack&lt;br /&gt;
local function slowdown_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Slowdown On Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local ability_type = &amp;quot;Primary&amp;quot;&lt;br /&gt;
	if weapon.slottype == &amp;quot;Off-Hand&amp;quot; then ability_type = &amp;quot;Secondary&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Hitslow&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	local duration = &amp;quot; for &amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
				or weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Bonus&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then duration = duration..&amp;quot;/&amp;quot; end&lt;br /&gt;
		duration = duration..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Duration&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..duration..&amp;quot;s&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the projectile&#039;s initial speed&lt;br /&gt;
local function initial_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Initial Projectile Speed (m/s)&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.initialspeed..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the hitbox image resized by inventory size&lt;br /&gt;
local function hitbox(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Hitbox + Impact Resist&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;[[File:&amp;quot;..weapon.name..&amp;quot; Hitbox.png|link=|&amp;quot;..size(weapon)..&amp;quot;]]&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helps format a string containing either Combo Damage or damagetype values&lt;br /&gt;
local function format_combo(weapon,ability,value)&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability][attack][value] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing ability combo values per attack&lt;br /&gt;
local function combo(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Combo&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	if weapon.abilities[&amp;quot;Primary&amp;quot;] then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Secondary&amp;quot;] then&lt;br /&gt;
		if #wt ~= 0 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Secondary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Special&amp;quot;] then&lt;br /&gt;
		if wt:match(&amp;quot;Primary&amp;quot;) or wt:match(&amp;quot;Secondary&amp;quot;) then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Special Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function determine_table_type(weapon)&lt;br /&gt;
	if weapon.args then&lt;br /&gt;
		if weapon.args[1] == &amp;quot;Shield&amp;quot; then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif (weapon.args[1]):lower():match(&amp;quot;bow&amp;quot;) or weapon.args[1] == &amp;quot;Firearm&amp;quot; or weapon.args[1] == &amp;quot;Throwable&amp;quot; then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	elseif weapon.types then&lt;br /&gt;
		if weapon.types.Shield then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm or weapon.types.Throwable then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;Default&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Determines which table data goes into the table row&lt;br /&gt;
local row_type = {&lt;br /&gt;
	[&amp;quot;Shield&amp;quot;] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},&lt;br /&gt;
	[&amp;quot;Ranged&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,action_move_speed_penalty,slowdown_on_hit,initial_speed},&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing weapon information,&lt;br /&gt;
local function row(weapon, is_header)&lt;br /&gt;
	is_header = is_header or false&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,content in ipairs(row_type[determine_table_type(weapon)]) do&lt;br /&gt;
		wt[#wt+1] = content(weapon,is_header)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Returns a predetermined weapon list&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	if not (frame.args and frame.args[1] and frame.args[2]) then return {} end&lt;br /&gt;
	if not (WD[frame.args[1]] and WD[frame.args[1]][frame.args[2]]) then return {} end&lt;br /&gt;
&lt;br /&gt;
	return WD[frame.args[1]][frame.args[2]]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	local item_list = get_list(frame)&lt;br /&gt;
	if count(item_list) == 0 then return &amp;quot;&amp;lt;span style=&#039;font-size:24pt&#039;&amp;gt;There are currently no items of this type.&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = [[&amp;lt;table cellspacing=&amp;quot;0&amp;quot; class=&amp;quot;wikitable sortable jquery-tablesorter&amp;quot; style=&amp;quot;text-align:center; vertical-align:middle;&amp;quot;&amp;gt;]]&lt;br /&gt;
	wt[#wt+1] = row(frame, true)&lt;br /&gt;
	for _,item_name in ipairs(item_list) do&lt;br /&gt;
		wt[#wt+1] = row(WD.Weapon[item_name])&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[###########################################################################&lt;br /&gt;
                Functions for individual weapon pages&lt;br /&gt;
#############################################################################]=]&lt;br /&gt;
local function p_projectile(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.initialspeed ~= &amp;quot;&amp;quot; then &lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Projectile&amp;lt;/h2&amp;gt;Initial Speed: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.initialspeed)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.piercecount ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;Pierce: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.piercecount)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_damage_falloff(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.damagefalloff == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Damage Falloff&amp;lt;/h2&amp;gt;%s has damage falloff depending on airtime of the projectile.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Projectile falloff damage chart:&amp;lt;br&amp;gt;X: Air time (seconds)&amp;lt;br&amp;gt;Y: %% Damage&amp;quot;,weapon_name,weapon.initialspeed)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:500px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = weapon.damagefalloff&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;Learn more at [[Damage_Calculation#Projectile_Falloff|Projectile Falloff]]&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_hitbox(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.hitbox or not weapon.hitbox.height then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Hitbox&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;Height: &amp;quot;..weapon.hitbox.height&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Width: &amp;quot;..weapon.hitbox.width&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Depth: &amp;quot;..weapon.hitbox.depth&lt;br /&gt;
&lt;br /&gt;
	local primary_attack = weapon.abilities.Primary and weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;] or {}&lt;br /&gt;
	local secondary_attack = weapon.abilities.Secondary and weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;] or {}&lt;br /&gt;
	if #primary_attack == 0 and #secondary_attack == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and has primary or secondary attacks&lt;br /&gt;
	if not (weapon.types[&amp;quot;Bow&amp;quot;] or weapon.types[&amp;quot;Crossow&amp;quot;] or weapon.types[&amp;quot;ThrowableStuff&amp;quot;])&lt;br /&gt;
			and (primary_attack.impactzones or secondary_attack.impactzones) then&lt;br /&gt;
		local impact_zones = primary_attack.impactzones or secondary_attack.impactzones&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;The weapon&#039;s hitbox doesn&#039;t necessarily determine a weapon&#039;s reach due to different arm extension during attack animations. Thus, shorter weapons can have better reach than longer weapons.&amp;lt;br&amp;gt;[[Impact Zones]] of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;:&amp;lt;div style=&#039;display:flex;align-items:center&#039;&amp;gt;&amp;lt;div&amp;gt;[[File:&amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot; Hitbox.png|x300px]]&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Green Zone deals &amp;quot;&lt;br /&gt;
		wt[#wt+1] = impact_zones[1]&lt;br /&gt;
		wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if impact_zones[2] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:orange&#039;&amp;gt;Orange Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[2]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if impact_zones[3] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Red Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[3]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;&#039;Disclaimer&#039;&#039;&#039;: The impact zone&#039;s locations are an approximation. The real hitboxes may be slightly larger, closer together, and/or follow the weapon&#039;s shape more closely. Image is not able to be updated automatically. If it is outdated, please report it to the [https://discord.gg/KbsxgACkFS Wiki Discord].&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if primary_attack.impactpower or secondary_attack.impactpower then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Impact Power&#039;&#039;&#039; of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;: &amp;quot;&lt;br /&gt;
		wt[#wt+1] = tostring(primary_attack.impactpower or secondary_attack.impactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Impact power helps with breaking crates, barricades or any other breakables in game. The higher the impact power the faster something breaks.&amp;lt;br&amp;gt;Impact power also affects shield blocking. If the attacking weapon&#039;s impact power is higher than shield&#039;s impact power, the shield holder staggers, and vice versa.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function list_to_string(l,separator)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(l) do&lt;br /&gt;
		if i~=1 then ret = ret..separator end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo_format(wt,attacks,type)&lt;br /&gt;
	if type == &amp;quot;Other&amp;quot; or type == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
	local first = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = type&lt;br /&gt;
	wt[#wt+1] = &amp;quot; Attack: &amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; else first = name end&lt;br /&gt;
		wt[#wt+1] = attacks[name].damagetype&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; dealing &amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = attacks[name].combodamage&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; damage on each swing, with impact zone: &amp;quot;&lt;br /&gt;
	wt[#wt+1] = list_to_string(attacks[first].impactzones, &amp;quot;/&amp;quot;)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function attack_animation_times(wt,weapon,ability_name,attack_name)&lt;br /&gt;
	local attack = weapon.animationtimes[ability_name][attack_name]&lt;br /&gt;
	if attack[&amp;quot;Windup&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                           attack[&amp;quot;Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Hit&amp;quot;]               then wt[#wt+1] = string.format(&amp;quot;Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                              attack[&amp;quot;Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Windup&amp;quot;]     then wt[#wt+1] = string.format(&amp;quot;Second Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                    attack[&amp;quot;Second Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Hit&amp;quot;]        then wt[#wt+1] = string.format(&amp;quot;Second Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                       attack[&amp;quot;Second Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Recover&amp;quot;]           then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Recover: %s&amp;lt;br&amp;gt;&amp;quot;,           attack[&amp;quot;Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Alternate Recover&amp;quot;] then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Alternate Recover: %s&amp;lt;br&amp;gt;&amp;quot;, attack[&amp;quot;Alternate Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Finish&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Finish: %s&amp;lt;br&amp;gt;&amp;quot;,            attack[&amp;quot;Finish&amp;quot;]) end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function animation_format(wt, weapon, ability)&lt;br /&gt;
	if ability == &amp;quot;Other&amp;quot; or ability == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	for i, ability_name in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		wt[#wt+1] = ability..&amp;quot; Combo: &amp;quot;..ability_name..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		attack_animation_times(wt,weapon,ability,ability_name)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_combo(wt, page_name)&lt;br /&gt;
	local weapon = WD.Weapon[page_name]&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and isn&#039;t shield, or at least isn&#039;t a Lantern Shield&lt;br /&gt;
	if not (weapon.types.Bow or weapon.types.Crossow or weapon.types.ThrowableStuff)&lt;br /&gt;
		and (not weapon.types.Shield or (weapon.name):match(&amp;quot;Lantern Shield&amp;quot;)) then&lt;br /&gt;
&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Attack Animation Time&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
			combo_format(wt,weapon.abilities[name],name)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;b&amp;gt;Attack Times&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
				animation_format(wt,weapon,name)&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;Reference [[Attack Times]] for a glossary of the terms used above.&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_block(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.impactresistance or weapon.impactresistance == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Block&amp;lt;/h2&amp;gt;%s has a block ability with an impact resistance of &amp;lt;b&amp;gt;%s&amp;lt;/b&amp;gt;.&amp;lt;br&amp;gt;To learn more about impact resistance see [[Impact Power]].&amp;quot;, weapon.name, weapon.impactresistance)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_action_move_slow_penalty(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Action Movement Slow&amp;lt;/h2&amp;gt;When a player performs certain actions, such as attacking or defending themselves, they move slower.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		for _,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
			-- There&#039;s a bug where Riposte values are recorded within &amp;quot;Other&amp;quot; abilities&lt;br /&gt;
			-- Since ripose is covered in Special abilities, we will skip it here&lt;br /&gt;
			if attack == &amp;quot;Riposte&amp;quot; then break end&lt;br /&gt;
&lt;br /&gt;
			local movespeedadd = weapon.abilities[ability][attack].effects&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
								or &amp;quot;&amp;quot;&lt;br /&gt;
			local movement_multiplier = weapon.abilities[ability][attack].movementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
			local prepare_movement_multiplier = weapon.abilities[ability][attack].preparemovementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if ability == &amp;quot;Other&amp;quot; then wt[#wt+1] = attack else wt[#wt+1] = ability end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if #prepare_movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = prepare_movement_multiplier&lt;br /&gt;
			elseif #movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
			else&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movespeedadd&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			-- Everything except attacks within Other has invariant move mults&lt;br /&gt;
			-- So after processessing the first attack, we break the loop&lt;br /&gt;
			if ability ~= &amp;quot;Other&amp;quot; then break end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_artifact(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.isartifact or weapon.artifactname == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Artifact&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;%s has a powerful [[Artifacts|Artifact]] named [[%s]].&amp;lt;/p&amp;gt;&amp;quot;, weapon.name, weapon.artifactname)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = &amp;quot;&amp;quot;&lt;br /&gt;
	if active then&lt;br /&gt;
		class = &amp;quot; class=&#039;selected-tab tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		class = &amp;quot; class=&#039;tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:inline-block&#039; %s data-tabid=&#039;%s&#039; data-tab=&#039;%s&#039; title=&#039;%s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,class,tabid,title,title,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reference_table(wt,table_type)&lt;br /&gt;
	local types_order = {&amp;quot;Sword&amp;quot;,&amp;quot;Dagger&amp;quot;,&amp;quot;Mace&amp;quot;,&amp;quot;Polearm&amp;quot;,&amp;quot;Axe&amp;quot;,&amp;quot;Throwable&amp;quot;,&amp;quot;Bow&amp;quot;,&amp;quot;Crossbow&amp;quot;,&amp;quot;Firearm&amp;quot;,&amp;quot;MagicStuff&amp;quot;,&amp;quot;Shield&amp;quot;,&amp;quot;LightSource&amp;quot;}&lt;br /&gt;
	local localization = {&lt;br /&gt;
		[&amp;quot;Sword&amp;quot;]        = &amp;quot;Swords&amp;quot;,&lt;br /&gt;
		[&amp;quot;Dagger&amp;quot;]       = &amp;quot;Daggers&amp;quot;,&lt;br /&gt;
		[&amp;quot;Mace&amp;quot;]         = &amp;quot;Maces&amp;quot;,&lt;br /&gt;
		[&amp;quot;Polearm&amp;quot;]      = &amp;quot;Polearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;Axe&amp;quot;]          = &amp;quot;Axes&amp;quot;,&lt;br /&gt;
		[&amp;quot;Throwable&amp;quot;]    = &amp;quot;Throwables&amp;quot;,&lt;br /&gt;
		[&amp;quot;Bow&amp;quot;]          = &amp;quot;Bows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Crossbow&amp;quot;]     = &amp;quot;Crossbows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Firearm&amp;quot;]      = &amp;quot;Firearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;MagicStuff&amp;quot;]   = &amp;quot;Magic Stuff&amp;quot;,&lt;br /&gt;
		[&amp;quot;Shield&amp;quot;]       = &amp;quot;Shields&amp;quot;,&lt;br /&gt;
		[&amp;quot;LightSource&amp;quot;]  = &amp;quot;Light Sources&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	local separator = &amp;quot;]] • [[&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:100%;text-align:center;vertical-align:middle;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for _,weapon_type in ipairs(types_order) do&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%&#039;&amp;gt;[[Weapons#%s|%s]]&amp;lt;/td&amp;gt;&amp;lt;td style=&#039;text-align-last:left;padding:10px 25px&#039;&amp;gt;&amp;quot;,localization[weapon_type],localization[weapon_type])&lt;br /&gt;
		if WD[weapon_type][table_type] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;[[&amp;quot;&lt;br /&gt;
			for i,v in ipairs(WD[weapon_type][table_type]) do&lt;br /&gt;
				if i~=1 then wt[#wt+1] = separator end&lt;br /&gt;
				wt[#wt+1] = v&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;]]&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.references()&lt;br /&gt;
	local table_types = {&amp;quot;Uncraftable&amp;quot;,&amp;quot;Craftable&amp;quot;,&amp;quot;Artifact&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;h2&amp;gt;References&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = tab_toggle(table_type,table_type..&amp;quot; Weapons&amp;quot;,i==1,&amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;&amp;quot;..table_type..&amp;quot;-data&#039;&amp;quot;&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;gt;&amp;quot; else wt[#wt+1] = &amp;quot;style=&#039;display:none&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		reference_table(wt,table_type)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local page_name = f.args.name&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	p_projectile(wt,page_name)&lt;br /&gt;
	p_damage_falloff(wt,page_name)&lt;br /&gt;
	p_hitbox(wt,page_name)&lt;br /&gt;
	p_combo(wt,page_name)&lt;br /&gt;
	p_block(wt,page_name)&lt;br /&gt;
	p_action_move_slow_penalty(wt,page_name)&lt;br /&gt;
	p_artifact(wt,page_name)&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on wiki with&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Crossbow&amp;quot;,&amp;quot;Uncraftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Shield&amp;quot;,&amp;quot;Craftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Polearm&amp;quot;,&amp;quot;Artifact&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={name=&amp;quot;Rapier&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=66306</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=66306"/>
		<updated>2026-04-15T21:26:59Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added some checks to allow for a natural-looking infobox on unarmed pages.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_index(item,rarity)&lt;br /&gt;
	return 1 + tonumber(rarity) - item.rarities[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, item)&lt;br /&gt;
	if not item.stats or count(item.stats.order) == 0 or item.numenchants[tonumber(args.rarity)] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(item.stats.order) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,item.stats[(stat_name):lower()][stat_index(item,args.rarity)] or item.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,item.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.haspassive or item.isartifact then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,item.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i ~= 1 then ret = ret..&amp;quot;, &amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	local i = 1&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if i ~= 1 then ret = ret..&amp;quot;, &amp;quot; end&lt;br /&gt;
		if v == type then&lt;br /&gt;
			ret = ret..k&lt;br /&gt;
		end&lt;br /&gt;
		i = i + 1&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	if count(weapon.hitbox) ~= 0 then&lt;br /&gt;
		rows[#rows+1] = hline()&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.invwidth ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		rows[#rows+1] = hline()&lt;br /&gt;
		rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
		if weapon.maxcount ~= 1 then&lt;br /&gt;
			rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
		end&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[index])&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,weapon.ap[index])&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[index])&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.flavortext ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		rows[#rows+1] = hline()&lt;br /&gt;
		rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Armor Type&amp;quot;,type_list(armor.types,&amp;quot;Armor&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,armor.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,accessory)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,accessory.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	if misc.maxcount ~= 1 then&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,misc.maxcount)&lt;br /&gt;
	end&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Max Contents&amp;quot;,misc.maxcontentscount)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active == rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Bloodthirst&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Golden Scarf&amp;quot;,category=&amp;quot;Armor&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Plate Pants&amp;quot;,category=&amp;quot;Armor&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Grimsmile Ring&amp;quot;,category=&amp;quot;Accessory&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Diamond&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Gold Coin Chest&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Quiver&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=66305</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=66305"/>
		<updated>2026-04-15T21:23:12Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Forgot you can&amp;#039;t use native counting with our metatables.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_index(item,rarity)&lt;br /&gt;
	return 1 + tonumber(rarity) - item.rarities[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, item)&lt;br /&gt;
	if not item.stats or count(item.stats.order) == 0 or item.numenchants[tonumber(args.rarity)] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(item.stats.order) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,item.stats[(stat_name):lower()][stat_index(item,args.rarity)] or item.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,item.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.haspassive or item.isartifact then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,item.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i ~= 1 then ret = ret..&amp;quot;, &amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	local i = 1&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if i ~= 1 then ret = ret..&amp;quot;, &amp;quot; end&lt;br /&gt;
		if v == type then&lt;br /&gt;
			ret = ret..k&lt;br /&gt;
		end&lt;br /&gt;
		i = i + 1&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	if count(weapon.hitbox) ~= 0 then&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	if weapon.maxcount ~= 1 then&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
	end&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,weapon.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Armor Type&amp;quot;,type_list(armor.types,&amp;quot;Armor&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,armor.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,accessory)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,accessory.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	if misc.maxcount ~= 1 then&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,misc.maxcount)&lt;br /&gt;
	end&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Max Contents&amp;quot;,misc.maxcontentscount)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active == rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Bloodthirst&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Golden Scarf&amp;quot;,category=&amp;quot;Armor&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Plate Pants&amp;quot;,category=&amp;quot;Armor&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Grimsmile Ring&amp;quot;,category=&amp;quot;Accessory&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Diamond&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Gold Coin Chest&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Quiver&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=66304</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=66304"/>
		<updated>2026-04-15T21:21:58Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added existence check on hitbox values.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_index(item,rarity)&lt;br /&gt;
	return 1 + tonumber(rarity) - item.rarities[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, item)&lt;br /&gt;
	if not item.stats or count(item.stats.order) == 0 or item.numenchants[tonumber(args.rarity)] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(item.stats.order) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,item.stats[(stat_name):lower()][stat_index(item,args.rarity)] or item.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,item.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.haspassive or item.isartifact then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,item.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i ~= 1 then ret = ret..&amp;quot;, &amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	local i = 1&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if i ~= 1 then ret = ret..&amp;quot;, &amp;quot; end&lt;br /&gt;
		if v == type then&lt;br /&gt;
			ret = ret..k&lt;br /&gt;
		end&lt;br /&gt;
		i = i + 1&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	if #weapon.hitbox ~= 0 then&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	if weapon.maxcount ~= 1 then&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
	end&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,weapon.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Armor Type&amp;quot;,type_list(armor.types,&amp;quot;Armor&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,armor.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,accessory)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,accessory.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	if misc.maxcount ~= 1 then&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,misc.maxcount)&lt;br /&gt;
	end&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Max Contents&amp;quot;,misc.maxcontentscount)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active == rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Bloodthirst&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Golden Scarf&amp;quot;,category=&amp;quot;Armor&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Plate Pants&amp;quot;,category=&amp;quot;Armor&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Grimsmile Ring&amp;quot;,category=&amp;quot;Accessory&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Diamond&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Gold Coin Chest&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Quiver&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:WeaponPageFlavorText&amp;diff=66303</id>
		<title>Template:WeaponPageFlavorText</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:WeaponPageFlavorText&amp;diff=66303"/>
		<updated>2026-04-15T21:16:02Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added something for unarmed.  Hopefully temporary.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;{{#switch:{{lc:{{{1}}}}}&lt;br /&gt;
	|unarmed=&amp;lt;h2&amp;gt;Unarmed&amp;lt;/h2&amp;gt;When without a weapon you can still throw punches to deal small amounts of damage.  Unarmed attacks have low range, low damage, and moderate attack speed.&lt;br /&gt;
	|sword=&amp;lt;h2&amp;gt;Swords&amp;lt;/h2&amp;gt;[[Swords]] are versatile weapons that can be used close to mid range. Swords have a variety of attack patterns, making them useful in most situations.&lt;br /&gt;
	|mace=&amp;lt;h2&amp;gt;Maces&amp;lt;/h2&amp;gt; [[Maces]] are heavy, blunt weapons used for close combat. They deal significant damage and have slower attack speed compared to lighter weapons such as swords.&lt;br /&gt;
	|dagger=&amp;lt;h2&amp;gt;Daggers&amp;lt;/h2&amp;gt; [[Daggers]] are fast, light weapons used for quick, precise strikes. They deal lower damage compared to heavier weapons such as swords or maces, but make up for it with increased speed and agility.&lt;br /&gt;
	|polearm=&amp;lt;h2&amp;gt;Polearms&amp;lt;/h2&amp;gt; [[Polearms]] are long-reaching weapons used for ranged combat. They have a longer reach than other weapons, allowing players to attack enemies from a safe distance. Polearms also have unique abilities or special attacks that allow players to control the battlefield and keep enemies at bay.&lt;br /&gt;
	|axe=&amp;lt;h2&amp;gt;Axes&amp;lt;/h2&amp;gt; [[Axes]] are heavy, chopping weapons used for devastating close combat. They typically deal high damage and have slower attack speed compared to lighter weapons such as swords or daggers with some exceptions.&lt;br /&gt;
	|bow=&amp;lt;h2&amp;gt;Bows&amp;lt;/h2&amp;gt; [[Bows]] are ranged weapons used for long-distance combat. They deal moderate to high damage and have a moderate attack speed, allowing players to take down enemies from a safe distance.&lt;br /&gt;
	|crossbow=&amp;lt;h2&amp;gt;Crossbows&amp;lt;/h2&amp;gt; [[Crossbows]] are ranged weapons that trade fire rate for increased damage and accuracy. They deal high damage and have slower attack speed compared to other ranged weapons such as bows, but they make up for it with increased accuracy and power.&lt;br /&gt;
	|firearm=&amp;lt;h2&amp;gt;Firearms&amp;lt;/h2&amp;gt; [[Firearms]] are ranged weapons that further trade fire rate for increased damage.  After every shot firearms must be reloaded, but what the weapon loses in speed it makes up for with a large burst of damage.&lt;br /&gt;
	|magicstuff=&amp;lt;h2&amp;gt;Magical Weapons&amp;lt;/h2&amp;gt; [[Magic Stuff|Magical Weapons]] are ranged and melee weapons that allow player to cast spells depending on their classes. They deal high magic damage with the expense of casting time and spell capacity. Some can be used as physical weapons aswell.&lt;br /&gt;
	|shield=&amp;lt;h2&amp;gt;Shields&amp;lt;/h2&amp;gt; [[Shields]] are a piece of defensive equipment used to provide protection to the player character. It is held in front of the player to deflect incoming attacks and reduce damage taken. Shields have different shapes, sizes, and designs, and may offer various advantages, such as increased defense, the ability to block certain types of attacks, or special abilities. Some shields may be heavier and slower to use, while others are lighter and quicker to maneuver.&lt;br /&gt;
	|musicalinstrument=&amp;lt;h2&amp;gt;Musical Instruments&amp;lt;/h2&amp;gt; [[Musical Instruments]] are used by [[Bard]] to play songs. While they are considered [[Utility|utilities]], some of them can be used as a physical weapon.&lt;br /&gt;
	|throwable&lt;br /&gt;
	|throwablestuff=&amp;lt;h2&amp;gt;Throwable Weapons&amp;lt;/h2&amp;gt; [[Throwables]] can be used by a variety of classes to harass from a distance.  They have moderate reach and speed, and can accumulate a respectable amount of damage.&lt;br /&gt;
	|#default=&amp;lt;span style=&amp;quot;color:red; font-size:20px&amp;quot;&amp;gt;[[Template:WeaponPageFlavorText]] couldn&#039;t match the provided weapon type. Follow the link for documentation to add the type.&amp;lt;/span&amp;gt;}}&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;{{WeaponPageFlavorText|Sord}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{WeaponPageFlavorText|Sord}}&lt;br /&gt;
&amp;lt;pre&amp;gt;{{WeaponPageFlavorText|Sword}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{WeaponPageFlavorText|Sword}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=66301</id>
		<title>Module:Weapon</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=66301"/>
		<updated>2026-04-15T21:03:15Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Removing damage falloff until parsing order is fixed.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local WD = mw.loadJsonData(&amp;quot;Data:Weapon.json&amp;quot;) --Global var holding tables from Data:Weapon.json&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
---Get the item&#039;s size in pixels&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Weapon.json don&#039;t work with next() or the # operator&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
local function color_values(weapon, values)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(weapon.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..weapon.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(weapon.rarities) do&lt;br /&gt;
		wt = wt..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment&lt;br /&gt;
local function inline_block(weapon,stat)&lt;br /&gt;
	if weapon.stats[(stat):lower()] == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:inline-block;vertical-align:top;margin:0 15px 0 15px&#039;&amp;gt;&amp;lt;b style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..stat..&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[(stat):lower()])..&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell for the iconbox&lt;br /&gt;
local function name(item, is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local color = &amp;quot;2&amp;quot;&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local icon_amount = &amp;quot;&amp;quot;&lt;br /&gt;
	if item.maxammocount &amp;gt; 0 then&lt;br /&gt;
		icon_amount = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxammocount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..icon_amount&lt;br /&gt;
			..&amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats the weapon&#039;s list of classes into a table cell of classes separated by linebreaks&lt;br /&gt;
local function classes(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Class&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, class in ipairs(weapon.classes) do&lt;br /&gt;
		wt = wt..newline..class&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end -- Subsequent loops will prefix a linebreak&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of slottype and handtype&lt;br /&gt;
local function slot_type(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Slot&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.slottype..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..weapon.handtype..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of move speeds&lt;br /&gt;
local function move_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Movement Speed&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[&amp;quot;move speed&amp;quot;])..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of Armor rating and Magical Resistance&lt;br /&gt;
local function armor_rating(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Armor Rating&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..inline_block(weapon,&amp;quot;Armor Rating&amp;quot;)..inline_block(weapon,&amp;quot;Magical Resistance&amp;quot;)..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function impact_zones(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Impact Zones + Impact Power&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt= &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then&lt;br /&gt;
			for j,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
				if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then&lt;br /&gt;
					wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..ability..&amp;quot; &amp;quot;..attack..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
					for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do&lt;br /&gt;
						if k~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
						wt = wt..impact_zone&lt;br /&gt;
					end&lt;br /&gt;
					wt = wt..&amp;quot; + &amp;quot;..weapon.abilities[ability][attack].impactpower..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.impactresistance~=&amp;quot;&amp;quot; then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Impact Resist&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..weapon.impactresistance&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helper function: Collects move mult and prepare move mult for display&lt;br /&gt;
local function collect_multipliers(ability)&lt;br /&gt;
	local move, prep_move = &amp;quot;&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack_name in ipairs(ability.order) do&lt;br /&gt;
		if ability[attack_name].movementmultiplier then&lt;br /&gt;
			if i ~= 1 then&lt;br /&gt;
				move = move..&amp;quot;/&amp;quot;&lt;br /&gt;
				prep_move = prep_move..&amp;quot;/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			move = move..(ability[attack_name].movementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			prep_move = prep_move..(ability[attack_name].preparemovementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return move,prep_move&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing speed penalties for each action&lt;br /&gt;
local function action_move_speed_penalty(weapon,is_header)&lt;br /&gt;
	if is_header then return [=[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]&amp;lt;/th&amp;gt;]=] end&lt;br /&gt;
&lt;br /&gt;
	local order = {} --reconstruct order to exclude the other ability group&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then order[#order+1] = ability end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability_name in ipairs(order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		if ability_name==&amp;quot;Other&amp;quot; or ability_name==&amp;quot;Block&amp;quot; then wt = wt..ability_name..&amp;quot; Actions&amp;quot; else wt = wt..ability_name..&amp;quot; Attacks&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		local movement_multiplier,prepare_movement_multiplier = collect_multipliers(weapon.abilities[ability_name])&lt;br /&gt;
		if #prepare_movement_multiplier &amp;gt; 3 then&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
			..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..prepare_movement_multiplier&lt;br /&gt;
		else&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing base Physical and Magical damage, if present.&lt;br /&gt;
local function damage_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:25%&amp;quot;&amp;gt;Damage on Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	wt = wt..inline_block(weapon,&amp;quot;Physical Base Weapon Damage&amp;quot;)&lt;br /&gt;
			..inline_block(weapon,&amp;quot;Magical Base Weapon Damage&amp;quot;)&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---These stats get their own table cells, as such they are excluded from the stats cell to avoid needless redundancy&lt;br /&gt;
local exclude_stat = {[&amp;quot;Physical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Magical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Armor Rating&amp;quot;]=true,[&amp;quot;Magical Resistance&amp;quot;]=true,[&amp;quot;Move Speed&amp;quot;]=true}&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing any stats not covered by the rest of the row&lt;br /&gt;
local function stats(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:12%&amp;quot;&amp;gt;Stats&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,stat in ipairs(weapon.stats.order) do&lt;br /&gt;
		if not exclude_stat[stat] then&lt;br /&gt;
			wt = wt..inline_block(weapon,stat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing animation times for ranges weapons&lt;br /&gt;
local function animation_time(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Animation Times&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local windup = &amp;quot;&amp;lt;br&amp;gt;Windup:&amp;quot;&lt;br /&gt;
	local finish = &amp;quot;&amp;lt;br&amp;gt;Finish:&amp;quot;&lt;br /&gt;
	local reload = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Reload&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Attack 1&amp;quot;] then&lt;br /&gt;
		-- windup = windup..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Windup&lt;br /&gt;
		-- finish = finish..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Finish&lt;br /&gt;
	else&lt;br /&gt;
		windup = &amp;quot;&amp;quot;&lt;br /&gt;
		finish = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Other&amp;quot;] then&lt;br /&gt;
		-- reload = reload..weapon.animationtimes[&amp;quot;Other&amp;quot;].Reload&lt;br /&gt;
	else&lt;br /&gt;
		reload = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if #windup == 0 and #finish == 0 and #reload == 0 then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attack&amp;lt;/span&amp;gt;&amp;quot;..windup..finish..reload..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing hitslow for each attack&lt;br /&gt;
local function slowdown_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Slowdown On Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local ability_type = &amp;quot;Primary&amp;quot;&lt;br /&gt;
	if weapon.slottype == &amp;quot;Off-Hand&amp;quot; then ability_type = &amp;quot;Secondary&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Hitslow&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	local duration = &amp;quot; for &amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
				or weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Bonus&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then duration = duration..&amp;quot;/&amp;quot; end&lt;br /&gt;
		duration = duration..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Duration&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..duration..&amp;quot;s&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the projectile&#039;s initial speed&lt;br /&gt;
local function initial_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Initial Projectile Speed (m/s)&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.initialspeed..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the hitbox image resized by inventory size&lt;br /&gt;
local function hitbox(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Hitbox + Impact Resist&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;[[File:&amp;quot;..weapon.name..&amp;quot; Hitbox.png|link=|&amp;quot;..size(weapon)..&amp;quot;]]&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helps format a string containing either Combo Damage or damagetype values&lt;br /&gt;
local function format_combo(weapon,ability,value)&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability][attack][value] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing ability combo values per attack&lt;br /&gt;
local function combo(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Combo&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	if weapon.abilities[&amp;quot;Primary&amp;quot;] then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Secondary&amp;quot;] then&lt;br /&gt;
		if #wt ~= 0 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Secondary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Special&amp;quot;] then&lt;br /&gt;
		if wt:match(&amp;quot;Primary&amp;quot;) or wt:match(&amp;quot;Secondary&amp;quot;) then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Special Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function determine_table_type(weapon)&lt;br /&gt;
	if weapon.args then&lt;br /&gt;
		if weapon.args[1] == &amp;quot;Shield&amp;quot; then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif (weapon.args[1]):lower():match(&amp;quot;bow&amp;quot;) or weapon.args[1] == &amp;quot;Firearm&amp;quot; or weapon.args[1] == &amp;quot;Throwable&amp;quot; then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	elseif weapon.types then&lt;br /&gt;
		if weapon.types.Shield then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm or weapon.types.Throwable then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;Default&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Determines which table data goes into the table row&lt;br /&gt;
local row_type = {&lt;br /&gt;
	[&amp;quot;Shield&amp;quot;] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},&lt;br /&gt;
	[&amp;quot;Ranged&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,action_move_speed_penalty,slowdown_on_hit,initial_speed},&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing weapon information,&lt;br /&gt;
local function row(weapon, is_header)&lt;br /&gt;
	is_header = is_header or false&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,content in ipairs(row_type[determine_table_type(weapon)]) do&lt;br /&gt;
		wt[#wt+1] = content(weapon,is_header)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Returns a predetermined weapon list&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	if not (frame.args and frame.args[1] and frame.args[2]) then return {} end&lt;br /&gt;
	if not (WD[frame.args[1]] and WD[frame.args[1]][frame.args[2]]) then return {} end&lt;br /&gt;
&lt;br /&gt;
	return WD[frame.args[1]][frame.args[2]]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	local item_list = get_list(frame)&lt;br /&gt;
	if count(item_list) == 0 then return &amp;quot;&amp;lt;span style=&#039;font-size:24pt&#039;&amp;gt;There are currently no items of this type.&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = [[&amp;lt;table cellspacing=&amp;quot;0&amp;quot; class=&amp;quot;wikitable sortable jquery-tablesorter&amp;quot; style=&amp;quot;text-align:center; vertical-align:middle;&amp;quot;&amp;gt;]]&lt;br /&gt;
	wt[#wt+1] = row(frame, true)&lt;br /&gt;
	for _,item_name in ipairs(item_list) do&lt;br /&gt;
		wt[#wt+1] = row(WD.Weapon[item_name])&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[###########################################################################&lt;br /&gt;
                Functions for individual weapon pages&lt;br /&gt;
#############################################################################]=]&lt;br /&gt;
local function p_projectile(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.initialspeed ~= &amp;quot;&amp;quot; then &lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Projectile&amp;lt;/h2&amp;gt;Initial Speed: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.initialspeed)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.piercecount ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;Pierce: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.piercecount)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_damage_falloff(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.damagefalloff == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Damage Falloff&amp;lt;/h2&amp;gt;%s has damage falloff depending on airtime of the projectile.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Projectile falloff damage chart:&amp;lt;br&amp;gt;X: Air time (seconds)&amp;lt;br&amp;gt;Y: %% Damage&amp;quot;,weapon_name,weapon.initialspeed)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:500px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = weapon.damagefalloff&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;Learn more at [[Damage_Calculation#Projectile_Falloff|Projectile Falloff]]&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_hitbox(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.hitbox or not weapon.hitbox.height then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Hitbox&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;Height: &amp;quot;..weapon.hitbox.height&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Width: &amp;quot;..weapon.hitbox.width&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Depth: &amp;quot;..weapon.hitbox.depth&lt;br /&gt;
&lt;br /&gt;
	local primary_attack = weapon.abilities.Primary and weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;] or {}&lt;br /&gt;
	local secondary_attack = weapon.abilities.Secondary and weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;] or {}&lt;br /&gt;
	if #primary_attack == 0 and #secondary_attack == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and has primary or secondary attacks&lt;br /&gt;
	if not (weapon.types[&amp;quot;Bow&amp;quot;] or weapon.types[&amp;quot;Crossow&amp;quot;] or weapon.types[&amp;quot;ThrowableStuff&amp;quot;])&lt;br /&gt;
			and (primary_attack.impactzones or secondary_attack.impactzones) then&lt;br /&gt;
		local impact_zones = primary_attack.impactzones or secondary_attack.impactzones&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;The weapon&#039;s hitbox doesn&#039;t necessarily determine a weapon&#039;s reach due to different arm extension during attack animations. Thus, shorter weapons can have better reach than longer weapons.&amp;lt;br&amp;gt;[[Impact Zones]] of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;:&amp;lt;div style=&#039;display:flex;align-items:center&#039;&amp;gt;&amp;lt;div&amp;gt;[[File:&amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot; Hitbox.png|x300px]]&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Green Zone deals &amp;quot;&lt;br /&gt;
		wt[#wt+1] = impact_zones[1]&lt;br /&gt;
		wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if impact_zones[2] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:orange&#039;&amp;gt;Orange Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[2]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if impact_zones[3] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Red Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[3]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;&#039;Disclaimer&#039;&#039;&#039;: The impact zone&#039;s locations are an approximation. The real hitboxes may be slightly larger, closer together, and/or follow the weapon&#039;s shape more closely. Image is not able to be updated automatically. If it is outdated, please report it to the [https://discord.gg/KbsxgACkFS Wiki Discord].&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if primary_attack.impactpower or secondary_attack.impactpower then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Impact Power&#039;&#039;&#039; of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;: &amp;quot;&lt;br /&gt;
		wt[#wt+1] = tostring(primary_attack.impactpower or secondary_attack.impactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Impact power helps with breaking crates, barricades or any other breakables in game. The higher the impact power the faster something breaks.&amp;lt;br&amp;gt;Impact power also affects shield blocking. If the attacking weapon&#039;s impact power is higher than shield&#039;s impact power, the shield holder staggers, and vice versa.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function list_to_string(l,separator)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(l) do&lt;br /&gt;
		if i~=1 then ret = ret..separator end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo_format(wt,attacks,type)&lt;br /&gt;
	if type == &amp;quot;Other&amp;quot; or type == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
	local first = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = type&lt;br /&gt;
	wt[#wt+1] = &amp;quot; Attack: &amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; else first = name end&lt;br /&gt;
		wt[#wt+1] = attacks[name].damagetype&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; dealing &amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = attacks[name].combodamage&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; damage on each swing, with impact zone: &amp;quot;&lt;br /&gt;
	wt[#wt+1] = list_to_string(attacks[first].impactzones, &amp;quot;/&amp;quot;)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function attack_animation_times(wt,weapon,ability_name,attack_name)&lt;br /&gt;
	local attack = weapon.animationtimes[ability_name][attack_name]&lt;br /&gt;
	if attack[&amp;quot;Windup&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                           attack[&amp;quot;Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Hit&amp;quot;]               then wt[#wt+1] = string.format(&amp;quot;Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                              attack[&amp;quot;Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Windup&amp;quot;]     then wt[#wt+1] = string.format(&amp;quot;Second Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                    attack[&amp;quot;Second Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Hit&amp;quot;]        then wt[#wt+1] = string.format(&amp;quot;Second Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                       attack[&amp;quot;Second Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Recover&amp;quot;]           then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Recover: %s&amp;lt;br&amp;gt;&amp;quot;,           attack[&amp;quot;Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Alternate Recover&amp;quot;] then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Alternate Recover: %s&amp;lt;br&amp;gt;&amp;quot;, attack[&amp;quot;Alternate Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Finish&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Finish: %s&amp;lt;br&amp;gt;&amp;quot;,            attack[&amp;quot;Finish&amp;quot;]) end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function animation_format(wt, weapon, ability)&lt;br /&gt;
	if ability == &amp;quot;Other&amp;quot; or ability == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	for i, ability_name in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		wt[#wt+1] = ability..&amp;quot; Combo: &amp;quot;..ability_name..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		attack_animation_times(wt,weapon,ability,ability_name)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_combo(wt, page_name)&lt;br /&gt;
	local weapon = WD.Weapon[page_name]&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and isn&#039;t shield, or at least isn&#039;t a Lantern Shield&lt;br /&gt;
	if not (weapon.types.Bow or weapon.types.Crossow or weapon.types.ThrowableStuff)&lt;br /&gt;
		and (not weapon.types.Shield or (weapon.name):match(&amp;quot;Lantern Shield&amp;quot;)) then&lt;br /&gt;
&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Attack Animation Time&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
			combo_format(wt,weapon.abilities[name],name)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Attack Times&#039;&#039;&#039;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
				animation_format(wt,weapon,name)&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;Reference [[Attack Times]] for a glossary of the terms used above.&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_block(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.impactresistance then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Block&amp;lt;/h2&amp;gt;%s has a block ability with an impact resistance of &#039;&#039;&#039;%s&#039;&#039;&#039;.&amp;lt;br&amp;gt;To learn more about impact resistance see [[Impact Power]].&amp;quot;, weapon.name, weapon.impactresistance)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_action_move_slow_penalty(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Action Movement Slow&amp;lt;/h2&amp;gt;When a player performs certain actions, such as attacking or defending themselves, they move slower.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		for _,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
			-- There&#039;s a bug where Riposte values are recorded within &amp;quot;Other&amp;quot; abilities&lt;br /&gt;
			-- Since ripose is covered in Special abilities, we will skip it here&lt;br /&gt;
			if attack == &amp;quot;Riposte&amp;quot; then break end&lt;br /&gt;
&lt;br /&gt;
			local movespeedadd = weapon.abilities[ability][attack].effects&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
								or &amp;quot;&amp;quot;&lt;br /&gt;
			local movement_multiplier = weapon.abilities[ability][attack].movementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
			local prepare_movement_multiplier = weapon.abilities[ability][attack].preparemovementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if ability == &amp;quot;Other&amp;quot; then wt[#wt+1] = attack else wt[#wt+1] = ability end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if #prepare_movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = prepare_movement_multiplier&lt;br /&gt;
			elseif #movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
			else&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movespeedadd&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			-- Everything except attacks within Other has invariant move mults&lt;br /&gt;
			-- So after processessing the first attack, we break the loop&lt;br /&gt;
			if ability ~= &amp;quot;Other&amp;quot; then break end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_artifact(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.isartifact or weapon.artifactname == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Artifact&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;%s has a powerful [[Artifacts|Artifact]] named [[%s]].&amp;lt;/p&amp;gt;&amp;quot;, weapon.name, weapon.artifactname)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = &amp;quot;&amp;quot;&lt;br /&gt;
	if active then&lt;br /&gt;
		class = &amp;quot; class=&#039;selected-tab tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		class = &amp;quot; class=&#039;tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:inline-block&#039; %s data-tabid=&#039;%s&#039; data-tab=&#039;%s&#039; title=&#039;%s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,class,tabid,title,title,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reference_table(wt,table_type)&lt;br /&gt;
	local types_order = {&amp;quot;Sword&amp;quot;,&amp;quot;Dagger&amp;quot;,&amp;quot;Mace&amp;quot;,&amp;quot;Polearm&amp;quot;,&amp;quot;Axe&amp;quot;,&amp;quot;Throwable&amp;quot;,&amp;quot;Bow&amp;quot;,&amp;quot;Crossbow&amp;quot;,&amp;quot;Firearm&amp;quot;,&amp;quot;MagicStuff&amp;quot;,&amp;quot;Shield&amp;quot;,&amp;quot;LightSource&amp;quot;}&lt;br /&gt;
	local localization = {&lt;br /&gt;
		[&amp;quot;Sword&amp;quot;]        = &amp;quot;Swords&amp;quot;,&lt;br /&gt;
		[&amp;quot;Dagger&amp;quot;]       = &amp;quot;Daggers&amp;quot;,&lt;br /&gt;
		[&amp;quot;Mace&amp;quot;]         = &amp;quot;Maces&amp;quot;,&lt;br /&gt;
		[&amp;quot;Polearm&amp;quot;]      = &amp;quot;Polearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;Axe&amp;quot;]          = &amp;quot;Axes&amp;quot;,&lt;br /&gt;
		[&amp;quot;Throwable&amp;quot;]    = &amp;quot;Throwables&amp;quot;,&lt;br /&gt;
		[&amp;quot;Bow&amp;quot;]          = &amp;quot;Bows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Crossbow&amp;quot;]     = &amp;quot;Crossbows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Firearm&amp;quot;]      = &amp;quot;Firearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;MagicStuff&amp;quot;]   = &amp;quot;Magic Stuff&amp;quot;,&lt;br /&gt;
		[&amp;quot;Shield&amp;quot;]       = &amp;quot;Shields&amp;quot;,&lt;br /&gt;
		[&amp;quot;LightSource&amp;quot;]  = &amp;quot;Light Sources&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	local separator = &amp;quot;]] • [[&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:100%;text-align:center;vertical-align:middle;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for _,weapon_type in ipairs(types_order) do&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%&#039;&amp;gt;[[Weapons#%s|%s]]&amp;lt;/td&amp;gt;&amp;lt;td style=&#039;text-align-last:left;padding:10px 25px&#039;&amp;gt;&amp;quot;,localization[weapon_type],localization[weapon_type])&lt;br /&gt;
		if WD[weapon_type][table_type] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;[[&amp;quot;&lt;br /&gt;
			for i,v in ipairs(WD[weapon_type][table_type]) do&lt;br /&gt;
				if i~=1 then wt[#wt+1] = separator end&lt;br /&gt;
				wt[#wt+1] = v&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;]]&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.references()&lt;br /&gt;
	local table_types = {&amp;quot;Uncraftable&amp;quot;,&amp;quot;Craftable&amp;quot;,&amp;quot;Artifact&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;h2&amp;gt;References&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = tab_toggle(table_type,table_type..&amp;quot; Weapons&amp;quot;,i==1,&amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;&amp;quot;..table_type..&amp;quot;-data&#039;&amp;quot;&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;gt;&amp;quot; else wt[#wt+1] = &amp;quot;style=&#039;display:none&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		reference_table(wt,table_type)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local page_name = f.args.name&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	p_projectile(wt,page_name)&lt;br /&gt;
	--p_damage_falloff(wt,page_name)&lt;br /&gt;
	p_hitbox(wt,page_name)&lt;br /&gt;
	p_combo(wt,page_name)&lt;br /&gt;
	p_block(wt,page_name)&lt;br /&gt;
	p_action_move_slow_penalty(wt,page_name)&lt;br /&gt;
	p_artifact(wt,page_name)&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on wiki with&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Crossbow&amp;quot;,&amp;quot;Uncraftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Shield&amp;quot;,&amp;quot;Craftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Polearm&amp;quot;,&amp;quot;Artifact&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={name=&amp;quot;Rapier&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Misc&amp;diff=66104</id>
		<title>Module:Misc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Misc&amp;diff=66104"/>
		<updated>2026-04-15T20:52:04Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Not all pages need quest information.  Added a check.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Misc.json&amp;quot;)--Global var holding tables from Data:Misc.json&lt;br /&gt;
local insert = table.insert&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the size of the item in pixels for the iconbox&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Misc.json don&#039;t work with next() or the # operator&lt;br /&gt;
--- @param t table&lt;br /&gt;
--- @return number&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the type of the armor as a string&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function get_type(item)&lt;br /&gt;
	--Assumes that the item only has one type&lt;br /&gt;
	for key,_ in pairs(item.types) do&lt;br /&gt;
		--Scribunto uses meta tables so we can&#039;t use next() here&lt;br /&gt;
		return key&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create css/html wikitext for iconbox&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function iconbox(item)&lt;br /&gt;
	local color = 2&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;display:inline-flex;width:max-content;max-width:initial;flex-direction:column;align-items:center;flex-wrap:wrap;white-space:pre-wrap&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			..&amp;quot;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
				..&amp;quot;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..&amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxcount..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			..&amp;quot;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
--- @param values string|table&lt;br /&gt;
--- @param misc table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function color_values(values,misc)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(misc.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..misc.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--TODO this assumes i is the same as the color rating, this may result in a bug&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(misc.rarities) do&lt;br /&gt;
		wikitext = wikitext..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return an array whose values have been divided by the provided integer&lt;br /&gt;
--- @param values table&lt;br /&gt;
--- @param multiplier number&lt;br /&gt;
local function mult_array(values, multiplier)&lt;br /&gt;
	local result = {}&lt;br /&gt;
	for i, value in pairs(values) do&lt;br /&gt;
		if i ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
			result[i] = tonumber(string.format(&amp;quot;%.3f&amp;quot;, value * multiplier))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing misc information&lt;br /&gt;
--- @param misc table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function row(misc,table_type)&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..iconbox(misc))&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(misc.sellprices,misc))&lt;br /&gt;
	if table_type ~= &amp;quot;Container&amp;quot; then&lt;br /&gt;
		local max_stack_per_slotsize = misc.maxcount / (misc.invwidth * misc.invheight)&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(misc.ap,misc))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(mult_array(misc.sellprices,max_stack_per_slotsize),misc))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(mult_array(misc.ap,max_stack_per_slotsize),misc))&lt;br /&gt;
	else&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(misc.maxcontentscount,misc))&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a list of misc keys to be used in the table&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return table - strings&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	return MD[frame.args[1]] or {}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Return a table row of appropriate headers cells&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function table_header(frame)&lt;br /&gt;
	if (frame.args[1]):lower() == &amp;quot;container&amp;quot; then&lt;br /&gt;
		return [=[&amp;lt;table class=&amp;quot;wikitable sortable jquery-tablesorter stripedtable&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;color:#eee;background:transparent;text-align:center;vertical-align:middle&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sell Price&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Capacity&amp;lt;/th&amp;gt;]=]&lt;br /&gt;
	end&lt;br /&gt;
	-- Default misc table header&lt;br /&gt;
	return [=[&amp;lt;table class=&amp;quot;wikitable sortable jquery-tablesorter stripedtable&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;color:#eee;background:transparent;text-align:center;vertical-align:middle&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th rowspan=&amp;quot;2&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th colspan=&amp;quot;2&amp;quot;&amp;gt;Single Item&amp;lt;/th&amp;gt;&amp;lt;th colspan=&amp;quot;2&amp;quot;&amp;gt;Maximum Item Stack&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Sell Price&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Item Achieve&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sell Price / Slot Size&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Item Achieve / Slot Size&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create Misc table wikitext&lt;br /&gt;
---- Test on wiki with:  mw.log(p.draw_table({args={&amp;quot;Container&amp;quot;}}))&lt;br /&gt;
----                     mw.log(p.draw_table({args={&amp;quot;Gem&amp;quot;}}))&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	if not MD then return &amp;quot;&amp;lt;span style=&#039;color:red;&#039;&amp;gt;Error: Could not load Misc data in [[Module:Misc]]&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext, table_header(frame))&lt;br /&gt;
	for _, key in ipairs(get_list(frame)) do&lt;br /&gt;
		insert(wikitext, row(MD.Misc[key],frame.args[1]))&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext, &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.quest_information(frame)&lt;br /&gt;
	if MD.Misc[frame.args.name].questinformation == &amp;quot;&amp;quot; then return&lt;br /&gt;
	else return &amp;quot;&amp;lt;h2&amp;gt;Quest Information&amp;lt;/h2&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;..MD.Misc[frame.args.name].questinformation..&amp;quot;&amp;lt;/ul&amp;gt;&amp;quot; end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:WeaponPageFlavorText&amp;diff=65709</id>
		<title>Template:WeaponPageFlavorText</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:WeaponPageFlavorText&amp;diff=65709"/>
		<updated>2026-04-15T20:45:41Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added firearm&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;{{#switch:{{lc:{{{1}}}}}&lt;br /&gt;
	|sword=&amp;lt;h2&amp;gt;Swords&amp;lt;/h2&amp;gt;[[Swords]] are versatile weapons that can be used close to mid range. Swords have a variety of attack patterns, making them useful in most situations.&lt;br /&gt;
	|mace=&amp;lt;h2&amp;gt;Maces&amp;lt;/h2&amp;gt; [[Maces]] are heavy, blunt weapons used for close combat. They deal significant damage and have slower attack speed compared to lighter weapons such as swords.&lt;br /&gt;
	|dagger=&amp;lt;h2&amp;gt;Daggers&amp;lt;/h2&amp;gt; [[Daggers]] are fast, light weapons used for quick, precise strikes. They deal lower damage compared to heavier weapons such as swords or maces, but make up for it with increased speed and agility.&lt;br /&gt;
	|polearm=&amp;lt;h2&amp;gt;Polearms&amp;lt;/h2&amp;gt; [[Polearms]] are long-reaching weapons used for ranged combat. They have a longer reach than other weapons, allowing players to attack enemies from a safe distance. Polearms also have unique abilities or special attacks that allow players to control the battlefield and keep enemies at bay.&lt;br /&gt;
	|axe=&amp;lt;h2&amp;gt;Axes&amp;lt;/h2&amp;gt; [[Axes]] are heavy, chopping weapons used for devastating close combat. They typically deal high damage and have slower attack speed compared to lighter weapons such as swords or daggers with some exceptions.&lt;br /&gt;
	|bow=&amp;lt;h2&amp;gt;Bows&amp;lt;/h2&amp;gt; [[Bows]] are ranged weapons used for long-distance combat. They deal moderate to high damage and have a moderate attack speed, allowing players to take down enemies from a safe distance.&lt;br /&gt;
	|crossbow=&amp;lt;h2&amp;gt;Crossbows&amp;lt;/h2&amp;gt; [[Crossbows]] are ranged weapons that trade fire rate for increased damage and accuracy. They deal high damage and have slower attack speed compared to other ranged weapons such as bows, but they make up for it with increased accuracy and power.&lt;br /&gt;
	|firearm=&amp;lt;h2&amp;gt;Firearms&amp;lt;/h2&amp;gt; [[Firearms]] are ranged weapons that further trade fire rate for increased damage.  After every shot firearms must be reloaded, but what the weapon loses in speed it makes up for with a large burst of damage.&lt;br /&gt;
	|magicstuff=&amp;lt;h2&amp;gt;Magical Weapons&amp;lt;/h2&amp;gt; [[Magic Stuff|Magical Weapons]] are ranged and melee weapons that allow player to cast spells depending on their classes. They deal high magic damage with the expense of casting time and spell capacity. Some can be used as physical weapons aswell.&lt;br /&gt;
	|shield=&amp;lt;h2&amp;gt;Shields&amp;lt;/h2&amp;gt; [[Shields]] are a piece of defensive equipment used to provide protection to the player character. It is held in front of the player to deflect incoming attacks and reduce damage taken. Shields have different shapes, sizes, and designs, and may offer various advantages, such as increased defense, the ability to block certain types of attacks, or special abilities. Some shields may be heavier and slower to use, while others are lighter and quicker to maneuver.&lt;br /&gt;
	|musicalinstrument=&amp;lt;h2&amp;gt;Musical Instruments&amp;lt;/h2&amp;gt; [[Musical Instruments]] are used by [[Bard]] to play songs. While they are considered [[Utility|utilities]], some of them can be used as a physical weapon.&lt;br /&gt;
	|throwable&lt;br /&gt;
	|throwablestuff=&amp;lt;h2&amp;gt;Throwable Weapons&amp;lt;/h2&amp;gt; [[Throwables]] can be used by a variety of classes to harass from a distance.  They have moderate reach and speed, and can accumulate a respectable amount of damage.&lt;br /&gt;
	|#default=&amp;lt;span style=&amp;quot;color:red; font-size:20px&amp;quot;&amp;gt;[[Template:WeaponPageFlavorText]] couldn&#039;t match the provided weapon type. Follow the link for documentation to add the type.&amp;lt;/span&amp;gt;}}&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;{{WeaponPageFlavorText|Sord}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{WeaponPageFlavorText|Sord}}&lt;br /&gt;
&amp;lt;pre&amp;gt;{{WeaponPageFlavorText|Sword}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{WeaponPageFlavorText|Sword}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:WeaponPageFlavorText&amp;diff=65576</id>
		<title>Template:WeaponPageFlavorText</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:WeaponPageFlavorText&amp;diff=65576"/>
		<updated>2026-04-15T20:41:06Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added missing types&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;{{#switch:{{lc:{{{1}}}}}&lt;br /&gt;
	|sword=&amp;lt;h2&amp;gt;Swords&amp;lt;/h2&amp;gt;[[Swords]] are versatile weapons that can be used close to mid range. Swords have a variety of attack patterns, making them useful in most situations.&lt;br /&gt;
	|mace=&amp;lt;h2&amp;gt;Maces&amp;lt;/h2&amp;gt; [[Maces]] are heavy, blunt weapons used for close combat. They deal significant damage and have slower attack speed compared to lighter weapons such as swords.&lt;br /&gt;
	|dagger=&amp;lt;h2&amp;gt;Daggers&amp;lt;/h2&amp;gt; [[Daggers]] are fast, light weapons used for quick, precise strikes. They deal lower damage compared to heavier weapons such as swords or maces, but make up for it with increased speed and agility.&lt;br /&gt;
	|polearm=&amp;lt;h2&amp;gt;Polearms&amp;lt;/h2&amp;gt; [[Polearms]] are long-reaching weapons used for ranged combat. They have a longer reach than other weapons, allowing players to attack enemies from a safe distance. Polearms also have unique abilities or special attacks that allow players to control the battlefield and keep enemies at bay.&lt;br /&gt;
	|axe=&amp;lt;h2&amp;gt;Axes&amp;lt;/h2&amp;gt; [[Axes]] are heavy, chopping weapons used for devastating close combat. They typically deal high damage and have slower attack speed compared to lighter weapons such as swords or daggers with some exceptions.&lt;br /&gt;
	|bow=&amp;lt;h2&amp;gt;Bows&amp;lt;/h2&amp;gt; [[Bows]] are ranged weapons used for long-distance combat. They deal moderate to high damage and have a moderate attack speed, allowing players to take down enemies from a safe distance.&lt;br /&gt;
	|crossbow=&amp;lt;h2&amp;gt;Crossbows&amp;lt;/h2&amp;gt; [[Crossbows]] are ranged weapons that trade fire rate for increased damage and accuracy. They deal high damage and have slower attack speed compared to other ranged weapons such as bows, but they make up for it with increased accuracy and power.&lt;br /&gt;
	|magicstuff=&amp;lt;h2&amp;gt;Magical Weapons&amp;lt;/h2&amp;gt; [[Magic Stuff|Magical Weapons]] are ranged and melee weapons that allow player to cast spells depending on their classes. They deal high magic damage with the expense of casting time and spell capacity. Some can be used as physical weapons aswell.&lt;br /&gt;
	|shield=&amp;lt;h2&amp;gt;Shields&amp;lt;/h2&amp;gt; [[Shields]] are a piece of defensive equipment used to provide protection to the player character. It is held in front of the player to deflect incoming attacks and reduce damage taken. Shields have different shapes, sizes, and designs, and may offer various advantages, such as increased defense, the ability to block certain types of attacks, or special abilities. Some shields may be heavier and slower to use, while others are lighter and quicker to maneuver.&lt;br /&gt;
	|musicalinstrument=&amp;lt;h2&amp;gt;Musical Instruments&amp;lt;/h2&amp;gt; [[Musical Instruments]] are used by [[Bard]] to play songs. While they are considered [[Utility|utilities]], some of them can be used as a physical weapon.&lt;br /&gt;
	|throwable&lt;br /&gt;
	|throwablestuff=&amp;lt;h2&amp;gt;Throwable Weapons&amp;lt;/h2&amp;gt; [[Throwables]] can be used by a variety of classes to harass from a distance.  They have moderate reach and speed, and can accumulate a respectable amount of damage.&lt;br /&gt;
	|#default=&amp;lt;span style=&amp;quot;color:red; font-size:20px&amp;quot;&amp;gt;[[Template:WeaponPageFlavorText]] couldn&#039;t match the provided weapon type. Follow the link for documentation to add the type.&amp;lt;/span&amp;gt;}}&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;{{WeaponPageFlavorText|Sord}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{WeaponPageFlavorText|Sord}}&lt;br /&gt;
&amp;lt;pre&amp;gt;{{WeaponPageFlavorText|Sword}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{WeaponPageFlavorText|Sword}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65549</id>
		<title>Module:Weapon</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65549"/>
		<updated>2026-04-15T20:24:00Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added check for primary and secondary attack values.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local WD = mw.loadJsonData(&amp;quot;Data:Weapon.json&amp;quot;) --Global var holding tables from Data:Weapon.json&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
---Get the item&#039;s size in pixels&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Weapon.json don&#039;t work with next() or the # operator&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
local function color_values(weapon, values)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(weapon.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..weapon.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(weapon.rarities) do&lt;br /&gt;
		wt = wt..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment&lt;br /&gt;
local function inline_block(weapon,stat)&lt;br /&gt;
	if weapon.stats[(stat):lower()] == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:inline-block;vertical-align:top;margin:0 15px 0 15px&#039;&amp;gt;&amp;lt;b style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..stat..&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[(stat):lower()])..&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell for the iconbox&lt;br /&gt;
local function name(item, is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local color = &amp;quot;2&amp;quot;&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local icon_amount = &amp;quot;&amp;quot;&lt;br /&gt;
	if item.maxammocount &amp;gt; 0 then&lt;br /&gt;
		icon_amount = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxammocount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..icon_amount&lt;br /&gt;
			..&amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats the weapon&#039;s list of classes into a table cell of classes separated by linebreaks&lt;br /&gt;
local function classes(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Class&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, class in ipairs(weapon.classes) do&lt;br /&gt;
		wt = wt..newline..class&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end -- Subsequent loops will prefix a linebreak&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of slottype and handtype&lt;br /&gt;
local function slot_type(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Slot&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.slottype..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..weapon.handtype..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of move speeds&lt;br /&gt;
local function move_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Movement Speed&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[&amp;quot;move speed&amp;quot;])..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of Armor rating and Magical Resistance&lt;br /&gt;
local function armor_rating(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Armor Rating&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..inline_block(weapon,&amp;quot;Armor Rating&amp;quot;)..inline_block(weapon,&amp;quot;Magical Resistance&amp;quot;)..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function impact_zones(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Impact Zones + Impact Power&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt= &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then&lt;br /&gt;
			for j,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
				if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then&lt;br /&gt;
					wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..ability..&amp;quot; &amp;quot;..attack..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
					for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do&lt;br /&gt;
						if k~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
						wt = wt..impact_zone&lt;br /&gt;
					end&lt;br /&gt;
					wt = wt..&amp;quot; + &amp;quot;..weapon.abilities[ability][attack].impactpower..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.impactresistance~=&amp;quot;&amp;quot; then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Impact Resist&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..weapon.impactresistance&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helper function: Collects move mult and prepare move mult for display&lt;br /&gt;
local function collect_multipliers(ability)&lt;br /&gt;
	local move, prep_move = &amp;quot;&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack_name in ipairs(ability.order) do&lt;br /&gt;
		if ability[attack_name].movementmultiplier then&lt;br /&gt;
			if i ~= 1 then&lt;br /&gt;
				move = move..&amp;quot;/&amp;quot;&lt;br /&gt;
				prep_move = prep_move..&amp;quot;/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			move = move..(ability[attack_name].movementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			prep_move = prep_move..(ability[attack_name].preparemovementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return move,prep_move&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing speed penalties for each action&lt;br /&gt;
local function action_move_speed_penalty(weapon,is_header)&lt;br /&gt;
	if is_header then return [=[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]&amp;lt;/th&amp;gt;]=] end&lt;br /&gt;
&lt;br /&gt;
	local order = {} --reconstruct order to exclude the other ability group&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then order[#order+1] = ability end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability_name in ipairs(order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		if ability_name==&amp;quot;Other&amp;quot; or ability_name==&amp;quot;Block&amp;quot; then wt = wt..ability_name..&amp;quot; Actions&amp;quot; else wt = wt..ability_name..&amp;quot; Attacks&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		local movement_multiplier,prepare_movement_multiplier = collect_multipliers(weapon.abilities[ability_name])&lt;br /&gt;
		if #prepare_movement_multiplier &amp;gt; 3 then&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
			..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..prepare_movement_multiplier&lt;br /&gt;
		else&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing base Physical and Magical damage, if present.&lt;br /&gt;
local function damage_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:25%&amp;quot;&amp;gt;Damage on Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	wt = wt..inline_block(weapon,&amp;quot;Physical Base Weapon Damage&amp;quot;)&lt;br /&gt;
			..inline_block(weapon,&amp;quot;Magical Base Weapon Damage&amp;quot;)&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---These stats get their own table cells, as such they are excluded from the stats cell to avoid needless redundancy&lt;br /&gt;
local exclude_stat = {[&amp;quot;Physical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Magical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Armor Rating&amp;quot;]=true,[&amp;quot;Magical Resistance&amp;quot;]=true,[&amp;quot;Move Speed&amp;quot;]=true}&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing any stats not covered by the rest of the row&lt;br /&gt;
local function stats(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:12%&amp;quot;&amp;gt;Stats&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,stat in ipairs(weapon.stats.order) do&lt;br /&gt;
		if not exclude_stat[stat] then&lt;br /&gt;
			wt = wt..inline_block(weapon,stat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing animation times for ranges weapons&lt;br /&gt;
local function animation_time(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Animation Times&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local windup = &amp;quot;&amp;lt;br&amp;gt;Windup:&amp;quot;&lt;br /&gt;
	local finish = &amp;quot;&amp;lt;br&amp;gt;Finish:&amp;quot;&lt;br /&gt;
	local reload = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Reload&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Attack 1&amp;quot;] then&lt;br /&gt;
		-- windup = windup..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Windup&lt;br /&gt;
		-- finish = finish..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Finish&lt;br /&gt;
	else&lt;br /&gt;
		windup = &amp;quot;&amp;quot;&lt;br /&gt;
		finish = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Other&amp;quot;] then&lt;br /&gt;
		-- reload = reload..weapon.animationtimes[&amp;quot;Other&amp;quot;].Reload&lt;br /&gt;
	else&lt;br /&gt;
		reload = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if #windup == 0 and #finish == 0 and #reload == 0 then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attack&amp;lt;/span&amp;gt;&amp;quot;..windup..finish..reload..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing hitslow for each attack&lt;br /&gt;
local function slowdown_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Slowdown On Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local ability_type = &amp;quot;Primary&amp;quot;&lt;br /&gt;
	if weapon.slottype == &amp;quot;Off-Hand&amp;quot; then ability_type = &amp;quot;Secondary&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Hitslow&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	local duration = &amp;quot; for &amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
				or weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Bonus&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then duration = duration..&amp;quot;/&amp;quot; end&lt;br /&gt;
		duration = duration..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Duration&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..duration..&amp;quot;s&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the projectile&#039;s initial speed&lt;br /&gt;
local function initial_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Initial Projectile Speed (m/s)&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.initialspeed..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the hitbox image resized by inventory size&lt;br /&gt;
local function hitbox(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Hitbox + Impact Resist&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;[[File:&amp;quot;..weapon.name..&amp;quot; Hitbox.png|link=|&amp;quot;..size(weapon)..&amp;quot;]]&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helps format a string containing either Combo Damage or damagetype values&lt;br /&gt;
local function format_combo(weapon,ability,value)&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability][attack][value] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing ability combo values per attack&lt;br /&gt;
local function combo(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Combo&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	if weapon.abilities[&amp;quot;Primary&amp;quot;] then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Secondary&amp;quot;] then&lt;br /&gt;
		if #wt ~= 0 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Secondary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Special&amp;quot;] then&lt;br /&gt;
		if wt:match(&amp;quot;Primary&amp;quot;) or wt:match(&amp;quot;Secondary&amp;quot;) then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Special Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function determine_table_type(weapon)&lt;br /&gt;
	if weapon.args then&lt;br /&gt;
		if weapon.args[1] == &amp;quot;Shield&amp;quot; then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif (weapon.args[1]):lower():match(&amp;quot;bow&amp;quot;) or weapon.args[1] == &amp;quot;Firearm&amp;quot; or weapon.args[1] == &amp;quot;Throwable&amp;quot; then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	elseif weapon.types then&lt;br /&gt;
		if weapon.types.Shield then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm or weapon.types.Throwable then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;Default&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Determines which table data goes into the table row&lt;br /&gt;
local row_type = {&lt;br /&gt;
	[&amp;quot;Shield&amp;quot;] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},&lt;br /&gt;
	[&amp;quot;Ranged&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,action_move_speed_penalty,slowdown_on_hit,initial_speed},&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing weapon information,&lt;br /&gt;
local function row(weapon, is_header)&lt;br /&gt;
	is_header = is_header or false&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,content in ipairs(row_type[determine_table_type(weapon)]) do&lt;br /&gt;
		wt[#wt+1] = content(weapon,is_header)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Returns a predetermined weapon list&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	if not (frame.args and frame.args[1] and frame.args[2]) then return {} end&lt;br /&gt;
	if not (WD[frame.args[1]] and WD[frame.args[1]][frame.args[2]]) then return {} end&lt;br /&gt;
&lt;br /&gt;
	return WD[frame.args[1]][frame.args[2]]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	local item_list = get_list(frame)&lt;br /&gt;
	if count(item_list) == 0 then return &amp;quot;&amp;lt;span style=&#039;font-size:24pt&#039;&amp;gt;There are currently no items of this type.&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = [[&amp;lt;table cellspacing=&amp;quot;0&amp;quot; class=&amp;quot;wikitable sortable jquery-tablesorter&amp;quot; style=&amp;quot;text-align:center; vertical-align:middle;&amp;quot;&amp;gt;]]&lt;br /&gt;
	wt[#wt+1] = row(frame, true)&lt;br /&gt;
	for _,item_name in ipairs(item_list) do&lt;br /&gt;
		wt[#wt+1] = row(WD.Weapon[item_name])&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[###########################################################################&lt;br /&gt;
                Functions for individual weapon pages&lt;br /&gt;
#############################################################################]=]&lt;br /&gt;
local function p_projectile(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.initialspeed ~= &amp;quot;&amp;quot; then &lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Projectile&amp;lt;/h2&amp;gt;Initial Speed: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.initialspeed)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.piercecount ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;Pierce: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.piercecount)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_damage_falloff(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.damagefalloff == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Damage Falloff&amp;lt;/h2&amp;gt;%s has damage falloff depending on airtime of the projectile.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Projectile falloff damage chart:&amp;lt;br&amp;gt;X: Air time (seconds)&amp;lt;br&amp;gt;Y: %% Damage&amp;quot;,weapon_name,weapon.initialspeed)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:500px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = weapon.damagefalloff&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;Learn more at [[Damage_Calculation#Projectile_Falloff|Projectile Falloff]]&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_hitbox(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.hitbox or not weapon.hitbox.height then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Hitbox&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;Height: &amp;quot;..weapon.hitbox.height&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Width: &amp;quot;..weapon.hitbox.width&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Depth: &amp;quot;..weapon.hitbox.depth&lt;br /&gt;
&lt;br /&gt;
	local primary_attack = weapon.abilities.Primary and weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;] or {}&lt;br /&gt;
	local secondary_attack = weapon.abilities.Secondary and weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;] or {}&lt;br /&gt;
	if #primary_attack == 0 and #secondary_attack == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and has primary or secondary attacks&lt;br /&gt;
	if not (weapon.types[&amp;quot;Bow&amp;quot;] or weapon.types[&amp;quot;Crossow&amp;quot;] or weapon.types[&amp;quot;ThrowableStuff&amp;quot;])&lt;br /&gt;
			and (primary_attack.impactzones or secondary_attack.impactzones) then&lt;br /&gt;
		local impact_zones = primary_attack.impactzones or secondary_attack.impactzones&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;The weapon&#039;s hitbox doesn&#039;t necessarily determine a weapon&#039;s reach due to different arm extension during attack animations. Thus, shorter weapons can have better reach than longer weapons.&amp;lt;br&amp;gt;[[Impact Zones]] of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;:&amp;lt;div style=&#039;display:flex;align-items:center&#039;&amp;gt;&amp;lt;div&amp;gt;[[File:&amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot; Hitbox.png|x300px]]&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Green Zone deals &amp;quot;&lt;br /&gt;
		wt[#wt+1] = impact_zones[1]&lt;br /&gt;
		wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if impact_zones[2] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:orange&#039;&amp;gt;Orange Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[2]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if impact_zones[3] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Red Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[3]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;&#039;Disclaimer&#039;&#039;&#039;: The impact zone&#039;s locations are an approximation. The real hitboxes may be slightly larger, closer together, and/or follow the weapon&#039;s shape more closely. Image is not able to be updated automatically. If it is outdated, please report it to the [https://discord.gg/KbsxgACkFS Wiki Discord].&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if primary_attack.impactpower or secondary_attack.impactpower then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Impact Power&#039;&#039;&#039; of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;: &amp;quot;&lt;br /&gt;
		wt[#wt+1] = tostring(primary_attack.impactpower or secondary_attack.impactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Impact power helps with breaking crates, barricades or any other breakables in game. The higher the impact power the faster something breaks.&amp;lt;br&amp;gt;Impact power also affects shield blocking. If the attacking weapon&#039;s impact power is higher than shield&#039;s impact power, the shield holder staggers, and vice versa.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function list_to_string(l,separator)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(l) do&lt;br /&gt;
		if i~=1 then ret = ret..separator end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo_format(wt,attacks,type)&lt;br /&gt;
	if type == &amp;quot;Other&amp;quot; or type == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
	local first = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = type&lt;br /&gt;
	wt[#wt+1] = &amp;quot; Attack: &amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; else first = name end&lt;br /&gt;
		wt[#wt+1] = attacks[name].damagetype&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; dealing &amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = attacks[name].combodamage&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; damage on each swing, with impact zone: &amp;quot;&lt;br /&gt;
	wt[#wt+1] = list_to_string(attacks[first].impactzones, &amp;quot;/&amp;quot;)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function attack_animation_times(wt,weapon,ability_name,attack_name)&lt;br /&gt;
	local attack = weapon.animationtimes[ability_name][attack_name]&lt;br /&gt;
	if attack[&amp;quot;Windup&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                           attack[&amp;quot;Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Hit&amp;quot;]               then wt[#wt+1] = string.format(&amp;quot;Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                              attack[&amp;quot;Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Windup&amp;quot;]     then wt[#wt+1] = string.format(&amp;quot;Second Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                    attack[&amp;quot;Second Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Hit&amp;quot;]        then wt[#wt+1] = string.format(&amp;quot;Second Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                       attack[&amp;quot;Second Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Recover&amp;quot;]           then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Recover: %s&amp;lt;br&amp;gt;&amp;quot;,           attack[&amp;quot;Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Alternate Recover&amp;quot;] then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Alternate Recover: %s&amp;lt;br&amp;gt;&amp;quot;, attack[&amp;quot;Alternate Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Finish&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Finish: %s&amp;lt;br&amp;gt;&amp;quot;,            attack[&amp;quot;Finish&amp;quot;]) end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function animation_format(wt, weapon, ability)&lt;br /&gt;
	if ability == &amp;quot;Other&amp;quot; or ability == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	for i, ability_name in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		wt[#wt+1] = ability..&amp;quot; Combo: &amp;quot;..ability_name..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		attack_animation_times(wt,weapon,ability,ability_name)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_combo(wt, page_name)&lt;br /&gt;
	local weapon = WD.Weapon[page_name]&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and isn&#039;t shield, or at least isn&#039;t a Lantern Shield&lt;br /&gt;
	if not (weapon.types.Bow or weapon.types.Crossow or weapon.types.ThrowableStuff)&lt;br /&gt;
		and (not weapon.types.Shield or (weapon.name):match(&amp;quot;Lantern Shield&amp;quot;)) then&lt;br /&gt;
&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Attack Animation Time&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
			combo_format(wt,weapon.abilities[name],name)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Attack Times&#039;&#039;&#039;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
				animation_format(wt,weapon,name)&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;Reference [[Attack Times]] for a glossary of the terms used above.&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_block(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.impactresistance then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Block&amp;lt;/h2&amp;gt;%s has a block ability with an impact resistance of &#039;&#039;&#039;%s&#039;&#039;&#039;.&amp;lt;br&amp;gt;To learn more about impact resistance see [[Impact Power]].&amp;quot;, weapon.name, weapon.impactresistance)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_action_move_slow_penalty(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Action Movement Slow&amp;lt;/h2&amp;gt;When a player performs certain actions, such as attacking or defending themselves, they move slower.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		for _,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
			-- There&#039;s a bug where Riposte values are recorded within &amp;quot;Other&amp;quot; abilities&lt;br /&gt;
			-- Since ripose is covered in Special abilities, we will skip it here&lt;br /&gt;
			if attack == &amp;quot;Riposte&amp;quot; then break end&lt;br /&gt;
&lt;br /&gt;
			local movespeedadd = weapon.abilities[ability][attack].effects&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
								or &amp;quot;&amp;quot;&lt;br /&gt;
			local movement_multiplier = weapon.abilities[ability][attack].movementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
			local prepare_movement_multiplier = weapon.abilities[ability][attack].preparemovementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if ability == &amp;quot;Other&amp;quot; then wt[#wt+1] = attack else wt[#wt+1] = ability end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if #prepare_movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = prepare_movement_multiplier&lt;br /&gt;
			elseif #movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
			else&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movespeedadd&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			-- Everything except attacks within Other has invariant move mults&lt;br /&gt;
			-- So after processessing the first attack, we break the loop&lt;br /&gt;
			if ability ~= &amp;quot;Other&amp;quot; then break end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_artifact(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.isartifact or weapon.artifactname == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Artifact&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;%s has a powerful [[Artifacts|Artifact]] named [[%s]].&amp;lt;/p&amp;gt;&amp;quot;, weapon.name, weapon.artifactname)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = &amp;quot;&amp;quot;&lt;br /&gt;
	if active then&lt;br /&gt;
		class = &amp;quot; class=&#039;selected-tab tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		class = &amp;quot; class=&#039;tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:inline-block&#039; %s data-tabid=&#039;%s&#039; data-tab=&#039;%s&#039; title=&#039;%s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,class,tabid,title,title,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reference_table(wt,table_type)&lt;br /&gt;
	local types_order = {&amp;quot;Sword&amp;quot;,&amp;quot;Dagger&amp;quot;,&amp;quot;Mace&amp;quot;,&amp;quot;Polearm&amp;quot;,&amp;quot;Axe&amp;quot;,&amp;quot;Throwable&amp;quot;,&amp;quot;Bow&amp;quot;,&amp;quot;Crossbow&amp;quot;,&amp;quot;Firearm&amp;quot;,&amp;quot;MagicStuff&amp;quot;,&amp;quot;Shield&amp;quot;,&amp;quot;LightSource&amp;quot;}&lt;br /&gt;
	local localization = {&lt;br /&gt;
		[&amp;quot;Sword&amp;quot;]        = &amp;quot;Swords&amp;quot;,&lt;br /&gt;
		[&amp;quot;Dagger&amp;quot;]       = &amp;quot;Daggers&amp;quot;,&lt;br /&gt;
		[&amp;quot;Mace&amp;quot;]         = &amp;quot;Maces&amp;quot;,&lt;br /&gt;
		[&amp;quot;Polearm&amp;quot;]      = &amp;quot;Polearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;Axe&amp;quot;]          = &amp;quot;Axes&amp;quot;,&lt;br /&gt;
		[&amp;quot;Throwable&amp;quot;]    = &amp;quot;Throwables&amp;quot;,&lt;br /&gt;
		[&amp;quot;Bow&amp;quot;]          = &amp;quot;Bows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Crossbow&amp;quot;]     = &amp;quot;Crossbows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Firearm&amp;quot;]      = &amp;quot;Firearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;MagicStuff&amp;quot;]   = &amp;quot;Magic Stuff&amp;quot;,&lt;br /&gt;
		[&amp;quot;Shield&amp;quot;]       = &amp;quot;Shields&amp;quot;,&lt;br /&gt;
		[&amp;quot;LightSource&amp;quot;]  = &amp;quot;Light Sources&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	local separator = &amp;quot;]] • [[&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:100%;text-align:center;vertical-align:middle;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for _,weapon_type in ipairs(types_order) do&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%&#039;&amp;gt;[[Weapons#%s|%s]]&amp;lt;/td&amp;gt;&amp;lt;td style=&#039;text-align-last:left;padding:10px 25px&#039;&amp;gt;&amp;quot;,localization[weapon_type],localization[weapon_type])&lt;br /&gt;
		if WD[weapon_type][table_type] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;[[&amp;quot;&lt;br /&gt;
			for i,v in ipairs(WD[weapon_type][table_type]) do&lt;br /&gt;
				if i~=1 then wt[#wt+1] = separator end&lt;br /&gt;
				wt[#wt+1] = v&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;]]&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.references()&lt;br /&gt;
	local table_types = {&amp;quot;Uncraftable&amp;quot;,&amp;quot;Craftable&amp;quot;,&amp;quot;Artifact&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;h2&amp;gt;References&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = tab_toggle(table_type,table_type..&amp;quot; Weapons&amp;quot;,i==1,&amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;&amp;quot;..table_type..&amp;quot;-data&#039;&amp;quot;&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;gt;&amp;quot; else wt[#wt+1] = &amp;quot;style=&#039;display:none&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		reference_table(wt,table_type)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local page_name = f.args.name&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	p_projectile(wt,page_name)&lt;br /&gt;
	p_damage_falloff(wt,page_name)&lt;br /&gt;
	p_hitbox(wt,page_name)&lt;br /&gt;
	p_combo(wt,page_name)&lt;br /&gt;
	p_block(wt,page_name)&lt;br /&gt;
	p_action_move_slow_penalty(wt,page_name)&lt;br /&gt;
	p_artifact(wt,page_name)&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on wiki with&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Crossbow&amp;quot;,&amp;quot;Uncraftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Shield&amp;quot;,&amp;quot;Craftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Polearm&amp;quot;,&amp;quot;Artifact&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={name=&amp;quot;Rapier&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65548</id>
		<title>Module:Weapon</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65548"/>
		<updated>2026-04-15T20:12:43Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Some function name changes.  Made hitbox check more thorough.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local WD = mw.loadJsonData(&amp;quot;Data:Weapon.json&amp;quot;) --Global var holding tables from Data:Weapon.json&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
---Get the item&#039;s size in pixels&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Weapon.json don&#039;t work with next() or the # operator&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
local function color_values(weapon, values)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(weapon.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..weapon.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(weapon.rarities) do&lt;br /&gt;
		wt = wt..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment&lt;br /&gt;
local function inline_block(weapon,stat)&lt;br /&gt;
	if weapon.stats[(stat):lower()] == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:inline-block;vertical-align:top;margin:0 15px 0 15px&#039;&amp;gt;&amp;lt;b style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..stat..&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[(stat):lower()])..&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell for the iconbox&lt;br /&gt;
local function name(item, is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local color = &amp;quot;2&amp;quot;&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local icon_amount = &amp;quot;&amp;quot;&lt;br /&gt;
	if item.maxammocount &amp;gt; 0 then&lt;br /&gt;
		icon_amount = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxammocount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..icon_amount&lt;br /&gt;
			..&amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats the weapon&#039;s list of classes into a table cell of classes separated by linebreaks&lt;br /&gt;
local function classes(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Class&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, class in ipairs(weapon.classes) do&lt;br /&gt;
		wt = wt..newline..class&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end -- Subsequent loops will prefix a linebreak&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of slottype and handtype&lt;br /&gt;
local function slot_type(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Slot&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.slottype..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..weapon.handtype..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of move speeds&lt;br /&gt;
local function move_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Movement Speed&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[&amp;quot;move speed&amp;quot;])..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of Armor rating and Magical Resistance&lt;br /&gt;
local function armor_rating(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Armor Rating&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..inline_block(weapon,&amp;quot;Armor Rating&amp;quot;)..inline_block(weapon,&amp;quot;Magical Resistance&amp;quot;)..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function impact_zones(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Impact Zones + Impact Power&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt= &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then&lt;br /&gt;
			for j,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
				if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then&lt;br /&gt;
					wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..ability..&amp;quot; &amp;quot;..attack..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
					for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do&lt;br /&gt;
						if k~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
						wt = wt..impact_zone&lt;br /&gt;
					end&lt;br /&gt;
					wt = wt..&amp;quot; + &amp;quot;..weapon.abilities[ability][attack].impactpower..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.impactresistance~=&amp;quot;&amp;quot; then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Impact Resist&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..weapon.impactresistance&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helper function: Collects move mult and prepare move mult for display&lt;br /&gt;
local function collect_multipliers(ability)&lt;br /&gt;
	local move, prep_move = &amp;quot;&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack_name in ipairs(ability.order) do&lt;br /&gt;
		if ability[attack_name].movementmultiplier then&lt;br /&gt;
			if i ~= 1 then&lt;br /&gt;
				move = move..&amp;quot;/&amp;quot;&lt;br /&gt;
				prep_move = prep_move..&amp;quot;/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			move = move..(ability[attack_name].movementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			prep_move = prep_move..(ability[attack_name].preparemovementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return move,prep_move&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing speed penalties for each action&lt;br /&gt;
local function action_move_speed_penalty(weapon,is_header)&lt;br /&gt;
	if is_header then return [=[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]&amp;lt;/th&amp;gt;]=] end&lt;br /&gt;
&lt;br /&gt;
	local order = {} --reconstruct order to exclude the other ability group&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then order[#order+1] = ability end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability_name in ipairs(order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		if ability_name==&amp;quot;Other&amp;quot; or ability_name==&amp;quot;Block&amp;quot; then wt = wt..ability_name..&amp;quot; Actions&amp;quot; else wt = wt..ability_name..&amp;quot; Attacks&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		local movement_multiplier,prepare_movement_multiplier = collect_multipliers(weapon.abilities[ability_name])&lt;br /&gt;
		if #prepare_movement_multiplier &amp;gt; 3 then&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
			..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..prepare_movement_multiplier&lt;br /&gt;
		else&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing base Physical and Magical damage, if present.&lt;br /&gt;
local function damage_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:25%&amp;quot;&amp;gt;Damage on Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	wt = wt..inline_block(weapon,&amp;quot;Physical Base Weapon Damage&amp;quot;)&lt;br /&gt;
			..inline_block(weapon,&amp;quot;Magical Base Weapon Damage&amp;quot;)&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---These stats get their own table cells, as such they are excluded from the stats cell to avoid needless redundancy&lt;br /&gt;
local exclude_stat = {[&amp;quot;Physical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Magical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Armor Rating&amp;quot;]=true,[&amp;quot;Magical Resistance&amp;quot;]=true,[&amp;quot;Move Speed&amp;quot;]=true}&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing any stats not covered by the rest of the row&lt;br /&gt;
local function stats(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:12%&amp;quot;&amp;gt;Stats&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,stat in ipairs(weapon.stats.order) do&lt;br /&gt;
		if not exclude_stat[stat] then&lt;br /&gt;
			wt = wt..inline_block(weapon,stat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing animation times for ranges weapons&lt;br /&gt;
local function animation_time(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Animation Times&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local windup = &amp;quot;&amp;lt;br&amp;gt;Windup:&amp;quot;&lt;br /&gt;
	local finish = &amp;quot;&amp;lt;br&amp;gt;Finish:&amp;quot;&lt;br /&gt;
	local reload = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Reload&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Attack 1&amp;quot;] then&lt;br /&gt;
		-- windup = windup..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Windup&lt;br /&gt;
		-- finish = finish..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Finish&lt;br /&gt;
	else&lt;br /&gt;
		windup = &amp;quot;&amp;quot;&lt;br /&gt;
		finish = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Other&amp;quot;] then&lt;br /&gt;
		-- reload = reload..weapon.animationtimes[&amp;quot;Other&amp;quot;].Reload&lt;br /&gt;
	else&lt;br /&gt;
		reload = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if #windup == 0 and #finish == 0 and #reload == 0 then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attack&amp;lt;/span&amp;gt;&amp;quot;..windup..finish..reload..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing hitslow for each attack&lt;br /&gt;
local function slowdown_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Slowdown On Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local ability_type = &amp;quot;Primary&amp;quot;&lt;br /&gt;
	if weapon.slottype == &amp;quot;Off-Hand&amp;quot; then ability_type = &amp;quot;Secondary&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Hitslow&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	local duration = &amp;quot; for &amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
				or weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Bonus&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then duration = duration..&amp;quot;/&amp;quot; end&lt;br /&gt;
		duration = duration..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Duration&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..duration..&amp;quot;s&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the projectile&#039;s initial speed&lt;br /&gt;
local function initial_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Initial Projectile Speed (m/s)&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.initialspeed..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the hitbox image resized by inventory size&lt;br /&gt;
local function hitbox(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Hitbox + Impact Resist&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;[[File:&amp;quot;..weapon.name..&amp;quot; Hitbox.png|link=|&amp;quot;..size(weapon)..&amp;quot;]]&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helps format a string containing either Combo Damage or damagetype values&lt;br /&gt;
local function format_combo(weapon,ability,value)&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability][attack][value] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing ability combo values per attack&lt;br /&gt;
local function combo(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Combo&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	if weapon.abilities[&amp;quot;Primary&amp;quot;] then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Secondary&amp;quot;] then&lt;br /&gt;
		if #wt ~= 0 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Secondary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Special&amp;quot;] then&lt;br /&gt;
		if wt:match(&amp;quot;Primary&amp;quot;) or wt:match(&amp;quot;Secondary&amp;quot;) then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Special Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function determine_table_type(weapon)&lt;br /&gt;
	if weapon.args then&lt;br /&gt;
		if weapon.args[1] == &amp;quot;Shield&amp;quot; then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif (weapon.args[1]):lower():match(&amp;quot;bow&amp;quot;) or weapon.args[1] == &amp;quot;Firearm&amp;quot; or weapon.args[1] == &amp;quot;Throwable&amp;quot; then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	elseif weapon.types then&lt;br /&gt;
		if weapon.types.Shield then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm or weapon.types.Throwable then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;Default&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Determines which table data goes into the table row&lt;br /&gt;
local row_type = {&lt;br /&gt;
	[&amp;quot;Shield&amp;quot;] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},&lt;br /&gt;
	[&amp;quot;Ranged&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,action_move_speed_penalty,slowdown_on_hit,initial_speed},&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing weapon information,&lt;br /&gt;
local function row(weapon, is_header)&lt;br /&gt;
	is_header = is_header or false&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,content in ipairs(row_type[determine_table_type(weapon)]) do&lt;br /&gt;
		wt[#wt+1] = content(weapon,is_header)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Returns a predetermined weapon list&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	if not (frame.args and frame.args[1] and frame.args[2]) then return {} end&lt;br /&gt;
	if not (WD[frame.args[1]] and WD[frame.args[1]][frame.args[2]]) then return {} end&lt;br /&gt;
&lt;br /&gt;
	return WD[frame.args[1]][frame.args[2]]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	local item_list = get_list(frame)&lt;br /&gt;
	if count(item_list) == 0 then return &amp;quot;&amp;lt;span style=&#039;font-size:24pt&#039;&amp;gt;There are currently no items of this type.&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = [[&amp;lt;table cellspacing=&amp;quot;0&amp;quot; class=&amp;quot;wikitable sortable jquery-tablesorter&amp;quot; style=&amp;quot;text-align:center; vertical-align:middle;&amp;quot;&amp;gt;]]&lt;br /&gt;
	wt[#wt+1] = row(frame, true)&lt;br /&gt;
	for _,item_name in ipairs(item_list) do&lt;br /&gt;
		wt[#wt+1] = row(WD.Weapon[item_name])&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[###########################################################################&lt;br /&gt;
                Functions for individual weapon pages&lt;br /&gt;
#############################################################################]=]&lt;br /&gt;
local function p_projectile(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.initialspeed ~= &amp;quot;&amp;quot; then &lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Projectile&amp;lt;/h2&amp;gt;Initial Speed: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.initialspeed)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.piercecount ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;Pierce: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.piercecount)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_damage_falloff(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.damagefalloff == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Damage Falloff&amp;lt;/h2&amp;gt;%s has damage falloff depending on airtime of the projectile.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Projectile falloff damage chart:&amp;lt;br&amp;gt;X: Air time (seconds)&amp;lt;br&amp;gt;Y: %% Damage&amp;quot;,weapon_name,weapon.initialspeed)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:500px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = weapon.damagefalloff&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;Learn more at [[Damage_Calculation#Projectile_Falloff|Projectile Falloff]]&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_hitbox(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.hitbox or not weapon.hitbox.height then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Hitbox&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;Height: &amp;quot;..weapon.hitbox.height&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Width: &amp;quot;..weapon.hitbox.width&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Depth: &amp;quot;..weapon.hitbox.depth&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and has primary or secondary attacks&lt;br /&gt;
	if not (weapon.types[&amp;quot;Bow&amp;quot;] or weapon.types[&amp;quot;Crossow&amp;quot;] or weapon.types[&amp;quot;ThrowableStuff&amp;quot;])&lt;br /&gt;
			and (weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactzones or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactzones) then&lt;br /&gt;
		local impact_zones = weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactzones or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactzones&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;The weapon&#039;s hitbox doesn&#039;t necessarily determine a weapon&#039;s reach due to different arm extension during attack animations. Thus, shorter weapons can have better reach than longer weapons.&amp;lt;br&amp;gt;[[Impact Zones]] of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;:&amp;lt;div style=&#039;display:flex;align-items:center&#039;&amp;gt;&amp;lt;div&amp;gt;[[File:&amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot; Hitbox.png|x300px]]&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Green Zone deals &amp;quot;&lt;br /&gt;
		wt[#wt+1] = impact_zones[1]&lt;br /&gt;
		wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if impact_zones[2] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:orange&#039;&amp;gt;Orange Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[2]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if impact_zones[3] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Red Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[3]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;&#039;Disclaimer&#039;&#039;&#039;: The impact zone&#039;s locations are an approximation. The real hitboxes may be slightly larger, closer together, and/or follow the weapon&#039;s shape more closely. Image is not able to be updated automatically. If it is outdated, please report it to the [https://discord.gg/KbsxgACkFS Wiki Discord].&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactpower or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactpower then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Impact Power&#039;&#039;&#039; of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;: &amp;quot;&lt;br /&gt;
		wt[#wt+1] = tostring(weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactpower or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Impact power helps with breaking crates, barricades or any other breakables in game. The higher the impact power the faster something breaks.&amp;lt;br&amp;gt;Impact power also affects shield blocking. If the attacking weapon&#039;s impact power is higher than shield&#039;s impact power, the shield holder staggers, and vice versa.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function list_to_string(l,separator)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(l) do&lt;br /&gt;
		if i~=1 then ret = ret..separator end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo_format(wt,attacks,type)&lt;br /&gt;
	if type == &amp;quot;Other&amp;quot; or type == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
	local first = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = type&lt;br /&gt;
	wt[#wt+1] = &amp;quot; Attack: &amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; else first = name end&lt;br /&gt;
		wt[#wt+1] = attacks[name].damagetype&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; dealing &amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = attacks[name].combodamage&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; damage on each swing, with impact zone: &amp;quot;&lt;br /&gt;
	wt[#wt+1] = list_to_string(attacks[first].impactzones, &amp;quot;/&amp;quot;)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function attack_animation_times(wt,weapon,ability_name,attack_name)&lt;br /&gt;
	local attack = weapon.animationtimes[ability_name][attack_name]&lt;br /&gt;
	if attack[&amp;quot;Windup&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                           attack[&amp;quot;Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Hit&amp;quot;]               then wt[#wt+1] = string.format(&amp;quot;Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                              attack[&amp;quot;Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Windup&amp;quot;]     then wt[#wt+1] = string.format(&amp;quot;Second Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                    attack[&amp;quot;Second Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Hit&amp;quot;]        then wt[#wt+1] = string.format(&amp;quot;Second Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                       attack[&amp;quot;Second Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Recover&amp;quot;]           then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Recover: %s&amp;lt;br&amp;gt;&amp;quot;,           attack[&amp;quot;Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Alternate Recover&amp;quot;] then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Alternate Recover: %s&amp;lt;br&amp;gt;&amp;quot;, attack[&amp;quot;Alternate Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Finish&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Finish: %s&amp;lt;br&amp;gt;&amp;quot;,            attack[&amp;quot;Finish&amp;quot;]) end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function animation_format(wt, weapon, ability)&lt;br /&gt;
	if ability == &amp;quot;Other&amp;quot; or ability == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	for i, ability_name in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		wt[#wt+1] = ability..&amp;quot; Combo: &amp;quot;..ability_name..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		attack_animation_times(wt,weapon,ability,ability_name)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_combo(wt, page_name)&lt;br /&gt;
	local weapon = WD.Weapon[page_name]&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and isn&#039;t shield, or at least isn&#039;t a Lantern Shield&lt;br /&gt;
	if not (weapon.types.Bow or weapon.types.Crossow or weapon.types.ThrowableStuff)&lt;br /&gt;
		and (not weapon.types.Shield or (weapon.name):match(&amp;quot;Lantern Shield&amp;quot;)) then&lt;br /&gt;
&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Attack Animation Time&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
			combo_format(wt,weapon.abilities[name],name)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Attack Times&#039;&#039;&#039;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
				animation_format(wt,weapon,name)&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;Reference [[Attack Times]] for a glossary of the terms used above.&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_block(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.impactresistance then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Block&amp;lt;/h2&amp;gt;%s has a block ability with an impact resistance of &#039;&#039;&#039;%s&#039;&#039;&#039;.&amp;lt;br&amp;gt;To learn more about impact resistance see [[Impact Power]].&amp;quot;, weapon.name, weapon.impactresistance)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_action_move_slow_penalty(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Action Movement Slow&amp;lt;/h2&amp;gt;When a player performs certain actions, such as attacking or defending themselves, they move slower.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		for _,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
			-- There&#039;s a bug where Riposte values are recorded within &amp;quot;Other&amp;quot; abilities&lt;br /&gt;
			-- Since ripose is covered in Special abilities, we will skip it here&lt;br /&gt;
			if attack == &amp;quot;Riposte&amp;quot; then break end&lt;br /&gt;
&lt;br /&gt;
			local movespeedadd = weapon.abilities[ability][attack].effects&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
								or &amp;quot;&amp;quot;&lt;br /&gt;
			local movement_multiplier = weapon.abilities[ability][attack].movementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
			local prepare_movement_multiplier = weapon.abilities[ability][attack].preparemovementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if ability == &amp;quot;Other&amp;quot; then wt[#wt+1] = attack else wt[#wt+1] = ability end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if #prepare_movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = prepare_movement_multiplier&lt;br /&gt;
			elseif #movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
			else&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movespeedadd&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			-- Everything except attacks within Other has invariant move mults&lt;br /&gt;
			-- So after processessing the first attack, we break the loop&lt;br /&gt;
			if ability ~= &amp;quot;Other&amp;quot; then break end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function p_artifact(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.isartifact or weapon.artifactname == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Artifact&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;%s has a powerful [[Artifacts|Artifact]] named [[%s]].&amp;lt;/p&amp;gt;&amp;quot;, weapon.name, weapon.artifactname)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = &amp;quot;&amp;quot;&lt;br /&gt;
	if active then&lt;br /&gt;
		class = &amp;quot; class=&#039;selected-tab tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		class = &amp;quot; class=&#039;tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:inline-block&#039; %s data-tabid=&#039;%s&#039; data-tab=&#039;%s&#039; title=&#039;%s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,class,tabid,title,title,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reference_table(wt,table_type)&lt;br /&gt;
	local types_order = {&amp;quot;Sword&amp;quot;,&amp;quot;Dagger&amp;quot;,&amp;quot;Mace&amp;quot;,&amp;quot;Polearm&amp;quot;,&amp;quot;Axe&amp;quot;,&amp;quot;Throwable&amp;quot;,&amp;quot;Bow&amp;quot;,&amp;quot;Crossbow&amp;quot;,&amp;quot;Firearm&amp;quot;,&amp;quot;MagicStuff&amp;quot;,&amp;quot;Shield&amp;quot;,&amp;quot;LightSource&amp;quot;}&lt;br /&gt;
	local localization = {&lt;br /&gt;
		[&amp;quot;Sword&amp;quot;]        = &amp;quot;Swords&amp;quot;,&lt;br /&gt;
		[&amp;quot;Dagger&amp;quot;]       = &amp;quot;Daggers&amp;quot;,&lt;br /&gt;
		[&amp;quot;Mace&amp;quot;]         = &amp;quot;Maces&amp;quot;,&lt;br /&gt;
		[&amp;quot;Polearm&amp;quot;]      = &amp;quot;Polearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;Axe&amp;quot;]          = &amp;quot;Axes&amp;quot;,&lt;br /&gt;
		[&amp;quot;Throwable&amp;quot;]    = &amp;quot;Throwables&amp;quot;,&lt;br /&gt;
		[&amp;quot;Bow&amp;quot;]          = &amp;quot;Bows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Crossbow&amp;quot;]     = &amp;quot;Crossbows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Firearm&amp;quot;]      = &amp;quot;Firearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;MagicStuff&amp;quot;]   = &amp;quot;Magic Stuff&amp;quot;,&lt;br /&gt;
		[&amp;quot;Shield&amp;quot;]       = &amp;quot;Shields&amp;quot;,&lt;br /&gt;
		[&amp;quot;LightSource&amp;quot;]  = &amp;quot;Light Sources&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	local separator = &amp;quot;]] • [[&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:100%;text-align:center;vertical-align:middle;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for _,weapon_type in ipairs(types_order) do&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%&#039;&amp;gt;[[Weapons#%s|%s]]&amp;lt;/td&amp;gt;&amp;lt;td style=&#039;text-align-last:left;padding:10px 25px&#039;&amp;gt;&amp;quot;,localization[weapon_type],localization[weapon_type])&lt;br /&gt;
		if WD[weapon_type][table_type] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;[[&amp;quot;&lt;br /&gt;
			for i,v in ipairs(WD[weapon_type][table_type]) do&lt;br /&gt;
				if i~=1 then wt[#wt+1] = separator end&lt;br /&gt;
				wt[#wt+1] = v&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;]]&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.references()&lt;br /&gt;
	local table_types = {&amp;quot;Uncraftable&amp;quot;,&amp;quot;Craftable&amp;quot;,&amp;quot;Artifact&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;h2&amp;gt;References&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = tab_toggle(table_type,table_type..&amp;quot; Weapons&amp;quot;,i==1,&amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;&amp;quot;..table_type..&amp;quot;-data&#039;&amp;quot;&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;gt;&amp;quot; else wt[#wt+1] = &amp;quot;style=&#039;display:none&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		reference_table(wt,table_type)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local page_name = f.args.name&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	p_projectile(wt,page_name)&lt;br /&gt;
	p_damage_falloff(wt,page_name)&lt;br /&gt;
	p_hitbox(wt,page_name)&lt;br /&gt;
	p_combo(wt,page_name)&lt;br /&gt;
	p_block(wt,page_name)&lt;br /&gt;
	p_action_move_slow_penalty(wt,page_name)&lt;br /&gt;
	p_artifact(wt,page_name)&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on wiki with&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Crossbow&amp;quot;,&amp;quot;Uncraftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Shield&amp;quot;,&amp;quot;Craftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Polearm&amp;quot;,&amp;quot;Artifact&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={name=&amp;quot;Rapier&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:WeaponPageFlavorText&amp;diff=65538</id>
		<title>Template:WeaponPageFlavorText</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:WeaponPageFlavorText&amp;diff=65538"/>
		<updated>2026-04-14T05:44:40Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Created page with &amp;quot;&amp;lt;includeonly&amp;gt;&amp;lt;!--  --&amp;gt;{{#switch:{{lc:{{{1}}}}} 	|sword=&amp;lt;h2&amp;gt;Swords&amp;lt;/h2&amp;gt;Swords are versatile weapons that can be used close to mid range. Swords have a variety of attack patterns, making them useful in most situations. 	|#default=&amp;lt;span style=&amp;quot;color:red; font-size:20px&amp;quot;&amp;gt;Template:WeaponPageFlavorText couldn&amp;#039;t match the provided weapon type. Follow the link for documentation to add the type.&amp;lt;/span&amp;gt;}}&amp;lt;!--  --&amp;gt;&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt; &amp;lt;pre&amp;gt;{{WeaponPageFlavorText|Sord}}...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;{{#switch:{{lc:{{{1}}}}}&lt;br /&gt;
	|sword=&amp;lt;h2&amp;gt;Swords&amp;lt;/h2&amp;gt;[[Swords]] are versatile weapons that can be used close to mid range. Swords have a variety of attack patterns, making them useful in most situations.&lt;br /&gt;
	|#default=&amp;lt;span style=&amp;quot;color:red; font-size:20px&amp;quot;&amp;gt;[[Template:WeaponPageFlavorText]] couldn&#039;t match the provided weapon type. Follow the link for documentation to add the type.&amp;lt;/span&amp;gt;}}&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;{{WeaponPageFlavorText|Sord}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{WeaponPageFlavorText|Sord}}&lt;br /&gt;
&amp;lt;pre&amp;gt;{{WeaponPageFlavorText|Sword}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{WeaponPageFlavorText|Sword}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Crafting&amp;diff=65537</id>
		<title>Crafting</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Crafting&amp;diff=65537"/>
		<updated>2026-04-13T11:39:00Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Replacing wikitext with scribunto module call&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;metadesc&amp;gt;Dark and Darker Wiki Crafting Guide&amp;lt;/metadesc&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{UpdateStatus|Crafting Data|Crafting}}&lt;br /&gt;
&lt;br /&gt;
Crafting is done through [[Merchants]]. You can craft [[Weapons|Silver]] and [[Weapons|Gold]] Weapons, and you can craft [[Armors|Copper]], [[Armors|Cobalt]], [[Armors|Rubysilver]], and [[Armors|Gold]] Armors. &lt;br /&gt;
&lt;br /&gt;
Some crafts are only unlocked after completing particular [[Quests]]. (specifics unknown)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt;All Craftables&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Too see craftable list from an [[Materials|ingredient]] go to their individual page. (Example: [[Bone]])&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Craft|crafting_page}}&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65536</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65536"/>
		<updated>2026-04-13T11:37:05Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added functions to handle tab toggles for crafting page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	if amount then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,merchant_name,false,&amp;quot;-1&amp;quot;,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = [=[ class=&amp;quot;tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	if active == &amp;quot;yes&amp;quot; then&lt;br /&gt;
		class = [=[ class=&amp;quot;selected-tab tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return [=[&amp;lt;div style=&amp;quot;display:inline-block&amp;quot;]=]..class..[=[ data-tabid=&amp;quot;]=]..tabid..[=[&amp;quot; data-tab=&amp;quot;]=]..title:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;)..[=[&amp;quot; title=&amp;quot;]=]..title..[=[&amp;quot;&amp;gt;]=]..content..[=[&amp;lt;/div&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.crafting_page(frame)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;div style=&amp;quot;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;None&amp;quot;,&amp;quot;[[File:None_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		if merchant_name == &amp;quot;Skeleton&amp;quot; or merchant_name == &amp;quot;Cockatrice&amp;quot; then&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;_Merchant.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant_name:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant_name..&amp;quot;.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;All&amp;quot;,&amp;quot;[[File:All_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;yes&amp;quot;,&amp;quot;0&amp;quot;)&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;center&amp;gt;&amp;lt;div class=&#039;None-data&#039;&amp;gt; &amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for _,merchant_name in ipairs(MD.craft_order) do&lt;br /&gt;
		text[#text+1] = [=[&amp;lt;div class=&amp;quot;]=]..merchant_name:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;)..[=[-data All-data&amp;quot;&amp;gt;]=]&lt;br /&gt;
		text[#text+1] = p.draw_table({args={merchant=merchant_name}})&lt;br /&gt;
		text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/center&amp;gt;&amp;quot;&lt;br /&gt;
	return table.concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_ingredient_table(wt,ingredient,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[item],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	draw_ingredient_table(wt,item,category,data)&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ore&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Merchant&amp;diff=65535</id>
		<title>Module:Merchant</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Merchant&amp;diff=65535"/>
		<updated>2026-04-13T11:18:37Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Removed craft_order from quest page.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Merchant.json don&#039;t work with next() or the # operator&lt;br /&gt;
--- @param t table&lt;br /&gt;
--- @return number&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing merchant information&lt;br /&gt;
--- @param quest table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function row(name,quest)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	if quest.requiredquest then&lt;br /&gt;
		text[#text+1] = &amp;quot;Complete quest:&amp;lt;br&amp;gt;&amp;quot;..quest.requiredquest&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for i=1,count(quest.objectives) do&lt;br /&gt;
		if i ~= 1 then text[#text+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		text[#text+1] = quest.objectives[i] and quest.objectives[i].summary&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..quest.rewards..&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	return concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Return a table row of appropriate headers cells&lt;br /&gt;
--- @return string&lt;br /&gt;
local function table_header(title)&lt;br /&gt;
	return [=[&amp;lt;table class=&amp;quot;wikitable mw-collapsible jquery-tablesorter&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;margin-top:5px; padding-bottom:5px; width:100%; color:#eee; background:transparent; text-align:center; vertical-align:middle;&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;caption style=&amp;quot;font-size:20px; white-space:nowrap; border:1px solid; padding:3px;&amp;quot;&amp;gt;Chapter: ]=]..title..[=[ &amp;lt;/caption&amp;gt;&lt;br /&gt;
	&amp;lt;tr style=&amp;quot;background-color: rgb(220,220,220,0.2)&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Quest&amp;lt;/th&amp;gt;&lt;br /&gt;
	&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Prerequisites&amp;lt;/th&amp;gt;&lt;br /&gt;
	&amp;lt;th style=&amp;quot;width:21%&amp;quot; class=&amp;quot;tooltip&amp;quot;&amp;gt;*Task(s)&amp;lt;span class=&amp;quot;tooltiptext&amp;quot; style=&amp;quot;font-weight:normal;&amp;quot;&amp;gt;Items need to be [[Looted_Handled_Supplied|looted]] unless specified otherwise.&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&lt;br /&gt;
	&amp;lt;th style=&amp;quot;width:21%&amp;quot;&amp;gt;Rewards&amp;lt;/th&amp;gt;&lt;br /&gt;
	&amp;lt;/tr&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create monster table wikitext&lt;br /&gt;
--- @param title string&lt;br /&gt;
--- @param chapter table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function draw_table(title,chapter)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = table_header(title)&lt;br /&gt;
	for _,quest in ipairs(chapter.order) do&lt;br /&gt;
		text[#text+1] = row(quest,chapter[quest])&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
	return concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create a wikitext table for every quest chapter&lt;br /&gt;
--- @param merchant any&lt;br /&gt;
--- @return string&lt;br /&gt;
local function draw_chapters(merchant)&lt;br /&gt;
	if not MD[merchant].questchapters then return &amp;quot;&amp;quot; end&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;div class=&amp;quot;line&amp;quot; style=&amp;quot;margin:30px 0px 5px; background-image:linear-gradient(to right,#0A0A0A,#646464,#0A0A0A)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;font-weight:bold;font-size:34px;text-align:center&amp;quot;&amp;gt;]=]..merchant..&amp;quot;&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
	for _,chapter in ipairs(MD[merchant].questchapters.order) do&lt;br /&gt;
		text[#text+1] = draw_table(chapter,MD[merchant].questchapters[chapter])&lt;br /&gt;
	end&lt;br /&gt;
	return concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create a tab toggle&lt;br /&gt;
--- @param title string&lt;br /&gt;
--- @param content string&lt;br /&gt;
--- @param active string&lt;br /&gt;
--- @param tabid string&lt;br /&gt;
--- @param tabname string&lt;br /&gt;
--- @return string&lt;br /&gt;
local function tab_toggle(title,content,active,tabid,tabname)&lt;br /&gt;
	local class = [=[ class=&amp;quot;tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	if active == &amp;quot;yes&amp;quot; then&lt;br /&gt;
		class = [=[ class=&amp;quot;selected-tab tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return [=[&amp;lt;div style=&amp;quot;display:inline-block&amp;quot;]=]..class..[=[ data-tabid=&amp;quot;]=]..tabid..[=[&amp;quot; data-tab=&amp;quot;]=]..title:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;)..[=[&amp;quot; title=&amp;quot;]=]..title..[=[&amp;quot;&amp;gt;]=]..content..[=[&amp;lt;/div&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Collect MD top-pevel keys into a sorted table&lt;br /&gt;
---@return table&lt;br /&gt;
local function keys_in_alph_order()&lt;br /&gt;
	local set = {}&lt;br /&gt;
	for key,_ in pairs(MD) do&lt;br /&gt;
		if key ~= &amp;quot;craft_order&amp;quot; then set[#set+1] = key end&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(set)&lt;br /&gt;
	return set&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create an array of merchant tabs and their associated quest sections&lt;br /&gt;
--- @param frame any&lt;br /&gt;
--- @return string&lt;br /&gt;
function p.draw_page(frame)&lt;br /&gt;
	local merchant_list = keys_in_alph_order()&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;p style=&amp;quot;text-align: center; font-weight: bold;&amp;quot;&amp;gt; &#039;&#039;Click All to see all quests, or a merchant icon to see quests specific to that merchant!&#039;&#039; &amp;lt;/p&amp;gt;&amp;lt;div style=&amp;quot;display:flex; flex-direction:row; flex-wrap:wrap; justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;None&amp;quot;,&amp;quot;[[File:None_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;yes&amp;quot;,&amp;quot;0&amp;quot;,&amp;quot;&amp;quot;)&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;All&amp;quot;,&amp;quot;[[File:All_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;,&amp;quot;&amp;quot;)&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;/div&amp;gt;&amp;lt;div style=&amp;quot;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	for _,merchant in ipairs(merchant_list) do&lt;br /&gt;
		if merchant == &amp;quot;Skeleton&amp;quot; or merchant == &amp;quot;Cockatrice&amp;quot; then&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant..&amp;quot;_Merchant.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;,&amp;quot;&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant..&amp;quot;.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;,&amp;quot;&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;None-data&amp;quot; style=&amp;quot;&amp;quot;&amp;gt; &amp;lt;/div&amp;gt;]=]&lt;br /&gt;
	for _,merchant in ipairs(merchant_list) do&lt;br /&gt;
		text[#text+1] = [=[&amp;lt;div class=&amp;quot;]=]..merchant:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;)..[=[-data All-data&amp;quot; style=&amp;quot;display:none&amp;quot;&amp;gt;]=]&lt;br /&gt;
		text[#text+1] = draw_chapters(merchant)&lt;br /&gt;
		text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
---- Test on wiki with:  mw.log(p.draw_page())&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- TODO&lt;br /&gt;
-- Look into highlighting on hover.  Hover prereq cell should highlight the prereq&#039;s quest row.&lt;br /&gt;
--		Put quest name in class and then use class in prereq cell to highlight the quest row on prereq hover.&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Merchant&amp;diff=65534</id>
		<title>Module:Merchant</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Merchant&amp;diff=65534"/>
		<updated>2026-04-13T11:16:09Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added minor data validation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Merchant.json don&#039;t work with next() or the # operator&lt;br /&gt;
--- @param t table&lt;br /&gt;
--- @return number&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing merchant information&lt;br /&gt;
--- @param quest table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function row(name,quest)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	if quest.requiredquest then&lt;br /&gt;
		text[#text+1] = &amp;quot;Complete quest:&amp;lt;br&amp;gt;&amp;quot;..quest.requiredquest&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for i=1,count(quest.objectives) do&lt;br /&gt;
		if i ~= 1 then text[#text+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		text[#text+1] = quest.objectives[i] and quest.objectives[i].summary&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..quest.rewards..&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	return concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Return a table row of appropriate headers cells&lt;br /&gt;
--- @return string&lt;br /&gt;
local function table_header(title)&lt;br /&gt;
	return [=[&amp;lt;table class=&amp;quot;wikitable mw-collapsible jquery-tablesorter&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;margin-top:5px; padding-bottom:5px; width:100%; color:#eee; background:transparent; text-align:center; vertical-align:middle;&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;caption style=&amp;quot;font-size:20px; white-space:nowrap; border:1px solid; padding:3px;&amp;quot;&amp;gt;Chapter: ]=]..title..[=[ &amp;lt;/caption&amp;gt;&lt;br /&gt;
	&amp;lt;tr style=&amp;quot;background-color: rgb(220,220,220,0.2)&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Quest&amp;lt;/th&amp;gt;&lt;br /&gt;
	&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Prerequisites&amp;lt;/th&amp;gt;&lt;br /&gt;
	&amp;lt;th style=&amp;quot;width:21%&amp;quot; class=&amp;quot;tooltip&amp;quot;&amp;gt;*Task(s)&amp;lt;span class=&amp;quot;tooltiptext&amp;quot; style=&amp;quot;font-weight:normal;&amp;quot;&amp;gt;Items need to be [[Looted_Handled_Supplied|looted]] unless specified otherwise.&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&lt;br /&gt;
	&amp;lt;th style=&amp;quot;width:21%&amp;quot;&amp;gt;Rewards&amp;lt;/th&amp;gt;&lt;br /&gt;
	&amp;lt;/tr&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create monster table wikitext&lt;br /&gt;
--- @param title string&lt;br /&gt;
--- @param chapter table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function draw_table(title,chapter)&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = table_header(title)&lt;br /&gt;
	for _,quest in ipairs(chapter.order) do&lt;br /&gt;
		text[#text+1] = row(quest,chapter[quest])&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
	return concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create a wikitext table for every quest chapter&lt;br /&gt;
--- @param merchant any&lt;br /&gt;
--- @return string&lt;br /&gt;
local function draw_chapters(merchant)&lt;br /&gt;
	if not MD[merchant].questchapters then return &amp;quot;&amp;quot; end&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;div class=&amp;quot;line&amp;quot; style=&amp;quot;margin:30px 0px 5px; background-image:linear-gradient(to right,#0A0A0A,#646464,#0A0A0A)&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;font-weight:bold;font-size:34px;text-align:center&amp;quot;&amp;gt;]=]..merchant..&amp;quot;&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
	for _,chapter in ipairs(MD[merchant].questchapters.order) do&lt;br /&gt;
		text[#text+1] = draw_table(chapter,MD[merchant].questchapters[chapter])&lt;br /&gt;
	end&lt;br /&gt;
	return concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create a tab toggle&lt;br /&gt;
--- @param title string&lt;br /&gt;
--- @param content string&lt;br /&gt;
--- @param active string&lt;br /&gt;
--- @param tabid string&lt;br /&gt;
--- @param tabname string&lt;br /&gt;
--- @return string&lt;br /&gt;
local function tab_toggle(title,content,active,tabid,tabname)&lt;br /&gt;
	local class = [=[ class=&amp;quot;tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	if active == &amp;quot;yes&amp;quot; then&lt;br /&gt;
		class = [=[ class=&amp;quot;selected-tab tab-toggle tab&amp;quot;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return [=[&amp;lt;div style=&amp;quot;display:inline-block&amp;quot;]=]..class..[=[ data-tabid=&amp;quot;]=]..tabid..[=[&amp;quot; data-tab=&amp;quot;]=]..title:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;)..[=[&amp;quot; title=&amp;quot;]=]..title..[=[&amp;quot;&amp;gt;]=]..content..[=[&amp;lt;/div&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Collect MD top-pevel keys into a sorted table&lt;br /&gt;
---@return table&lt;br /&gt;
local function keys_in_alph_order()&lt;br /&gt;
	local set = {}&lt;br /&gt;
	for key,_ in pairs(MD) do&lt;br /&gt;
		set[#set+1] = key&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(set)&lt;br /&gt;
	return set&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create an array of merchant tabs and their associated quest sections&lt;br /&gt;
--- @param frame any&lt;br /&gt;
--- @return string&lt;br /&gt;
function p.draw_page(frame)&lt;br /&gt;
	local merchant_list = keys_in_alph_order()&lt;br /&gt;
	local text = {}&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;p style=&amp;quot;text-align: center; font-weight: bold;&amp;quot;&amp;gt; &#039;&#039;Click All to see all quests, or a merchant icon to see quests specific to that merchant!&#039;&#039; &amp;lt;/p&amp;gt;&amp;lt;div style=&amp;quot;display:flex; flex-direction:row; flex-wrap:wrap; justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;None&amp;quot;,&amp;quot;[[File:None_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;yes&amp;quot;,&amp;quot;0&amp;quot;,&amp;quot;&amp;quot;)&lt;br /&gt;
	text[#text+1] = tab_toggle(&amp;quot;All&amp;quot;,&amp;quot;[[File:All_Merchants_Tab.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;,&amp;quot;&amp;quot;)&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;/div&amp;gt;&amp;lt;div style=&amp;quot;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;&amp;quot;&amp;gt;]=]&lt;br /&gt;
	for _,merchant in ipairs(merchant_list) do&lt;br /&gt;
		if merchant == &amp;quot;Skeleton&amp;quot; or merchant == &amp;quot;Cockatrice&amp;quot; then&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant..&amp;quot;_Merchant.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;,&amp;quot;&amp;quot;)&lt;br /&gt;
		else&lt;br /&gt;
			text[#text+1] = tab_toggle(merchant:gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;),&amp;quot;[[File:&amp;quot;..merchant..&amp;quot;.png|link=|130px]]&amp;quot;,&amp;quot;&amp;quot;,&amp;quot;0&amp;quot;,&amp;quot;&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	text[#text+1] = [=[&amp;lt;/div&amp;gt;&amp;lt;div class=&amp;quot;None-data&amp;quot; style=&amp;quot;&amp;quot;&amp;gt; &amp;lt;/div&amp;gt;]=]&lt;br /&gt;
	for _,merchant in ipairs(merchant_list) do&lt;br /&gt;
		text[#text+1] = [=[&amp;lt;div class=&amp;quot;]=]..merchant:gsub(&amp;quot; &amp;quot;,&amp;quot;&amp;quot;):gsub(&amp;quot;&#039;&amp;quot;,&amp;quot;&amp;quot;)..[=[-data All-data&amp;quot; style=&amp;quot;display:none&amp;quot;&amp;gt;]=]&lt;br /&gt;
		text[#text+1] = draw_chapters(merchant)&lt;br /&gt;
		text[#text+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return concat(text)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
---- Test on wiki with:  mw.log(p.draw_page())&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- TODO&lt;br /&gt;
-- Look into highlighting on hover.  Hover prereq cell should highlight the prereq&#039;s quest row.&lt;br /&gt;
--		Put quest name in class and then use class in prereq cell to highlight the quest row on prereq hover.&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Crafting&amp;diff=65533</id>
		<title>Crafting</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Crafting&amp;diff=65533"/>
		<updated>2026-04-13T11:06:25Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Temporary update for crafting tables.  The module will eventually handle which merchants are to be displayed.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;metadesc&amp;gt;Dark and Darker Wiki Crafting Guide&amp;lt;/metadesc&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{UpdateStatus|Crafting Data|Crafting}}&lt;br /&gt;
&lt;br /&gt;
Crafting is done through [[Merchants]]. You can craft [[Weapons|Silver]] and [[Weapons|Gold]] Weapons, and you can craft [[Armors|Copper]], [[Armors|Cobalt]], [[Armors|Rubysilver]], and [[Armors|Gold]] Armors. &lt;br /&gt;
&lt;br /&gt;
Some crafts are only unlocked after completing particular [[Quests]]. (specifics unknown)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h2&amp;gt;All Craftables&amp;lt;/h2&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For craftable list from an [[Materials|ingredient]] go to their individual page. (Example. [[Bone]])&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;&amp;quot;&amp;gt;&lt;br /&gt;
	{{TabToggle|All|[[File:all-merchants-tab.jpg|link=|x140px]]|active=yes}}&lt;br /&gt;
	{{TabToggle|Alchemist|[[File:Alchemist.png|link=|x140px]]}}&lt;br /&gt;
	{{TabToggle|Armourer|[[File:Armourer.png|link=|x140px]]}}&lt;br /&gt;
	{{TabToggle|Goldsmith|[[File:Goldsmith.png|link=|x140px]]}}&lt;br /&gt;
	{{TabToggle|Leathersmith|[[File:Leathersmith.png|link=|x140px]]}}&lt;br /&gt;
	{{TabToggle|Tailor|[[File:Tailor.png|link=|x140px]]}}&lt;br /&gt;
	{{TabToggle|Weaponsmith|[[File:Weaponsmith.png|link=|x140px]]}}&lt;br /&gt;
	{{TabToggle|Woodsman|[[File:Woodsman.png|link=|x140px]]}}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&amp;quot;Alchemist-data All-data&amp;quot;&amp;gt;&lt;br /&gt;
		{{#invoke:Craft|draw_table|merchant=Alchemist}}&amp;lt;/div&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&amp;quot;Armourer-data All-data&amp;quot;&amp;gt;&lt;br /&gt;
		{{#invoke:Craft|draw_table|merchant=Armourer}}&amp;lt;/div&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&amp;quot;Goldsmith-data All-data&amp;quot;&amp;gt;&lt;br /&gt;
		{{#invoke:Craft|draw_table|merchant=Goldsmith}}&amp;lt;/div&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&amp;quot;Leathersmith-data All-data&amp;quot;&amp;gt;&lt;br /&gt;
		{{#invoke:Craft|draw_table|merchant=Leathersmith}}&amp;lt;/div&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&amp;quot;Tailor-data All-data&amp;quot;&amp;gt;&lt;br /&gt;
		{{#invoke:Craft|draw_table|merchant=Tailor}}&amp;lt;/div&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&amp;quot;Weaponsmith-data All-data&amp;quot;&amp;gt;&lt;br /&gt;
		{{#invoke:Craft|draw_table|merchant=Weaponsmith}}&amp;lt;/div&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&amp;quot;Woodsman-data All-data&amp;quot;&amp;gt;&lt;br /&gt;
		{{#invoke:Craft|draw_table|merchant=Woodsman}}&amp;lt;/div&amp;gt;&amp;lt;/center&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Misc&amp;diff=65532</id>
		<title>Module:Misc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Misc&amp;diff=65532"/>
		<updated>2026-04-13T10:47:18Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Misc.json&amp;quot;)--Global var holding tables from Data:Misc.json&lt;br /&gt;
local insert = table.insert&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the size of the item in pixels for the iconbox&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Misc.json don&#039;t work with next() or the # operator&lt;br /&gt;
--- @param t table&lt;br /&gt;
--- @return number&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the type of the armor as a string&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function get_type(item)&lt;br /&gt;
	--Assumes that the item only has one type&lt;br /&gt;
	for key,_ in pairs(item.types) do&lt;br /&gt;
		--Scribunto uses meta tables so we can&#039;t use next() here&lt;br /&gt;
		return key&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create css/html wikitext for iconbox&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function iconbox(item)&lt;br /&gt;
	local color = 2&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;display:inline-flex;width:max-content;max-width:initial;flex-direction:column;align-items:center;flex-wrap:wrap;white-space:pre-wrap&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			..&amp;quot;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
				..&amp;quot;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..&amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxcount..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			..&amp;quot;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
--- @param values string|table&lt;br /&gt;
--- @param misc table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function color_values(values,misc)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(misc.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..misc.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--TODO this assumes i is the same as the color rating, this may result in a bug&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(misc.rarities) do&lt;br /&gt;
		wikitext = wikitext..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return an array whose values have been divided by the provided integer&lt;br /&gt;
--- @param values table&lt;br /&gt;
--- @param multiplier number&lt;br /&gt;
local function mult_array(values, multiplier)&lt;br /&gt;
	local result = {}&lt;br /&gt;
	for i, value in pairs(values) do&lt;br /&gt;
		if i ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
			result[i] = tonumber(string.format(&amp;quot;%.3f&amp;quot;, value * multiplier))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing misc information&lt;br /&gt;
--- @param misc table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function row(misc,table_type)&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..iconbox(misc))&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(misc.sellprices,misc))&lt;br /&gt;
	if table_type ~= &amp;quot;Container&amp;quot; then&lt;br /&gt;
		local max_stack_per_slotsize = misc.maxcount / (misc.invwidth * misc.invheight)&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(misc.ap,misc))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(mult_array(misc.sellprices,max_stack_per_slotsize),misc))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(mult_array(misc.ap,max_stack_per_slotsize),misc))&lt;br /&gt;
	else&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(misc.maxcontentscount,misc))&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a list of misc keys to be used in the table&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return table - strings&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	return MD[frame.args[1]] or {}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Return a table row of appropriate headers cells&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function table_header(frame)&lt;br /&gt;
	if (frame.args[1]):lower() == &amp;quot;container&amp;quot; then&lt;br /&gt;
		return [=[&amp;lt;table class=&amp;quot;wikitable sortable jquery-tablesorter stripedtable&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;color:#eee;background:transparent;text-align:center;vertical-align:middle&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sell Price&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Capacity&amp;lt;/th&amp;gt;]=]&lt;br /&gt;
	end&lt;br /&gt;
	-- Default misc table header&lt;br /&gt;
	return [=[&amp;lt;table class=&amp;quot;wikitable sortable jquery-tablesorter stripedtable&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;color:#eee;background:transparent;text-align:center;vertical-align:middle&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th rowspan=&amp;quot;2&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th colspan=&amp;quot;2&amp;quot;&amp;gt;Single Item&amp;lt;/th&amp;gt;&amp;lt;th colspan=&amp;quot;2&amp;quot;&amp;gt;Maximum Item Stack&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Sell Price&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Item Achieve&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sell Price / Slot Size&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Item Achieve / Slot Size&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create Misc table wikitext&lt;br /&gt;
---- Test on wiki with:  mw.log(p.draw_table({args={&amp;quot;Container&amp;quot;}}))&lt;br /&gt;
----                     mw.log(p.draw_table({args={&amp;quot;Gem&amp;quot;}}))&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	if not MD then return &amp;quot;&amp;lt;span style=&#039;color:red;&#039;&amp;gt;Error: Could not load Misc data in [[Module:Misc]]&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext, table_header(frame))&lt;br /&gt;
	for _, key in ipairs(get_list(frame)) do&lt;br /&gt;
		insert(wikitext, row(MD.Misc[key],frame.args[1]))&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext, &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.quest_information(frame)&lt;br /&gt;
	return &amp;quot;&amp;lt;h2&amp;gt;Quest Information&amp;lt;/h2&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;..MD.Misc[frame.args.name].questinformation..&amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Misc&amp;diff=65531</id>
		<title>Module:Misc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Misc&amp;diff=65531"/>
		<updated>2026-04-13T10:46:26Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Misc.json&amp;quot;)--Global var holding tables from Data:Misc.json&lt;br /&gt;
local insert = table.insert&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the size of the item in pixels for the iconbox&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Misc.json don&#039;t work with next() or the # operator&lt;br /&gt;
--- @param t table&lt;br /&gt;
--- @return number&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the type of the armor as a string&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function get_type(item)&lt;br /&gt;
	--Assumes that the item only has one type&lt;br /&gt;
	for key,_ in pairs(item.types) do&lt;br /&gt;
		--Scribunto uses meta tables so we can&#039;t use next() here&lt;br /&gt;
		return key&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create css/html wikitext for iconbox&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function iconbox(item)&lt;br /&gt;
	local color = 2&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;display:inline-flex;width:max-content;max-width:initial;flex-direction:column;align-items:center;flex-wrap:wrap;white-space:pre-wrap&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			..&amp;quot;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
				..&amp;quot;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..&amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxcount..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			..&amp;quot;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
--- @param values string|table&lt;br /&gt;
--- @param misc table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function color_values(values,misc)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(misc.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..misc.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--TODO this assumes i is the same as the color rating, this may result in a bug&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(misc.rarities) do&lt;br /&gt;
		wikitext = wikitext..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return an array whose values have been divided by the provided integer&lt;br /&gt;
--- @param values table&lt;br /&gt;
--- @param multiplier number&lt;br /&gt;
local function mult_array(values, multiplier)&lt;br /&gt;
	local result = {}&lt;br /&gt;
	for i, value in pairs(values) do&lt;br /&gt;
		if i ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
			result[i] = tonumber(string.format(&amp;quot;%.3f&amp;quot;, value * multiplier))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing misc information&lt;br /&gt;
--- @param misc table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function row(misc,table_type)&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..iconbox(misc))&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(misc.sellprices,misc))&lt;br /&gt;
	if table_type ~= &amp;quot;Container&amp;quot; then&lt;br /&gt;
		local max_stack_per_slotsize = misc.maxcount / (misc.invwidth * misc.invheight)&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(misc.ap,misc))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(mult_array(misc.sellprices,max_stack_per_slotsize),misc))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(mult_array(misc.ap,max_stack_per_slotsize),misc))&lt;br /&gt;
	else&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;..color_values(misc.maxcontentscount,misc))&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a list of misc keys to be used in the table&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return table - strings&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	return MD[frame.args[1]] or {}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Return a table row of appropriate headers cells&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function table_header(frame)&lt;br /&gt;
	if (frame.args[1]):lower() == &amp;quot;container&amp;quot; then&lt;br /&gt;
		return [=[&amp;lt;table class=&amp;quot;wikitable sortable jquery-tablesorter stripedtable&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;color:#eee;background:transparent;text-align:center;vertical-align:middle&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sell Price&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Capacity&amp;lt;/th&amp;gt;]=]&lt;br /&gt;
	end&lt;br /&gt;
	-- Default misc table header&lt;br /&gt;
	return [=[&amp;lt;table class=&amp;quot;wikitable sortable jquery-tablesorter stripedtable&amp;quot; cellspacing=&amp;quot;0&amp;quot; style=&amp;quot;color:#eee;background:transparent;text-align:center;vertical-align:middle&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th rowspan=&amp;quot;2&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;lt;th colspan=&amp;quot;2&amp;quot;&amp;gt;Single Item&amp;lt;/th&amp;gt;&amp;lt;th colspan=&amp;quot;2&amp;quot;&amp;gt;Maximum Item Stack&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&lt;br /&gt;
&amp;lt;tr&amp;gt;&amp;lt;th&amp;gt;Sell Price&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Item Achieve&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Sell Price / Slot Size&amp;lt;/th&amp;gt;&amp;lt;th&amp;gt;Item Achieve / Slot Size&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create Misc table wikitext&lt;br /&gt;
---- Test on wiki with:  mw.log(p.draw_table({args={&amp;quot;Container&amp;quot;}}))&lt;br /&gt;
----                     mw.log(p.draw_table({args={&amp;quot;Gem&amp;quot;}}))&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	if not MD then return &amp;quot;&amp;lt;span style=&#039;color:red;&#039;&amp;gt;Error: Could not load Misc data in [[Module:Misc]]&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext, table_header(frame))&lt;br /&gt;
	for _, key in ipairs(get_list(frame)) do&lt;br /&gt;
		insert(wikitext, row(MD.Misc[key],frame.args[1]))&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext, &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.quest_information(frame)&lt;br /&gt;
	return &amp;quot;&amp;lt;h2&amp;gt;Quest Information&amp;lt;/h2&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;..MD.Mist[frame.args.name].questinformation..&amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:Weapons&amp;diff=65530</id>
		<title>Template:Weapons</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:Weapons&amp;diff=65530"/>
		<updated>2026-04-13T10:35:10Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Replacing wikitext template with scribunto module&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#invoke:Weapon|references}}&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65529</id>
		<title>Module:Weapon</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65529"/>
		<updated>2026-04-13T10:34:54Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local WD = mw.loadJsonData(&amp;quot;Data:Weapon.json&amp;quot;) --Global var holding tables from Data:Weapon.json&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
---Get the item&#039;s size in pixels&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Weapon.json don&#039;t work with next() or the # operator&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
local function color_values(weapon, values)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(weapon.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..weapon.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(weapon.rarities) do&lt;br /&gt;
		wt = wt..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment&lt;br /&gt;
local function inline_block(weapon,stat)&lt;br /&gt;
	if weapon.stats[(stat):lower()] == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:inline-block;vertical-align:top;margin:0 15px 0 15px&#039;&amp;gt;&amp;lt;b style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..stat..&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[(stat):lower()])..&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell for the iconbox&lt;br /&gt;
local function name(item, is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local color = &amp;quot;2&amp;quot;&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local icon_amount = &amp;quot;&amp;quot;&lt;br /&gt;
	if item.maxammocount &amp;gt; 0 then&lt;br /&gt;
		icon_amount = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxammocount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..icon_amount&lt;br /&gt;
			..&amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats the weapon&#039;s list of classes into a table cell of classes separated by linebreaks&lt;br /&gt;
local function classes(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Class&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, class in ipairs(weapon.classes) do&lt;br /&gt;
		wt = wt..newline..class&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end -- Subsequent loops will prefix a linebreak&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of slottype and handtype&lt;br /&gt;
local function slot_type(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Slot&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.slottype..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..weapon.handtype..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of move speeds&lt;br /&gt;
local function move_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Movement Speed&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[&amp;quot;move speed&amp;quot;])..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of Armor rating and Magical Resistance&lt;br /&gt;
local function armor_rating(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Armor Rating&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..inline_block(weapon,&amp;quot;Armor Rating&amp;quot;)..inline_block(weapon,&amp;quot;Magical Resistance&amp;quot;)..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function impact_zones(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Impact Zones + Impact Power&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt= &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then&lt;br /&gt;
			for j,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
				if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then&lt;br /&gt;
					wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..ability..&amp;quot; &amp;quot;..attack..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
					for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do&lt;br /&gt;
						if k~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
						wt = wt..impact_zone&lt;br /&gt;
					end&lt;br /&gt;
					wt = wt..&amp;quot; + &amp;quot;..weapon.abilities[ability][attack].impactpower..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.impactresistance~=&amp;quot;&amp;quot; then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Impact Resist&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..weapon.impactresistance&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helper function: Collects move mult and prepare move mult for display&lt;br /&gt;
local function collect_multipliers(ability)&lt;br /&gt;
	local move, prep_move = &amp;quot;&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack_name in ipairs(ability.order) do&lt;br /&gt;
		if ability[attack_name].movementmultiplier then&lt;br /&gt;
			if i ~= 1 then&lt;br /&gt;
				move = move..&amp;quot;/&amp;quot;&lt;br /&gt;
				prep_move = prep_move..&amp;quot;/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			move = move..(ability[attack_name].movementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			prep_move = prep_move..(ability[attack_name].preparemovementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return move,prep_move&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing speed penalties for each action&lt;br /&gt;
local function action_move_speed_penalty(weapon,is_header)&lt;br /&gt;
	if is_header then return [=[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]&amp;lt;/th&amp;gt;]=] end&lt;br /&gt;
&lt;br /&gt;
	local order = {} --reconstruct order to exclude the other ability group&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then order[#order+1] = ability end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability_name in ipairs(order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		if ability_name==&amp;quot;Other&amp;quot; or ability_name==&amp;quot;Block&amp;quot; then wt = wt..ability_name..&amp;quot; Actions&amp;quot; else wt = wt..ability_name..&amp;quot; Attacks&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		local movement_multiplier,prepare_movement_multiplier = collect_multipliers(weapon.abilities[ability_name])&lt;br /&gt;
		if #prepare_movement_multiplier &amp;gt; 3 then&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
			..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..prepare_movement_multiplier&lt;br /&gt;
		else&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing base Physical and Magical damage, if present.&lt;br /&gt;
local function damage_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:25%&amp;quot;&amp;gt;Damage on Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	wt = wt..inline_block(weapon,&amp;quot;Physical Base Weapon Damage&amp;quot;)&lt;br /&gt;
			..inline_block(weapon,&amp;quot;Magical Base Weapon Damage&amp;quot;)&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---These stats get their own table cells, as such they are excluded from the stats cell to avoid needless redundancy&lt;br /&gt;
local exclude_stat = {[&amp;quot;Physical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Magical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Armor Rating&amp;quot;]=true,[&amp;quot;Magical Resistance&amp;quot;]=true,[&amp;quot;Move Speed&amp;quot;]=true}&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing any stats not covered by the rest of the row&lt;br /&gt;
local function stats(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:12%&amp;quot;&amp;gt;Stats&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,stat in ipairs(weapon.stats.order) do&lt;br /&gt;
		if not exclude_stat[stat] then&lt;br /&gt;
			wt = wt..inline_block(weapon,stat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing animation times for ranges weapons&lt;br /&gt;
local function animation_time(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Animation Times&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local windup = &amp;quot;&amp;lt;br&amp;gt;Windup:&amp;quot;&lt;br /&gt;
	local finish = &amp;quot;&amp;lt;br&amp;gt;Finish:&amp;quot;&lt;br /&gt;
	local reload = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Reload&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Attack 1&amp;quot;] then&lt;br /&gt;
		-- windup = windup..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Windup&lt;br /&gt;
		-- finish = finish..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Finish&lt;br /&gt;
	else&lt;br /&gt;
		windup = &amp;quot;&amp;quot;&lt;br /&gt;
		finish = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Other&amp;quot;] then&lt;br /&gt;
		-- reload = reload..weapon.animationtimes[&amp;quot;Other&amp;quot;].Reload&lt;br /&gt;
	else&lt;br /&gt;
		reload = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if #windup == 0 and #finish == 0 and #reload == 0 then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attack&amp;lt;/span&amp;gt;&amp;quot;..windup..finish..reload..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing hitslow for each attack&lt;br /&gt;
local function slowdown_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Slowdown On Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local ability_type = &amp;quot;Primary&amp;quot;&lt;br /&gt;
	if weapon.slottype == &amp;quot;Off-Hand&amp;quot; then ability_type = &amp;quot;Secondary&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Hitslow&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	local duration = &amp;quot; for &amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
				or weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Bonus&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then duration = duration..&amp;quot;/&amp;quot; end&lt;br /&gt;
		duration = duration..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Duration&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..duration..&amp;quot;s&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the projectile&#039;s initial speed&lt;br /&gt;
local function initial_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Initial Projectile Speed (m/s)&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.initialspeed..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the hitbox image resized by inventory size&lt;br /&gt;
local function hitbox(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Hitbox + Impact Resist&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;[[File:&amp;quot;..weapon.name..&amp;quot; Hitbox.png|link=|&amp;quot;..size(weapon)..&amp;quot;]]&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helps format a string containing either Combo Damage or damagetype values&lt;br /&gt;
local function format_combo(weapon,ability,value)&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability][attack][value] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing ability combo values per attack&lt;br /&gt;
local function combo(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Combo&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	if weapon.abilities[&amp;quot;Primary&amp;quot;] then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Secondary&amp;quot;] then&lt;br /&gt;
		if #wt ~= 0 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Secondary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Special&amp;quot;] then&lt;br /&gt;
		if wt:match(&amp;quot;Primary&amp;quot;) or wt:match(&amp;quot;Secondary&amp;quot;) then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Special Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function determine_table_type(weapon)&lt;br /&gt;
	if weapon.args then&lt;br /&gt;
		if weapon.args[1] == &amp;quot;Shield&amp;quot; then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif (weapon.args[1]):lower():match(&amp;quot;bow&amp;quot;) or weapon.args[1] == &amp;quot;Firearm&amp;quot; or weapon.args[1] == &amp;quot;Throwable&amp;quot; then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	elseif weapon.types then&lt;br /&gt;
		if weapon.types.Shield then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm or weapon.types.Throwable then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;Default&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Determines which table data goes into the table row&lt;br /&gt;
local row_type = {&lt;br /&gt;
	[&amp;quot;Shield&amp;quot;] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},&lt;br /&gt;
	[&amp;quot;Ranged&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,action_move_speed_penalty,slowdown_on_hit,initial_speed},&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing weapon information,&lt;br /&gt;
local function row(weapon, is_header)&lt;br /&gt;
	is_header = is_header or false&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,content in ipairs(row_type[determine_table_type(weapon)]) do&lt;br /&gt;
		wt[#wt+1] = content(weapon,is_header)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Returns a predetermined weapon list&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	if not (frame.args and frame.args[1] and frame.args[2]) then return {} end&lt;br /&gt;
	if not (WD[frame.args[1]] and WD[frame.args[1]][frame.args[2]]) then return {} end&lt;br /&gt;
&lt;br /&gt;
	return WD[frame.args[1]][frame.args[2]]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	local item_list = get_list(frame)&lt;br /&gt;
	if count(item_list) == 0 then return &amp;quot;&amp;lt;span style=&#039;font-size:24pt&#039;&amp;gt;There are currently no items of this type.&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = [[&amp;lt;table cellspacing=&amp;quot;0&amp;quot; class=&amp;quot;wikitable sortable jquery-tablesorter&amp;quot; style=&amp;quot;text-align:center; vertical-align:middle;&amp;quot;&amp;gt;]]&lt;br /&gt;
	wt[#wt+1] = row(frame, true)&lt;br /&gt;
	for _,item_name in ipairs(item_list) do&lt;br /&gt;
		wt[#wt+1] = row(WD.Weapon[item_name])&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[###########################################################################&lt;br /&gt;
                Functions for individual weapon pages&lt;br /&gt;
#############################################################################]=]&lt;br /&gt;
local function projectile(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.initialspeed ~= &amp;quot;&amp;quot; then &lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Projectile&amp;lt;/h2&amp;gt;Initial Speed: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.initialspeed)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.piercecount ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;Pierce: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.piercecount)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function damage_falloff(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.damagefalloff == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Damage Falloff&amp;lt;/h2&amp;gt;%s has damage falloff depending on airtime of the projectile.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Projectile falloff damage chart:&amp;lt;br&amp;gt;X: Air time (seconds)&amp;lt;br&amp;gt;Y: %% Damage&amp;quot;,weapon_name,weapon.initialspeed)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:500px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = weapon.damagefalloff&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;Learn more at [[Damage_Calculation#Projectile_Falloff|Projectile Falloff]]&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hitbox(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.hitbox.height then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Hitbox&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;Height: &amp;quot;..weapon.hitbox.height&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Width: &amp;quot;..weapon.hitbox.width&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Depth: &amp;quot;..weapon.hitbox.depth&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and has primary or secondary attacks&lt;br /&gt;
	if not (weapon.types[&amp;quot;Bow&amp;quot;] or weapon.types[&amp;quot;Crossow&amp;quot;] or weapon.types[&amp;quot;ThrowableStuff&amp;quot;])&lt;br /&gt;
			and (weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactzones or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactzones) then&lt;br /&gt;
		local impact_zones = weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactzones or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactzones&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;The weapon&#039;s hitbox doesn&#039;t necessarily determine a weapon&#039;s reach due to different arm extension during attack animations. Thus, shorter weapons can have better reach than longer weapons.&amp;lt;br&amp;gt;[[Impact Zones]] of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;:&amp;lt;div style=&#039;display:flex;align-items:center&#039;&amp;gt;&amp;lt;div&amp;gt;[[File:&amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot; Hitbox.png|x300px]]&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Green Zone deals &amp;quot;&lt;br /&gt;
		wt[#wt+1] = impact_zones[1]&lt;br /&gt;
		wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if impact_zones[2] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:orange&#039;&amp;gt;Orange Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[2]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if impact_zones[3] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Red Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[3]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;&#039;Disclaimer&#039;&#039;&#039;: The impact zone&#039;s locations are an approximation. The real hitboxes may be slightly larger, closer together, and/or follow the weapon&#039;s shape more closely. Image is not able to be updated automatically. If it is outdated, please report it to the [https://discord.gg/KbsxgACkFS Wiki Discord].&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactpower or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactpower then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Impact Power&#039;&#039;&#039; of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;: &amp;quot;&lt;br /&gt;
		wt[#wt+1] = tostring(weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactpower or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Impact power helps with breaking crates, barricades or any other breakables in game. The higher the impact power the faster something breaks.&amp;lt;br&amp;gt;Impact power also affects shield blocking. If the attacking weapon&#039;s impact power is higher than shield&#039;s impact power, the shield holder staggers, and vice versa.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function list_to_string(l,separator)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(l) do&lt;br /&gt;
		if i~=1 then ret = ret..separator end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo_format(wt,attacks,type)&lt;br /&gt;
	if type == &amp;quot;Other&amp;quot; or type == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
	local first = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = type&lt;br /&gt;
	wt[#wt+1] = &amp;quot; Attack: &amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; else first = name end&lt;br /&gt;
		wt[#wt+1] = attacks[name].damagetype&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; dealing &amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = attacks[name].combodamage&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; damage on each swing, with impact zone: &amp;quot;&lt;br /&gt;
	wt[#wt+1] = list_to_string(attacks[first].impactzones, &amp;quot;/&amp;quot;)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function attack_animation_times(wt,weapon,ability_name,attack_name)&lt;br /&gt;
	local attack = weapon.animationtimes[ability_name][attack_name]&lt;br /&gt;
	if attack[&amp;quot;Windup&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                           attack[&amp;quot;Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Hit&amp;quot;]               then wt[#wt+1] = string.format(&amp;quot;Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                              attack[&amp;quot;Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Windup&amp;quot;]     then wt[#wt+1] = string.format(&amp;quot;Second Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                    attack[&amp;quot;Second Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Hit&amp;quot;]        then wt[#wt+1] = string.format(&amp;quot;Second Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                       attack[&amp;quot;Second Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Recover&amp;quot;]           then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Recover: %s&amp;lt;br&amp;gt;&amp;quot;,           attack[&amp;quot;Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Alternate Recover&amp;quot;] then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Alternate Recover: %s&amp;lt;br&amp;gt;&amp;quot;, attack[&amp;quot;Alternate Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Finish&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Finish: %s&amp;lt;br&amp;gt;&amp;quot;,            attack[&amp;quot;Finish&amp;quot;]) end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function animation_format(wt, weapon, ability)&lt;br /&gt;
	if ability == &amp;quot;Other&amp;quot; or ability == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	for i, name in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		wt[#wt+1] = ability..&amp;quot; Combo: &amp;quot;..name..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		attack_animation_times(wt,weapon,ability,name)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo(wt, page_name)&lt;br /&gt;
	local weapon = WD.Weapon[page_name]&lt;br /&gt;
&lt;br /&gt;
	-- ensure that there is relevant data before continuing.&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and isn&#039;t shield, or at least isn&#039;t a Lantern Shield&lt;br /&gt;
	if not (weapon.types.Bow or weapon.types.Crossow or weapon.types.ThrowableStuff)&lt;br /&gt;
		and (not weapon.types.Shield or (weapon.name):match(&amp;quot;Lantern Shield&amp;quot;)) then&lt;br /&gt;
&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Attack Animation Time&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
			combo_format(wt,weapon.abilities[name],name)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Attack Times&#039;&#039;&#039;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
				animation_format(wt,weapon,name)&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;Reference [[Attack Times]] for a glossary of the terms used above.&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function block(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	if not weapon.impactresistance then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Block&amp;lt;/h2&amp;gt;%s has a block ability with an impact resistance of &#039;&#039;&#039;%s&#039;&#039;&#039;.&amp;lt;br&amp;gt;To learn more about impact resistance see [[Impact Power]].&amp;quot;, weapon.name, weapon.impactresistance)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function action_move_slow_penalty(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Action Movement Slow&amp;lt;/h2&amp;gt;When a player performs certain actions, such as attacking or defending themselves, they move slower.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		for _,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
			-- There&#039;s a bug where Riposte values are recorded within &amp;quot;Other&amp;quot; abilities&lt;br /&gt;
			-- Since ripose is covered in Special abilities, we will skip it here&lt;br /&gt;
			if attack == &amp;quot;Riposte&amp;quot; then break end&lt;br /&gt;
&lt;br /&gt;
			local movespeedadd = weapon.abilities[ability][attack].effects&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
								or &amp;quot;&amp;quot;&lt;br /&gt;
			local movement_multiplier = weapon.abilities[ability][attack].movementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
			local prepare_movement_multiplier = weapon.abilities[ability][attack].preparemovementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if ability == &amp;quot;Other&amp;quot; then wt[#wt+1] = attack else wt[#wt+1] = ability end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if #prepare_movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = prepare_movement_multiplier&lt;br /&gt;
			elseif #movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
			else&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movespeedadd&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			-- Everything except attacks within Other has invariant move mults&lt;br /&gt;
			-- So after processessing the first attack, we break the loop&lt;br /&gt;
			if ability ~= &amp;quot;Other&amp;quot; then break end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function artifact(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	if weapon.isartifact or weapon.artifactname == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Artifact&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;%s has a powerful [[Artifacts|Artifact]] named [[%s]].&amp;lt;/p&amp;gt;&amp;quot;, weapon.name, weapon.artifactname)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = &amp;quot;&amp;quot;&lt;br /&gt;
	if active then&lt;br /&gt;
		class = &amp;quot; class=&#039;selected-tab tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		class = &amp;quot; class=&#039;tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:inline-block&#039; %s data-tabid=&#039;%s&#039; data-tab=&#039;%s&#039; title=&#039;%s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,class,tabid,title,title,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reference_table(wt,table_type)&lt;br /&gt;
	local types_order = {&amp;quot;Sword&amp;quot;,&amp;quot;Dagger&amp;quot;,&amp;quot;Mace&amp;quot;,&amp;quot;Polearm&amp;quot;,&amp;quot;Axe&amp;quot;,&amp;quot;Throwable&amp;quot;,&amp;quot;Bow&amp;quot;,&amp;quot;Crossbow&amp;quot;,&amp;quot;Firearm&amp;quot;,&amp;quot;MagicStuff&amp;quot;,&amp;quot;Shield&amp;quot;,&amp;quot;LightSource&amp;quot;}&lt;br /&gt;
	local localization = {&lt;br /&gt;
		[&amp;quot;Sword&amp;quot;]        = &amp;quot;Swords&amp;quot;,&lt;br /&gt;
		[&amp;quot;Dagger&amp;quot;]       = &amp;quot;Daggers&amp;quot;,&lt;br /&gt;
		[&amp;quot;Mace&amp;quot;]         = &amp;quot;Maces&amp;quot;,&lt;br /&gt;
		[&amp;quot;Polearm&amp;quot;]      = &amp;quot;Polearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;Axe&amp;quot;]          = &amp;quot;Axes&amp;quot;,&lt;br /&gt;
		[&amp;quot;Throwable&amp;quot;]    = &amp;quot;Throwables&amp;quot;,&lt;br /&gt;
		[&amp;quot;Bow&amp;quot;]          = &amp;quot;Bows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Crossbow&amp;quot;]     = &amp;quot;Crossbows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Firearm&amp;quot;]      = &amp;quot;Firearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;MagicStuff&amp;quot;]   = &amp;quot;Magic Stuff&amp;quot;,&lt;br /&gt;
		[&amp;quot;Shield&amp;quot;]       = &amp;quot;Shields&amp;quot;,&lt;br /&gt;
		[&amp;quot;LightSource&amp;quot;]  = &amp;quot;Light Sources&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	local separator = &amp;quot;]] • [[&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:100%;text-align:center;vertical-align:middle;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for _,weapon_type in ipairs(types_order) do&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%&#039;&amp;gt;[[Weapons#%s|%s]]&amp;lt;/td&amp;gt;&amp;lt;td style=&#039;text-align-last:left;padding:10px 25px&#039;&amp;gt;&amp;quot;,localization[weapon_type],localization[weapon_type])&lt;br /&gt;
		if WD[weapon_type][table_type] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;[[&amp;quot;&lt;br /&gt;
			for i,v in ipairs(WD[weapon_type][table_type]) do&lt;br /&gt;
				if i~=1 then wt[#wt+1] = separator end&lt;br /&gt;
				wt[#wt+1] = v&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;]]&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.references()&lt;br /&gt;
	local table_types = {&amp;quot;Uncraftable&amp;quot;,&amp;quot;Craftable&amp;quot;,&amp;quot;Artifact&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;h2&amp;gt;References&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = tab_toggle(table_type,table_type..&amp;quot; Weapons&amp;quot;,i==1,&amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;&amp;quot;..table_type..&amp;quot;-data&#039;&amp;quot;&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;gt;&amp;quot; else wt[#wt+1] = &amp;quot;style=&#039;display:none&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		reference_table(wt,table_type)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local page_name = f.args.name&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	projectile(wt,page_name)&lt;br /&gt;
	damage_falloff(wt,page_name)&lt;br /&gt;
	hitbox(wt,page_name)&lt;br /&gt;
	combo(wt,page_name)&lt;br /&gt;
	block(wt,page_name)&lt;br /&gt;
	action_move_slow_penalty(wt,page_name)&lt;br /&gt;
	artifact(wt,page_name)&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on wiki with&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Crossbow&amp;quot;,&amp;quot;Uncraftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Shield&amp;quot;,&amp;quot;Craftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Polearm&amp;quot;,&amp;quot;Artifact&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={name=&amp;quot;Rapier&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65528</id>
		<title>Module:Weapon</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65528"/>
		<updated>2026-04-13T10:33:27Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Moved references out from page invoke&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local WD = mw.loadJsonData(&amp;quot;Data:Weapon.json&amp;quot;) --Global var holding tables from Data:Weapon.json&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
---Get the item&#039;s size in pixels&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Weapon.json don&#039;t work with next() or the # operator&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
local function color_values(weapon, values)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(weapon.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..weapon.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(weapon.rarities) do&lt;br /&gt;
		wt = wt..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment&lt;br /&gt;
local function inline_block(weapon,stat)&lt;br /&gt;
	if weapon.stats[(stat):lower()] == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:inline-block;vertical-align:top;margin:0 15px 0 15px&#039;&amp;gt;&amp;lt;b style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..stat..&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[(stat):lower()])..&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell for the iconbox&lt;br /&gt;
local function name(item, is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local color = &amp;quot;2&amp;quot;&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local icon_amount = &amp;quot;&amp;quot;&lt;br /&gt;
	if item.maxammocount &amp;gt; 0 then&lt;br /&gt;
		icon_amount = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxammocount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..icon_amount&lt;br /&gt;
			..&amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats the weapon&#039;s list of classes into a table cell of classes separated by linebreaks&lt;br /&gt;
local function classes(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Class&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, class in ipairs(weapon.classes) do&lt;br /&gt;
		wt = wt..newline..class&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end -- Subsequent loops will prefix a linebreak&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of slottype and handtype&lt;br /&gt;
local function slot_type(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Slot&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.slottype..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..weapon.handtype..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of move speeds&lt;br /&gt;
local function move_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Movement Speed&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[&amp;quot;move speed&amp;quot;])..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of Armor rating and Magical Resistance&lt;br /&gt;
local function armor_rating(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Armor Rating&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..inline_block(weapon,&amp;quot;Armor Rating&amp;quot;)..inline_block(weapon,&amp;quot;Magical Resistance&amp;quot;)..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function impact_zones(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Impact Zones + Impact Power&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt= &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then&lt;br /&gt;
			for j,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
				if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then&lt;br /&gt;
					wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..ability..&amp;quot; &amp;quot;..attack..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
					for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do&lt;br /&gt;
						if k~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
						wt = wt..impact_zone&lt;br /&gt;
					end&lt;br /&gt;
					wt = wt..&amp;quot; + &amp;quot;..weapon.abilities[ability][attack].impactpower..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.impactresistance~=&amp;quot;&amp;quot; then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Impact Resist&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..weapon.impactresistance&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helper function: Collects move mult and prepare move mult for display&lt;br /&gt;
local function collect_multipliers(ability)&lt;br /&gt;
	local move, prep_move = &amp;quot;&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack_name in ipairs(ability.order) do&lt;br /&gt;
		if ability[attack_name].movementmultiplier then&lt;br /&gt;
			if i ~= 1 then&lt;br /&gt;
				move = move..&amp;quot;/&amp;quot;&lt;br /&gt;
				prep_move = prep_move..&amp;quot;/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			move = move..(ability[attack_name].movementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			prep_move = prep_move..(ability[attack_name].preparemovementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return move,prep_move&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing speed penalties for each action&lt;br /&gt;
local function action_move_speed_penalty(weapon,is_header)&lt;br /&gt;
	if is_header then return [=[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]&amp;lt;/th&amp;gt;]=] end&lt;br /&gt;
&lt;br /&gt;
	local order = {} --reconstruct order to exclude the other ability group&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then order[#order+1] = ability end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability_name in ipairs(order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		if ability_name==&amp;quot;Other&amp;quot; or ability_name==&amp;quot;Block&amp;quot; then wt = wt..ability_name..&amp;quot; Actions&amp;quot; else wt = wt..ability_name..&amp;quot; Attacks&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		local movement_multiplier,prepare_movement_multiplier = collect_multipliers(weapon.abilities[ability_name])&lt;br /&gt;
		if #prepare_movement_multiplier &amp;gt; 3 then&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
			..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..prepare_movement_multiplier&lt;br /&gt;
		else&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing base Physical and Magical damage, if present.&lt;br /&gt;
local function damage_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:25%&amp;quot;&amp;gt;Damage on Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	wt = wt..inline_block(weapon,&amp;quot;Physical Base Weapon Damage&amp;quot;)&lt;br /&gt;
			..inline_block(weapon,&amp;quot;Magical Base Weapon Damage&amp;quot;)&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---These stats get their own table cells, as such they are excluded from the stats cell to avoid needless redundancy&lt;br /&gt;
local exclude_stat = {[&amp;quot;Physical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Magical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Armor Rating&amp;quot;]=true,[&amp;quot;Magical Resistance&amp;quot;]=true,[&amp;quot;Move Speed&amp;quot;]=true}&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing any stats not covered by the rest of the row&lt;br /&gt;
local function stats(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:12%&amp;quot;&amp;gt;Stats&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,stat in ipairs(weapon.stats.order) do&lt;br /&gt;
		if not exclude_stat[stat] then&lt;br /&gt;
			wt = wt..inline_block(weapon,stat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing animation times for ranges weapons&lt;br /&gt;
local function animation_time(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Animation Times&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local windup = &amp;quot;&amp;lt;br&amp;gt;Windup:&amp;quot;&lt;br /&gt;
	local finish = &amp;quot;&amp;lt;br&amp;gt;Finish:&amp;quot;&lt;br /&gt;
	local reload = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Reload&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Attack 1&amp;quot;] then&lt;br /&gt;
		-- windup = windup..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Windup&lt;br /&gt;
		-- finish = finish..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Finish&lt;br /&gt;
	else&lt;br /&gt;
		windup = &amp;quot;&amp;quot;&lt;br /&gt;
		finish = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Other&amp;quot;] then&lt;br /&gt;
		-- reload = reload..weapon.animationtimes[&amp;quot;Other&amp;quot;].Reload&lt;br /&gt;
	else&lt;br /&gt;
		reload = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if #windup == 0 and #finish == 0 and #reload == 0 then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attack&amp;lt;/span&amp;gt;&amp;quot;..windup..finish..reload..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing hitslow for each attack&lt;br /&gt;
local function slowdown_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Slowdown On Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local ability_type = &amp;quot;Primary&amp;quot;&lt;br /&gt;
	if weapon.slottype == &amp;quot;Off-Hand&amp;quot; then ability_type = &amp;quot;Secondary&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Hitslow&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	local duration = &amp;quot; for &amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
				or weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Bonus&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then duration = duration..&amp;quot;/&amp;quot; end&lt;br /&gt;
		duration = duration..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Duration&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..duration..&amp;quot;s&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the projectile&#039;s initial speed&lt;br /&gt;
local function initial_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Initial Projectile Speed (m/s)&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.initialspeed..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the hitbox image resized by inventory size&lt;br /&gt;
local function hitbox(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Hitbox + Impact Resist&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;[[File:&amp;quot;..weapon.name..&amp;quot; Hitbox.png|link=|&amp;quot;..size(weapon)..&amp;quot;]]&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helps format a string containing either Combo Damage or damagetype values&lt;br /&gt;
local function format_combo(weapon,ability,value)&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability][attack][value] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing ability combo values per attack&lt;br /&gt;
local function combo(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Combo&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	if weapon.abilities[&amp;quot;Primary&amp;quot;] then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Secondary&amp;quot;] then&lt;br /&gt;
		if #wt ~= 0 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Secondary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Special&amp;quot;] then&lt;br /&gt;
		if wt:match(&amp;quot;Primary&amp;quot;) or wt:match(&amp;quot;Secondary&amp;quot;) then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Special Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function determine_table_type(weapon)&lt;br /&gt;
	if weapon.args then&lt;br /&gt;
		if weapon.args[1] == &amp;quot;Shield&amp;quot; then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif (weapon.args[1]):lower():match(&amp;quot;bow&amp;quot;) or weapon.args[1] == &amp;quot;Firearm&amp;quot; or weapon.args[1] == &amp;quot;Throwable&amp;quot; then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	elseif weapon.types then&lt;br /&gt;
		if weapon.types.Shield then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm or weapon.types.Throwable then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;Default&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Determines which table data goes into the table row&lt;br /&gt;
local row_type = {&lt;br /&gt;
	[&amp;quot;Shield&amp;quot;] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},&lt;br /&gt;
	[&amp;quot;Ranged&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,action_move_speed_penalty,slowdown_on_hit,initial_speed},&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing weapon information,&lt;br /&gt;
local function row(weapon, is_header)&lt;br /&gt;
	is_header = is_header or false&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,content in ipairs(row_type[determine_table_type(weapon)]) do&lt;br /&gt;
		wt[#wt+1] = content(weapon,is_header)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Returns a predetermined weapon list&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	if not (frame.args and frame.args[1] and frame.args[2]) then return {} end&lt;br /&gt;
	if not (WD[frame.args[1]] and WD[frame.args[1]][frame.args[2]]) then return {} end&lt;br /&gt;
&lt;br /&gt;
	return WD[frame.args[1]][frame.args[2]]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	local item_list = get_list(frame)&lt;br /&gt;
	if count(item_list) == 0 then return &amp;quot;&amp;lt;span style=&#039;font-size:24pt&#039;&amp;gt;There are currently no items of this type.&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = [[&amp;lt;table cellspacing=&amp;quot;0&amp;quot; class=&amp;quot;wikitable sortable jquery-tablesorter&amp;quot; style=&amp;quot;text-align:center; vertical-align:middle;&amp;quot;&amp;gt;]]&lt;br /&gt;
	wt[#wt+1] = row(frame, true)&lt;br /&gt;
	for _,item_name in ipairs(item_list) do&lt;br /&gt;
		wt[#wt+1] = row(WD.Weapon[item_name])&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[###########################################################################&lt;br /&gt;
                Functions for individual weapon pages&lt;br /&gt;
#############################################################################]=]&lt;br /&gt;
local function projectile(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.initialspeed ~= &amp;quot;&amp;quot; then &lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Projectile&amp;lt;/h2&amp;gt;Initial Speed: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.initialspeed)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.piercecount ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;Pierce: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.piercecount)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function damage_falloff(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.damagefalloff == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Damage Falloff&amp;lt;/h2&amp;gt;%s has damage falloff depending on airtime of the projectile.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Projectile falloff damage chart:&amp;lt;br&amp;gt;X: Air time (seconds)&amp;lt;br&amp;gt;Y: %% Damage&amp;quot;,weapon_name,weapon.initialspeed)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:500px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = weapon.damagefalloff&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;Learn more at [[Damage_Calculation#Projectile_Falloff|Projectile Falloff]]&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hitbox(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.hitbox.height then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Hitbox&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;Height: &amp;quot;..weapon.hitbox.height&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Width: &amp;quot;..weapon.hitbox.width&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Depth: &amp;quot;..weapon.hitbox.depth&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and has primary or secondary attacks&lt;br /&gt;
	if not (weapon.types[&amp;quot;Bow&amp;quot;] or weapon.types[&amp;quot;Crossow&amp;quot;] or weapon.types[&amp;quot;ThrowableStuff&amp;quot;])&lt;br /&gt;
			and (weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactzones or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactzones) then&lt;br /&gt;
		local impact_zones = weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactzones or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactzones&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;The weapon&#039;s hitbox doesn&#039;t necessarily determine a weapon&#039;s reach due to different arm extension during attack animations. Thus, shorter weapons can have better reach than longer weapons.&amp;lt;br&amp;gt;[[Impact Zones]] of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;:&amp;lt;div style=&#039;display:flex;align-items:center&#039;&amp;gt;&amp;lt;div&amp;gt;[[File:&amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot; Hitbox.png|x300px]]&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Green Zone deals &amp;quot;&lt;br /&gt;
		wt[#wt+1] = impact_zones[1]&lt;br /&gt;
		wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if impact_zones[2] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:orange&#039;&amp;gt;Orange Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[2]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if impact_zones[3] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Red Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[3]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;&#039;Disclaimer&#039;&#039;&#039;: The impact zone&#039;s locations are an approximation. The real hitboxes may be slightly larger, closer together, and/or follow the weapon&#039;s shape more closely. Image is not able to be updated automatically. If it is outdated, please report it to the [https://discord.gg/KbsxgACkFS Wiki Discord].&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactpower or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactpower then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Impact Power&#039;&#039;&#039; of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;: &amp;quot;&lt;br /&gt;
		wt[#wt+1] = tostring(weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactpower or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Impact power helps with breaking crates, barricades or any other breakables in game. The higher the impact power the faster something breaks.&amp;lt;br&amp;gt;Impact power also affects shield blocking. If the attacking weapon&#039;s impact power is higher than shield&#039;s impact power, the shield holder staggers, and vice versa.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function list_to_string(l,separator)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(l) do&lt;br /&gt;
		if i~=1 then ret = ret..separator end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo_format(wt,attacks,type)&lt;br /&gt;
	if type == &amp;quot;Other&amp;quot; or type == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
	local first = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = type&lt;br /&gt;
	wt[#wt+1] = &amp;quot; Attack: &amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; else first = name end&lt;br /&gt;
		wt[#wt+1] = attacks[name].damagetype&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; dealing &amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = attacks[name].combodamage&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; damage on each swing, with impact zone: &amp;quot;&lt;br /&gt;
	wt[#wt+1] = list_to_string(attacks[first].impactzones, &amp;quot;/&amp;quot;)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function attack_animation_times(wt,weapon,ability_name,attack_name)&lt;br /&gt;
	local attack = weapon.animationtimes[ability_name][attack_name]&lt;br /&gt;
	if attack[&amp;quot;Windup&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                           attack[&amp;quot;Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Hit&amp;quot;]               then wt[#wt+1] = string.format(&amp;quot;Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                              attack[&amp;quot;Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Windup&amp;quot;]     then wt[#wt+1] = string.format(&amp;quot;Second Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                    attack[&amp;quot;Second Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Hit&amp;quot;]        then wt[#wt+1] = string.format(&amp;quot;Second Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                       attack[&amp;quot;Second Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Recover&amp;quot;]           then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Recover: %s&amp;lt;br&amp;gt;&amp;quot;,           attack[&amp;quot;Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Alternate Recover&amp;quot;] then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Alternate Recover: %s&amp;lt;br&amp;gt;&amp;quot;, attack[&amp;quot;Alternate Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Finish&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Finish: %s&amp;lt;br&amp;gt;&amp;quot;,            attack[&amp;quot;Finish&amp;quot;]) end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function animation_format(wt, weapon, ability)&lt;br /&gt;
	if ability == &amp;quot;Other&amp;quot; or ability == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	for i, name in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		wt[#wt+1] = ability..&amp;quot; Combo: &amp;quot;..name..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		attack_animation_times(wt,weapon,ability,name)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo(wt, page_name)&lt;br /&gt;
	local weapon = WD.Weapon[page_name]&lt;br /&gt;
&lt;br /&gt;
	-- ensure that there is relevant data before continuing.&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and isn&#039;t shield, or at least isn&#039;t a Lantern Shield&lt;br /&gt;
	if not (weapon.types.Bow or weapon.types.Crossow or weapon.types.ThrowableStuff)&lt;br /&gt;
		and (not weapon.types.Shield or (weapon.name):match(&amp;quot;Lantern Shield&amp;quot;)) then&lt;br /&gt;
&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Attack Animation Time&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
			combo_format(wt,weapon.abilities[name],name)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Attack Times&#039;&#039;&#039;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
				animation_format(wt,weapon,name)&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;Reference [[Attack Times]] for a glossary of the terms used above.&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function block(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	if not weapon.impactresistance then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Block&amp;lt;/h2&amp;gt;%s has a block ability with an impact resistance of &#039;&#039;&#039;%s&#039;&#039;&#039;.&amp;lt;br&amp;gt;To learn more about impact resistance see [[Impact Power]].&amp;quot;, weapon.name, weapon.impactresistance)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function action_move_slow_penalty(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Action Movement Slow&amp;lt;/h2&amp;gt;When a player performs certain actions, such as attacking or defending themselves, they move slower.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		for _,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
			-- There&#039;s a bug where Riposte values are recorded within &amp;quot;Other&amp;quot; abilities&lt;br /&gt;
			-- Since ripose is covered in Special abilities, we will skip it here&lt;br /&gt;
			if attack == &amp;quot;Riposte&amp;quot; then break end&lt;br /&gt;
&lt;br /&gt;
			local movespeedadd = weapon.abilities[ability][attack].effects&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
								or &amp;quot;&amp;quot;&lt;br /&gt;
			local movement_multiplier = weapon.abilities[ability][attack].movementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
			local prepare_movement_multiplier = weapon.abilities[ability][attack].preparemovementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if ability == &amp;quot;Other&amp;quot; then wt[#wt+1] = attack else wt[#wt+1] = ability end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if #prepare_movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = prepare_movement_multiplier&lt;br /&gt;
			elseif #movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
			else&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movespeedadd&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			-- Everything except attacks within Other has invariant move mults&lt;br /&gt;
			-- So after processessing the first attack, we break the loop&lt;br /&gt;
			if ability ~= &amp;quot;Other&amp;quot; then break end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function artifact(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	if weapon.isartifact or weapon.artifactname == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Artifact&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;%s has a powerful [[Artifacts|Artifact]] named [[%s]].&amp;lt;/p&amp;gt;&amp;quot;, weapon.name, weapon.artifactname)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = &amp;quot;&amp;quot;&lt;br /&gt;
	if active then&lt;br /&gt;
		class = &amp;quot; class=&#039;selected-tab tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		class = &amp;quot; class=&#039;tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:inline-block&#039; %s data-tabid=&#039;%s&#039; data-tab=&#039;%s&#039; title=&#039;%s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,class,tabid,title,title,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reference_table(wt,table_type)&lt;br /&gt;
	local types_order = {&amp;quot;Sword&amp;quot;,&amp;quot;Dagger&amp;quot;,&amp;quot;Mace&amp;quot;,&amp;quot;Polearm&amp;quot;,&amp;quot;Axe&amp;quot;,&amp;quot;Throwable&amp;quot;,&amp;quot;Bow&amp;quot;,&amp;quot;Crossbow&amp;quot;,&amp;quot;Firearm&amp;quot;,&amp;quot;MagicStuff&amp;quot;,&amp;quot;Shield&amp;quot;,&amp;quot;LightSource&amp;quot;}&lt;br /&gt;
	local localization = {&lt;br /&gt;
		[&amp;quot;Sword&amp;quot;]        = &amp;quot;Swords&amp;quot;,&lt;br /&gt;
		[&amp;quot;Dagger&amp;quot;]       = &amp;quot;Daggers&amp;quot;,&lt;br /&gt;
		[&amp;quot;Mace&amp;quot;]         = &amp;quot;Maces&amp;quot;,&lt;br /&gt;
		[&amp;quot;Polearm&amp;quot;]      = &amp;quot;Polearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;Axe&amp;quot;]          = &amp;quot;Axes&amp;quot;,&lt;br /&gt;
		[&amp;quot;Throwable&amp;quot;]    = &amp;quot;Throwables&amp;quot;,&lt;br /&gt;
		[&amp;quot;Bow&amp;quot;]          = &amp;quot;Bows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Crossbow&amp;quot;]     = &amp;quot;Crossbows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Firearm&amp;quot;]      = &amp;quot;Firearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;MagicStuff&amp;quot;]   = &amp;quot;Magic Stuff&amp;quot;,&lt;br /&gt;
		[&amp;quot;Shield&amp;quot;]       = &amp;quot;Shields&amp;quot;,&lt;br /&gt;
		[&amp;quot;LightSource&amp;quot;]  = &amp;quot;Light Sources&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	local separator = &amp;quot;]] • [[&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:100%;text-align:center;vertical-align:middle;margin-top:15px&#039;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td colspan=&#039;2&#039; style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for _,weapon_type in ipairs(types_order) do&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%&#039;&amp;gt;[[Weapons#%s|%s]]&amp;lt;/td&amp;gt;&amp;lt;td style=&#039;text-align-last:left;padding:10px 25px&#039;&amp;gt;&amp;quot;,localization[weapon_type],localization[weapon_type])&lt;br /&gt;
		if WD[weapon_type][table_type] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;[[&amp;quot;&lt;br /&gt;
			for i,v in ipairs(WD[weapon_type][table_type]) do&lt;br /&gt;
				if i~=1 then wt[#wt+1] = separator end&lt;br /&gt;
				wt[#wt+1] = v&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;]]&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.references()&lt;br /&gt;
	local table_types = {&amp;quot;Uncraftable&amp;quot;,&amp;quot;Craftable&amp;quot;,&amp;quot;Artifact&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;h2&amp;gt;References&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = tab_toggle(table_type,table_type..&amp;quot; Weapons&amp;quot;,i==1,&amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;&amp;quot;..table_type..&amp;quot;-data&#039;&amp;quot;&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;gt;&amp;quot; else wt[#wt+1] = &amp;quot;style=&#039;display:none&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		reference_table(wt,table_type)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local page_name = f.args.name&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	projectile(wt,page_name)&lt;br /&gt;
	damage_falloff(wt,page_name)&lt;br /&gt;
	hitbox(wt,page_name)&lt;br /&gt;
	combo(wt,page_name)&lt;br /&gt;
	block(wt,page_name)&lt;br /&gt;
	action_move_slow_penalty(wt,page_name)&lt;br /&gt;
	artifact(wt,page_name)&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on wiki with&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Crossbow&amp;quot;,&amp;quot;Uncraftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Shield&amp;quot;,&amp;quot;Craftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Polearm&amp;quot;,&amp;quot;Artifact&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={name=&amp;quot;Rapier&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65527</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65527"/>
		<updated>2026-04-13T10:23:42Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Fixed missing artifact abilities. Fixed missing infobox rows.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_index(item,rarity)&lt;br /&gt;
	return 1 + tonumber(rarity) - item.rarities[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, item)&lt;br /&gt;
	if not item.stats or count(item.stats.order) == 0 or item.numenchants[tonumber(args.rarity)] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(item.stats.order) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,item.stats[(stat_name):lower()][stat_index(item,args.rarity)] or item.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,item.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.haspassive or item.isartifact then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,item.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i ~= 1 then ret = ret..&amp;quot;, &amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	local i = 1&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if i ~= 1 then ret = ret..&amp;quot;, &amp;quot; end&lt;br /&gt;
		if v == type then&lt;br /&gt;
			ret = ret..k&lt;br /&gt;
		end&lt;br /&gt;
		i = i + 1&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	if weapon.maxcount ~= 1 then&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
	end&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,weapon.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Armor Type&amp;quot;,type_list(armor.types,&amp;quot;Armor&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,armor.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,accessory)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,accessory.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	local index = tonumber(args.rarity)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	if misc.maxcount ~= 1 then&lt;br /&gt;
		rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,misc.maxcount)&lt;br /&gt;
	end&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Max Contents&amp;quot;,misc.maxcontentscount)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[index])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[index])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active == rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Bloodthirst&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Golden Scarf&amp;quot;,category=&amp;quot;Armor&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Plate Pants&amp;quot;,category=&amp;quot;Armor&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Grimsmile Ring&amp;quot;,category=&amp;quot;Accessory&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Diamond&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Gold Coin Chest&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Quiver&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox/doc&amp;diff=65526</id>
		<title>Module:Infobox/doc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox/doc&amp;diff=65526"/>
		<updated>2026-04-13T09:07:29Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: /* Overview */ Removing currently unused data pages.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Overview=&lt;br /&gt;
Function for making Iconbox tabs. Data comes from various data jsons: [[Data:Weapon.json]], [[Data:Armor.json]], [[Data:Accessory.json]], [[Data:Misc.json]], [[Data:Monster.json]].&lt;br /&gt;
&lt;br /&gt;
=Functions=&lt;br /&gt;
==draw==&lt;br /&gt;
Creates an iconbox that floats to the right.&lt;br /&gt;
&lt;br /&gt;
===Parameters===&lt;br /&gt;
* &#039;&#039;name&#039;&#039; - &amp;lt;Name&amp;gt;&lt;br /&gt;
* &#039;&#039;is_active&#039;&#039; - {1,2,3,4,5,6,7,8,Common,Elite,Nightmare}&lt;br /&gt;
* &#039;&#039;category&#039;&#039; - {Weapon,Armor,Accessory,Utility,Misc,Monster,Prop}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==draw examples==&lt;br /&gt;
&amp;lt;h3 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Halberd,7,Weapon&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Halberd|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Halberd|category=Weapon}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Cinder,8,Weapon&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Cinder|is_active=8|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Cinder|is_active=8|category=Weapon}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Ranger Hood,5,Armor&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Ranger Hood|is_active=5|category=Armor}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Ranger Hood|is_active=5|category=Armor}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h1 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Code&amp;lt;/h1&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65525</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65525"/>
		<updated>2026-04-13T09:06:24Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Slightly more confident in this one.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_index(item,rarity)&lt;br /&gt;
	return 1 + tonumber(rarity) - item.rarities[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, item)&lt;br /&gt;
	if count(item.stats.order) == 0 or item.numenchants[args.rarity] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(item.stats.order) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,item.stats[(stat_name):lower()][stat_index(item,args.rarity)] or item.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,item.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.haspassive then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,item.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i~=1 then ret = ret..&amp;quot;,&amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if v==type then ret=ret..k end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,accessory)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,utility)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,utility.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,utility.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,utility.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(utility.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,misc)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,misc.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active == rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Cinder&amp;quot;,is_active=&amp;quot;8&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65524</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65524"/>
		<updated>2026-04-13T09:02:27Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Test failed, and I shouldn&amp;#039;t be testing it live.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link == &amp;quot;nolink&amp;quot; then&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	elseif args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.name, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, weapon)&lt;br /&gt;
	if count(weapon.stats.order) == 0 or weapon.numenchants[args.rarity] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(weapon.stats.order) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,weapon.stats[(stat_name):lower()][tonumber(args.rarity)] or weapon.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,weapon.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.haspassive then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,weapon.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i~=1 then ret = ret..&amp;quot;,&amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if v==type then ret=ret..k end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,accessory)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,utility)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,utility.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,utility.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,utility.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(utility.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,misc)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,misc.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,link=&amp;quot;nolink&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Cinder&amp;quot;,is_active=&amp;quot;8&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65523</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65523"/>
		<updated>2026-04-13T09:01:07Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Testing index fix&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_index(item,rarity)&lt;br /&gt;
	return 1 + tonumber(rarity) - item.rarities[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, item)&lt;br /&gt;
	if count(item.stats.order) == 0 or item.numenchants[args.rarity] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(item.stats.order) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,item.stats[(stat_name):lower()][stat_index(item,rarity)] or item.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,item.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if item.haspassive then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,item.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i~=1 then ret = ret..&amp;quot;,&amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if v==type then ret=ret..k end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,accessory)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,utility)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,utility.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,utility.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,utility.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(utility.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,misc)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,misc.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active == rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,link=&amp;quot;nolink&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Cinder&amp;quot;,is_active=&amp;quot;8&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:Armors&amp;diff=65522</id>
		<title>Template:Armors</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Template:Armors&amp;diff=65522"/>
		<updated>2026-04-13T07:33:30Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Replacing wikitext template with scribunto module&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{#invoke:Armor|references}}&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Armor&amp;diff=65521</id>
		<title>Module:Armor</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Armor&amp;diff=65521"/>
		<updated>2026-04-13T07:31:06Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Table header is made redundant and unnecessary by the tab toggles&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local AD = mw.loadJsonData(&amp;quot;Data:Armor.json&amp;quot;)&lt;br /&gt;
local insert = table.insert&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
local is_special = {[&amp;quot;Move Speed&amp;quot;]=true, [&amp;quot;Armor Rating&amp;quot;]=true, [&amp;quot;Magical Resistance&amp;quot;]=true}&lt;br /&gt;
local is_primitive = {[&amp;quot;Strength&amp;quot;]=true, [&amp;quot;Vigor&amp;quot;]=true, [&amp;quot;Agility&amp;quot;]=true, [&amp;quot;Dexterity&amp;quot;]=true, [&amp;quot;Will&amp;quot;]=true, [&amp;quot;Knowledge&amp;quot;]=true, [&amp;quot;Resourcefulness&amp;quot;]=true}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the type of the armor as a string&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function get_type(item)&lt;br /&gt;
	--Assumes that the item only has one type&lt;br /&gt;
	for key,_ in pairs(item.types) do&lt;br /&gt;
		--Scribunto uses meta tables so we can&#039;t use next() here&lt;br /&gt;
		return key&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the size of the armor in pixels&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Misc.json don&#039;t work with next() or the # operator&lt;br /&gt;
--- @param t table&lt;br /&gt;
--- @return number&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create css/html wikitext for iconbox&lt;br /&gt;
---@param item table&lt;br /&gt;
---@return string&lt;br /&gt;
local function iconbox(item)&lt;br /&gt;
	local color = &amp;quot;2&amp;quot;&lt;br /&gt;
	local caption = &amp;quot;[[&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		caption = caption..&amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name..&amp;quot;&amp;lt;/b&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;display:inline-flex;width:max-content;max-width:initial;flex-direction:column;align-items:center;flex-wrap:wrap;white-space:pre-wrap&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		..&amp;quot;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		..&amp;quot;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
		..&amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..get_type(item)..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		..caption..&amp;quot;]]&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Turns a list of classes into a wikitext string with line break separators&lt;br /&gt;
--- @param list table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function classes(list)&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot; --classes are few enough that we can just concatenate them without worrying about performance&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, class in ipairs(list) do&lt;br /&gt;
		wikitext = wikitext..newline..class&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
---@param values string|table&lt;br /&gt;
---@param armor table&lt;br /&gt;
---@return string&lt;br /&gt;
local function color_values(values,armor)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; then&lt;br /&gt;
		if count(armor.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..armor.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return values&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--TODO this assumes i is the same as the color rating, this may result in a bug&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, value in ipairs(values) do&lt;br /&gt;
		wikitext = wikitext..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..i..&amp;quot;&#039;&amp;gt;&amp;quot;..value..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment&lt;br /&gt;
--- @param stat string&lt;br /&gt;
--- @param armor table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function inline_block(stat, armor)&lt;br /&gt;
	if armor.stats[(stat):lower()] == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:inline-block;vertical-align:top;margin:0 15px 0 15px&#039;&amp;gt;&amp;lt;b style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..stat..&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..color_values(armor.stats[(stat):lower()],armor)..&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing armor information&lt;br /&gt;
--- @param armor table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function row(armor)&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
	insert(wikitext,iconbox(armor))&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	if armor.slottype ~= &amp;quot;Back&amp;quot; then&lt;br /&gt;
		insert(wikitext,classes(armor.classes))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
		insert(wikitext,color_values(armor.stats[&amp;quot;move speed&amp;quot;],armor))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
	-- elseif armor.stats[&amp;quot;move speed&amp;quot;] then&lt;br /&gt;
	-- 	insert(wikitext,&amp;quot;&amp;lt;span style=&#039;color:red;&#039;&amp;gt;Error: Back &amp;quot;..armor.name..&amp;quot; has unpresented move speed.&amp;lt;/span&amp;gt;&amp;quot;)&lt;br /&gt;
	-- elseif next(armor.classes) then&lt;br /&gt;
	-- 	insert(wikitext,&amp;quot;&amp;lt;span style=&#039;color:red;&#039;&amp;gt;Error: Back &amp;quot;..armor.name..&amp;quot; has unpresented class requirements.&amp;lt;/span&amp;gt;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	insert(wikitext,inline_block(&amp;quot;Armor Rating&amp;quot;,armor))&lt;br /&gt;
	insert(wikitext,inline_block(&amp;quot;Magical Resistance&amp;quot;,armor))&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	-- create the table data cell for attributes&lt;br /&gt;
	for _,stat in ipairs(armor.stats.order) do&lt;br /&gt;
		if is_primitive[stat] then&lt;br /&gt;
			insert(wikitext,inline_block(stat,armor))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	-- create the table data cell for the rest of the stats&lt;br /&gt;
	for _, stat in ipairs(armor.stats.order) do&lt;br /&gt;
		if not is_primitive[stat] and not is_special[stat] then&lt;br /&gt;
			insert(wikitext,inline_block(stat,armor))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Return a list of armor keys to be used in the table&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return table - strings&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	--TODO add minor string validation and correction, e.g. &amp;quot;DeMoN&amp;quot; -&amp;gt; &amp;quot;demon&amp;quot;  (&amp;quot;DeMoN&amp;quot;):lower()&lt;br /&gt;
	return AD[frame.args[1]][frame.args[2]] or {}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Return a table row of appropriate headers cells&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function table_header(frame)&lt;br /&gt;
	local wikitext = [=[&amp;lt;table cellspacing=&amp;quot;0&amp;quot; class=&amp;quot;wikitable sortable jquery-tablesorter&amp;quot; style=&amp;quot;width:95%;color:#eee;background:transparent;text-align:center;vertical-align:middle;&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;]=]&lt;br /&gt;
	if (frame.args[1] or &amp;quot;&amp;quot;):lower() ~= &amp;quot;back&amp;quot; then&lt;br /&gt;
		wikitext = wikitext..[=[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Class Requirements&amp;lt;/th&amp;gt;&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Movement Speed&amp;lt;/th&amp;gt;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext..[=[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Armor/magical Rating&amp;lt;/th&amp;gt;&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Attributes&amp;lt;/th&amp;gt;&amp;lt;th style=&amp;quot;width:25%&amp;quot;&amp;gt;Other&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create monster table wikitext&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	if not AD then return &amp;quot;&amp;lt;span style=&#039;color:red;&#039;&amp;gt;Error: Could not load Armor data in [[Module:Armor]]&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext, table_header(frame))&lt;br /&gt;
	for _, key in ipairs(get_list(frame)) do&lt;br /&gt;
		insert(wikitext, row(AD.Armor[key]))&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext, &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
################################################################################&lt;br /&gt;
                        Functions for individual armor pages&lt;br /&gt;
################################################################################&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = &amp;quot;&amp;quot;&lt;br /&gt;
	if active then&lt;br /&gt;
		class = &amp;quot; class=&#039;selected-tab tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		class = &amp;quot; class=&#039;tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:inline-block&#039; %s data-tabid=&#039;%s&#039; data-tab=&#039;%s&#039; title=&#039;%s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,class,tabid,title,title,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reference_table(wt,table_type)&lt;br /&gt;
	local types_order = {&amp;quot;Head&amp;quot;,&amp;quot;Chest&amp;quot;,&amp;quot;Legs&amp;quot;,&amp;quot;Hands&amp;quot;,&amp;quot;Foot&amp;quot;,&amp;quot;Back&amp;quot;}&lt;br /&gt;
	local separator = &amp;quot;]] • [[&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:100%;text-align:center;vertical-align:middle;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for _,armor_type in ipairs(types_order) do&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%&#039;&amp;gt;[[Armors#%s|%s]]&amp;lt;/td&amp;gt;&amp;lt;td style=&#039;text-align-last:left;padding:10px 25px&#039;&amp;gt;&amp;quot;,armor_type,armor_type)&lt;br /&gt;
		if AD[armor_type][table_type] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;[[&amp;quot;&lt;br /&gt;
			for i,v in ipairs(AD[armor_type][table_type]) do&lt;br /&gt;
				if i~=1 then wt[#wt+1] = separator end&lt;br /&gt;
				wt[#wt+1] = v&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;]]&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.references()&lt;br /&gt;
	local table_types = {&amp;quot;Uncraftable&amp;quot;,&amp;quot;Craftable&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;h2&amp;gt;References&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = tab_toggle(table_type,table_type..&amp;quot; Armors&amp;quot;,i==1,&amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;&amp;quot;..table_type..&amp;quot;-data&#039;&amp;quot;&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;gt;&amp;quot; else wt[#wt+1] = &amp;quot;style=&#039;display:none&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		reference_table(wt,table_type)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on wiki with&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Back&amp;quot;,&amp;quot;Craftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Head&amp;quot;,&amp;quot;Uncraftable&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.references())&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft/doc&amp;diff=65221</id>
		<title>Module:Craft/doc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft/doc&amp;diff=65221"/>
		<updated>2026-04-09T00:24:04Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: /* page */ typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Overview=&lt;br /&gt;
Functions for making [[Crafting]] table. Data comes from [[Data:Merchant.json]].&lt;br /&gt;
&lt;br /&gt;
=Functions=&lt;br /&gt;
==draw_table==&lt;br /&gt;
Creates a table of craftables&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-| Parameters=	&lt;br /&gt;
* &#039;&#039;merchant&#039;&#039; - Merchant of interest.&lt;br /&gt;
* &#039;&#039;item&#039;&#039; - Singular item for which to create a table.&lt;br /&gt;
* &#039;&#039;ingredient&#039;&#039; - Ingredient name. Used to filter the provided merchant&#039;s crafts for those which use the ingredient.&lt;br /&gt;
&lt;br /&gt;
|-| Examples=	&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|merchant=Weaponsmith}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_table|merchant=Weaponsmith}}&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==page==&lt;br /&gt;
Creates, if possible, two tables.  The first presents recipes for if the given item is craftable.  The second presents recipes that use the item to create other items.&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-| Parameters=	&lt;br /&gt;
* &#039;&#039;item&#039;&#039; - Singular item for which to create a table.&lt;br /&gt;
* &#039;&#039;category&#039;&#039; - Item&#039;s item category. Used to lookup merchant crafting presence.&lt;br /&gt;
&lt;br /&gt;
|-| Examples=	&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|page|item=Void Blade|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|page|item=Void Blade|category=Weapon}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|page|category=Misc|item=Iron Ingot}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|page|category=Misc|item=Iron Ingot}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|page|category=Misc|item=Iron Ore}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|page|category=Misc|item=Iron Ore}}&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft/doc&amp;diff=65220</id>
		<title>Module:Craft/doc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft/doc&amp;diff=65220"/>
		<updated>2026-04-09T00:22:01Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Merged draw_crafting_table() and draw_ingredient_table() into page()&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Overview=&lt;br /&gt;
Functions for making [[Crafting]] table. Data comes from [[Data:Merchant.json]].&lt;br /&gt;
&lt;br /&gt;
=Functions=&lt;br /&gt;
==draw_table==&lt;br /&gt;
Creates a table of craftables&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-| Parameters=	&lt;br /&gt;
* &#039;&#039;merchant&#039;&#039; - Merchant of interest.&lt;br /&gt;
* &#039;&#039;item&#039;&#039; - Singular item for which to create a table.&lt;br /&gt;
* &#039;&#039;ingredient&#039;&#039; - Ingredient name. Used to filter the provided merchant&#039;s crafts for those which use the ingredient.&lt;br /&gt;
&lt;br /&gt;
|-| Examples=	&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|merchant=Weaponsmith}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_table|merchant=Weaponsmith}}&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==page==&lt;br /&gt;
Creates, if possible, two tables.  The first presents recipes for if the given item is creaftable.  The second prsents recipes that use the item to create other items.&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-| Parameters=	&lt;br /&gt;
* &#039;&#039;item&#039;&#039; - Singular item for which to create a table.&lt;br /&gt;
* &#039;&#039;category&#039;&#039; - Item&#039;s item category. Used to lookup merchant crafting presence.&lt;br /&gt;
&lt;br /&gt;
|-| Examples=	&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|page|item=Void Blade|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|page|item=Void Blade|category=Weapon}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|page|category=Misc|item=Iron Ingot}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|page|category=Misc|item=Iron Ingot}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|page|category=Misc|item=Iron Ore}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|page|category=Misc|item=Iron Ore}}&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65219</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65219"/>
		<updated>2026-04-09T00:15:42Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Merged two invoke-able functions into one.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	if amount then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,merchant_name,false,&amp;quot;-1&amp;quot;,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_ingredient_table(wt,ingredient,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[item],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	draw_craftable_table(wt,item,category,data)&lt;br /&gt;
	draw_ingredient_table(wt,item,category,data)&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.page({args={item=&amp;quot;Iron Ore&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65218</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65218"/>
		<updated>2026-04-08T23:39:39Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Some data minor validation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	if amount then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,merchant_name,false,&amp;quot;-1&amp;quot;,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_ingredient_table(f)&lt;br /&gt;
	local ingredient = f.args.ingredient&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
	local merchants = data[category] and data[category][ingredient] and data[category][ingredient].incraftingrecipefor or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_craftable_table(f)&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
	local merchants = data[category] and data[category][item] and data[category][item].iscraftableby or {}&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[item],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.draw_ingredient_table({args={ingredient=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.draw_craftable_table({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Armor&amp;diff=65217</id>
		<title>Module:Armor</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Armor&amp;diff=65217"/>
		<updated>2026-04-08T23:29:07Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added invoke-able function to create reference armor tables&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local AD = mw.loadJsonData(&amp;quot;Data:Armor.json&amp;quot;)&lt;br /&gt;
local insert = table.insert&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
local is_special = {[&amp;quot;Move Speed&amp;quot;]=true, [&amp;quot;Armor Rating&amp;quot;]=true, [&amp;quot;Magical Resistance&amp;quot;]=true}&lt;br /&gt;
local is_primitive = {[&amp;quot;Strength&amp;quot;]=true, [&amp;quot;Vigor&amp;quot;]=true, [&amp;quot;Agility&amp;quot;]=true, [&amp;quot;Dexterity&amp;quot;]=true, [&amp;quot;Will&amp;quot;]=true, [&amp;quot;Knowledge&amp;quot;]=true, [&amp;quot;Resourcefulness&amp;quot;]=true}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the type of the armor as a string&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function get_type(item)&lt;br /&gt;
	--Assumes that the item only has one type&lt;br /&gt;
	for key,_ in pairs(item.types) do&lt;br /&gt;
		--Scribunto uses meta tables so we can&#039;t use next() here&lt;br /&gt;
		return key&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Get the size of the armor in pixels&lt;br /&gt;
--- @param item table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Misc.json don&#039;t work with next() or the # operator&lt;br /&gt;
--- @param t table&lt;br /&gt;
--- @return number&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Create css/html wikitext for iconbox&lt;br /&gt;
---@param item table&lt;br /&gt;
---@return string&lt;br /&gt;
local function iconbox(item)&lt;br /&gt;
	local color = &amp;quot;2&amp;quot;&lt;br /&gt;
	local caption = &amp;quot;[[&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		caption = caption..&amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name..&amp;quot;&amp;lt;/b&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;display:inline-flex;width:max-content;max-width:initial;flex-direction:column;align-items:center;flex-wrap:wrap;white-space:pre-wrap&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		..&amp;quot;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		..&amp;quot;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
		..&amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..get_type(item)..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		..caption..&amp;quot;]]&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Turns a list of classes into a wikitext string with line break separators&lt;br /&gt;
--- @param list table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function classes(list)&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot; --classes are few enough that we can just concatenate them without worrying about performance&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, class in ipairs(list) do&lt;br /&gt;
		wikitext = wikitext..newline..class&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
---@param values string|table&lt;br /&gt;
---@param armor table&lt;br /&gt;
---@return string&lt;br /&gt;
local function color_values(values,armor)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; then&lt;br /&gt;
		if count(armor.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..armor.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return values&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--TODO this assumes i is the same as the color rating, this may result in a bug&lt;br /&gt;
	local wikitext = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, value in ipairs(values) do&lt;br /&gt;
		wikitext = wikitext..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..i..&amp;quot;&#039;&amp;gt;&amp;quot;..value..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment&lt;br /&gt;
--- @param stat string&lt;br /&gt;
--- @param armor table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function inline_block(stat, armor)&lt;br /&gt;
	if armor.stats[(stat):lower()] == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:inline-block;vertical-align:top;margin:0 15px 0 15px&#039;&amp;gt;&amp;lt;b style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..stat..&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..color_values(armor.stats[(stat):lower()],armor)..&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing armor information&lt;br /&gt;
--- @param armor table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function row(armor)&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
	insert(wikitext,iconbox(armor))&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	if armor.slottype ~= &amp;quot;Back&amp;quot; then&lt;br /&gt;
		insert(wikitext,classes(armor.classes))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
		insert(wikitext,color_values(armor.stats[&amp;quot;move speed&amp;quot;],armor))&lt;br /&gt;
		insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
	-- elseif armor.stats[&amp;quot;move speed&amp;quot;] then&lt;br /&gt;
	-- 	insert(wikitext,&amp;quot;&amp;lt;span style=&#039;color:red;&#039;&amp;gt;Error: Back &amp;quot;..armor.name..&amp;quot; has unpresented move speed.&amp;lt;/span&amp;gt;&amp;quot;)&lt;br /&gt;
	-- elseif next(armor.classes) then&lt;br /&gt;
	-- 	insert(wikitext,&amp;quot;&amp;lt;span style=&#039;color:red;&#039;&amp;gt;Error: Back &amp;quot;..armor.name..&amp;quot; has unpresented class requirements.&amp;lt;/span&amp;gt;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	insert(wikitext,inline_block(&amp;quot;Armor Rating&amp;quot;,armor))&lt;br /&gt;
	insert(wikitext,inline_block(&amp;quot;Magical Resistance&amp;quot;,armor))&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	-- create the table data cell for attributes&lt;br /&gt;
	for _,stat in ipairs(armor.stats.order) do&lt;br /&gt;
		if is_primitive[stat] then&lt;br /&gt;
			insert(wikitext,inline_block(stat,armor))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;td&amp;gt;&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
	-- create the table data cell for the rest of the stats&lt;br /&gt;
	for _, stat in ipairs(armor.stats.order) do&lt;br /&gt;
		if not is_primitive[stat] and not is_special[stat] then&lt;br /&gt;
			insert(wikitext,inline_block(stat,armor))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext,&amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Return a list of armor keys to be used in the table&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return table - strings&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	--TODO add minor string validation and correction, e.g. &amp;quot;DeMoN&amp;quot; -&amp;gt; &amp;quot;demon&amp;quot;  (&amp;quot;DeMoN&amp;quot;):lower()&lt;br /&gt;
	return AD[frame.args[1]][frame.args[2]] or {}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--- Return a table row of appropriate headers cells&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
local function table_header(frame)&lt;br /&gt;
	local wikitext = [=[&amp;lt;table cellspacing=&amp;quot;0&amp;quot; class=&amp;quot;wikitable sortable jquery-tablesorter&amp;quot; style=&amp;quot;width:95%;color:#eee;background:transparent;text-align:center;vertical-align:middle;&amp;quot;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;]=]&lt;br /&gt;
	if (frame.args[1] or &amp;quot;&amp;quot;):lower() ~= &amp;quot;back&amp;quot; then&lt;br /&gt;
		wikitext = wikitext..[=[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Class Requirements&amp;lt;/th&amp;gt;&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Movement Speed&amp;lt;/th&amp;gt;]=]&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext..[=[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Armor/magical Rating&amp;lt;/th&amp;gt;&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Attributes&amp;lt;/th&amp;gt;&amp;lt;th style=&amp;quot;width:25%&amp;quot;&amp;gt;Other&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;]=]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Create monster table wikitext&lt;br /&gt;
--- @param frame table&lt;br /&gt;
--- @return string&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	if not AD then return &amp;quot;&amp;lt;span style=&#039;color:red;&#039;&amp;gt;Error: Could not load Armor data in [[Module:Armor]]&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wikitext = {}&lt;br /&gt;
	insert(wikitext, table_header(frame))&lt;br /&gt;
	for _, key in ipairs(get_list(frame)) do&lt;br /&gt;
		insert(wikitext, row(AD.Armor[key]))&lt;br /&gt;
	end&lt;br /&gt;
	insert(wikitext, &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;)&lt;br /&gt;
	return concat(wikitext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
################################################################################&lt;br /&gt;
                        Functions for individual armor pages&lt;br /&gt;
################################################################################&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = &amp;quot;&amp;quot;&lt;br /&gt;
	if active then&lt;br /&gt;
		class = &amp;quot; class=&#039;selected-tab tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		class = &amp;quot; class=&#039;tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:inline-block&#039; %s data-tabid=&#039;%s&#039; data-tab=&#039;%s&#039; title=&#039;%s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,class,tabid,title,title,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reference_table(wt,table_type)&lt;br /&gt;
	local types_order = {&amp;quot;Head&amp;quot;,&amp;quot;Chest&amp;quot;,&amp;quot;Legs&amp;quot;,&amp;quot;Hands&amp;quot;,&amp;quot;Foot&amp;quot;,&amp;quot;Back&amp;quot;}&lt;br /&gt;
	local separator = &amp;quot;]] • [[&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:100%;text-align:center;vertical-align:middle;margin-top:15px&#039;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td colspan=&#039;2&#039; style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = table_type&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for _,armor_type in ipairs(types_order) do&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%&#039;&amp;gt;[[Armors#%s|%s]]&amp;lt;/td&amp;gt;&amp;lt;td style=&#039;text-align-last:left;padding:10px 25px&#039;&amp;gt;&amp;quot;,armor_type,armor_type)&lt;br /&gt;
		if AD[armor_type][table_type] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;[[&amp;quot;&lt;br /&gt;
			for i,v in ipairs(AD[armor_type][table_type]) do&lt;br /&gt;
				if i~=1 then wt[#wt+1] = separator end&lt;br /&gt;
				wt[#wt+1] = v&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;]]&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.references()&lt;br /&gt;
	local table_types = {&amp;quot;Uncraftable&amp;quot;,&amp;quot;Craftable&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;h2&amp;gt;References&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = tab_toggle(table_type,table_type..&amp;quot; Armors&amp;quot;,i==1,&amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;&amp;quot;..table_type..&amp;quot;-data&#039;&amp;quot;&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;gt;&amp;quot; else wt[#wt+1] = &amp;quot;style=&#039;display:none&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		reference_table(wt,table_type)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on wiki with&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Back&amp;quot;,&amp;quot;Craftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Head&amp;quot;,&amp;quot;Uncraftable&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.references())&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65216</id>
		<title>Module:Weapon</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Weapon&amp;diff=65216"/>
		<updated>2026-04-08T23:15:28Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added functions for populating individual weapon pages with automated information.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local WD = mw.loadJsonData(&amp;quot;Data:Weapon.json&amp;quot;) --Global var holding tables from Data:Weapon.json&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
---Get the item&#039;s size in pixels&lt;br /&gt;
local function size(item)&lt;br /&gt;
	return tostring(item.invwidth*45)..&amp;quot;x&amp;quot;..tostring(item.invheight*45)..&amp;quot;px&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Counts number of keys. Tables from Weapon.json don&#039;t work with next() or the # operator&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a list of values with color based on rarity&lt;br /&gt;
local function color_values(weapon, values)&lt;br /&gt;
	if values == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	if type(values)==&amp;quot;string&amp;quot; or type(values)==&amp;quot;number&amp;quot; then&lt;br /&gt;
		if count(weapon.rarities) == 1 then return &amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..weapon.rarities[1]..&amp;quot;&#039;&amp;gt;&amp;quot;..values..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
		return tostring(values)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, key in ipairs(weapon.rarities) do&lt;br /&gt;
		wt = wt..newline..&amp;quot;&amp;lt;span class=&#039;cr&amp;quot;..key..&amp;quot;&#039;&amp;gt;&amp;quot;..tostring(values[tonumber(key)] or &amp;quot;&amp;quot;)..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Marks up a stat block with the stat name and colored values and appropriate margins and alignment&lt;br /&gt;
local function inline_block(weapon,stat)&lt;br /&gt;
	if weapon.stats[(stat):lower()] == nil then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:inline-block;vertical-align:top;margin:0 15px 0 15px&#039;&amp;gt;&amp;lt;b style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..stat..&amp;quot;&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[(stat):lower()])..&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell for the iconbox&lt;br /&gt;
local function name(item, is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Name&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local color = &amp;quot;2&amp;quot;&lt;br /&gt;
	local bold_link = &amp;quot;|&amp;lt;b&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	if count(item.rarities) == 1 then&lt;br /&gt;
		color = item.rarities[1]&lt;br /&gt;
		bold_link = &amp;quot;|&amp;lt;b class=cr&amp;quot;..color..&amp;quot;&amp;gt;&amp;quot;..item.name&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local icon_amount = &amp;quot;&amp;quot;&lt;br /&gt;
	if item.maxammocount &amp;gt; 0 then&lt;br /&gt;
		icon_amount = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..item.maxammocount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..color..&amp;quot; rounded relative&#039;&amp;gt;[[File:&amp;quot;..item.name..&amp;quot;.png|&amp;quot;..size(item)..&amp;quot;|link=&amp;quot;..item.name..&amp;quot;]]&amp;quot;&lt;br /&gt;
				..icon_amount&lt;br /&gt;
			..&amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;[[&amp;quot;..item.name..bold_link..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;lt;/div&amp;gt;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats the weapon&#039;s list of classes into a table cell of classes separated by linebreaks&lt;br /&gt;
local function classes(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Class&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	local newline = &amp;quot;&amp;quot;&lt;br /&gt;
	for i, class in ipairs(weapon.classes) do&lt;br /&gt;
		wt = wt..newline..class&lt;br /&gt;
		if i == 1 then newline = &amp;quot;&amp;lt;br&amp;gt;&amp;quot; end -- Subsequent loops will prefix a linebreak&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of slottype and handtype&lt;br /&gt;
local function slot_type(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Slot&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.slottype..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..weapon.handtype..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of move speeds&lt;br /&gt;
local function move_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Movement Speed&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..color_values(weapon,weapon.stats[&amp;quot;move speed&amp;quot;])..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell of Armor rating and Magical Resistance&lt;br /&gt;
local function armor_rating(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:15%&amp;quot;&amp;gt;Armor Rating&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..inline_block(weapon,&amp;quot;Armor Rating&amp;quot;)..inline_block(weapon,&amp;quot;Magical Resistance&amp;quot;)..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function impact_zones(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Impact Zones + Impact Power&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt= &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then&lt;br /&gt;
			for j,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
				if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then&lt;br /&gt;
					wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;..ability..&amp;quot; &amp;quot;..attack..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
					for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do&lt;br /&gt;
						if k~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
						wt = wt..impact_zone&lt;br /&gt;
					end&lt;br /&gt;
					wt = wt..&amp;quot; + &amp;quot;..weapon.abilities[ability][attack].impactpower..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.impactresistance~=&amp;quot;&amp;quot; then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Impact Resist&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..weapon.impactresistance&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helper function: Collects move mult and prepare move mult for display&lt;br /&gt;
local function collect_multipliers(ability)&lt;br /&gt;
	local move, prep_move = &amp;quot;&amp;quot;,&amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack_name in ipairs(ability.order) do&lt;br /&gt;
		if ability[attack_name].movementmultiplier then&lt;br /&gt;
			if i ~= 1 then&lt;br /&gt;
				move = move..&amp;quot;/&amp;quot;&lt;br /&gt;
				prep_move = prep_move..&amp;quot;/&amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
			move = move..(ability[attack_name].movementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			prep_move = prep_move..(ability[attack_name].preparemovementmultiplier or &amp;quot;&amp;quot;)&lt;br /&gt;
			i = i + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return move,prep_move&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing speed penalties for each action&lt;br /&gt;
local function action_move_speed_penalty(weapon,is_header)&lt;br /&gt;
	if is_header then return [=[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]&amp;lt;/th&amp;gt;]=] end&lt;br /&gt;
&lt;br /&gt;
	local order = {} --reconstruct order to exclude the other ability group&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		if ability ~= &amp;quot;Other&amp;quot; then order[#order+1] = ability end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,ability_name in ipairs(order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		if ability_name==&amp;quot;Other&amp;quot; or ability_name==&amp;quot;Block&amp;quot; then wt = wt..ability_name..&amp;quot; Actions&amp;quot; else wt = wt..ability_name..&amp;quot; Attacks&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		local movement_multiplier,prepare_movement_multiplier = collect_multipliers(weapon.abilities[ability_name])&lt;br /&gt;
		if #prepare_movement_multiplier &amp;gt; 3 then&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
			..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..prepare_movement_multiplier&lt;br /&gt;
		else&lt;br /&gt;
			wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;..movement_multiplier&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing base Physical and Magical damage, if present.&lt;br /&gt;
local function damage_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:25%&amp;quot;&amp;gt;Damage on Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	wt = wt..inline_block(weapon,&amp;quot;Physical Base Weapon Damage&amp;quot;)&lt;br /&gt;
			..inline_block(weapon,&amp;quot;Magical Base Weapon Damage&amp;quot;)&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---These stats get their own table cells, as such they are excluded from the stats cell to avoid needless redundancy&lt;br /&gt;
local exclude_stat = {[&amp;quot;Physical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Magical Base Weapon Damage&amp;quot;]=true,[&amp;quot;Armor Rating&amp;quot;]=true,[&amp;quot;Magical Resistance&amp;quot;]=true,[&amp;quot;Move Speed&amp;quot;]=true}&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing any stats not covered by the rest of the row&lt;br /&gt;
local function stats(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:12%&amp;quot;&amp;gt;Stats&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,stat in ipairs(weapon.stats.order) do&lt;br /&gt;
		if not exclude_stat[stat] then&lt;br /&gt;
			wt = wt..inline_block(weapon,stat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing animation times for ranges weapons&lt;br /&gt;
local function animation_time(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Animation Times&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local windup = &amp;quot;&amp;lt;br&amp;gt;Windup:&amp;quot;&lt;br /&gt;
	local finish = &amp;quot;&amp;lt;br&amp;gt;Finish:&amp;quot;&lt;br /&gt;
	local reload = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Reload&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Attack 1&amp;quot;] then&lt;br /&gt;
		-- windup = windup..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Windup&lt;br /&gt;
		-- finish = finish..weapon.animationtimes[&amp;quot;Attack 1&amp;quot;].Finish&lt;br /&gt;
	else&lt;br /&gt;
		windup = &amp;quot;&amp;quot;&lt;br /&gt;
		finish = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.animationtimes[&amp;quot;Other&amp;quot;] then&lt;br /&gt;
		-- reload = reload..weapon.animationtimes[&amp;quot;Other&amp;quot;].Reload&lt;br /&gt;
	else&lt;br /&gt;
		reload = &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if #windup == 0 and #finish == 0 and #reload == 0 then&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attack&amp;lt;/span&amp;gt;&amp;quot;..windup..finish..reload..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing hitslow for each attack&lt;br /&gt;
local function slowdown_on_hit(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Slowdown On Hit&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return &amp;quot;&amp;lt;td&amp;gt;&amp;lt;/td&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local ability_type = &amp;quot;Primary&amp;quot;&lt;br /&gt;
	if weapon.slottype == &amp;quot;Off-Hand&amp;quot; then ability_type = &amp;quot;Secondary&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Hitslow&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	local duration = &amp;quot; for &amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
				or weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Move Speed Bonus&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability_type].order) do&lt;br /&gt;
		if i~=1 then duration = duration..&amp;quot;/&amp;quot; end&lt;br /&gt;
		duration = duration..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global[&amp;quot;Duration&amp;quot;] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..duration..&amp;quot;s&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the projectile&#039;s initial speed&lt;br /&gt;
local function initial_speed(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:5%&amp;quot;&amp;gt;Initial Projectile Speed (m/s)&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..weapon.initialspeed..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing the hitbox image resized by inventory size&lt;br /&gt;
local function hitbox(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:10%&amp;quot;&amp;gt;Hitbox + Impact Resist&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;[[File:&amp;quot;..weapon.name..&amp;quot; Hitbox.png|link=|&amp;quot;..size(weapon)..&amp;quot;]]&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Helps format a string containing either Combo Damage or damagetype values&lt;br /&gt;
local function format_combo(weapon,ability,value)&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		if i~=1 then wt = wt..&amp;quot;/&amp;quot; end&lt;br /&gt;
		wt = wt..(weapon.abilities[ability][attack][value] or &amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return wt&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Formats a table cell containing ability combo values per attack&lt;br /&gt;
local function combo(weapon,is_header)&lt;br /&gt;
	if is_header then return [[&amp;lt;th style=&amp;quot;width:8%&amp;quot;&amp;gt;Combo&amp;lt;/th&amp;gt;]] end&lt;br /&gt;
&lt;br /&gt;
	local wt = &amp;quot;&amp;quot;&lt;br /&gt;
	if weapon.abilities[&amp;quot;Primary&amp;quot;] then&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Primary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Primary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Secondary&amp;quot;] then&lt;br /&gt;
		if #wt ~= 0 then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Secondary Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Secondary&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.abilities[&amp;quot;Special&amp;quot;] then&lt;br /&gt;
		if wt:match(&amp;quot;Primary&amp;quot;) or wt:match(&amp;quot;Secondary&amp;quot;) then wt = wt..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot; end&lt;br /&gt;
		wt = wt..&amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Special Attacks&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;damagetype&amp;quot;)..&amp;quot;&amp;lt;br&amp;gt;&amp;quot;..format_combo(weapon,&amp;quot;Special&amp;quot;,&amp;quot;combodamage&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	return &amp;quot;&amp;lt;td&amp;gt;&amp;quot;..wt..&amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function determine_table_type(weapon)&lt;br /&gt;
	if weapon.args then&lt;br /&gt;
		if weapon.args[1] == &amp;quot;Shield&amp;quot; then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif (weapon.args[1]):lower():match(&amp;quot;bow&amp;quot;) or weapon.args[1] == &amp;quot;Firearm&amp;quot; or weapon.args[1] == &amp;quot;Throwable&amp;quot; then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	elseif weapon.types then&lt;br /&gt;
		if weapon.types.Shield then return &amp;quot;Shield&amp;quot;&lt;br /&gt;
		elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm or weapon.types.Throwable then return &amp;quot;Ranged&amp;quot; end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return &amp;quot;Default&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Determines which table data goes into the table row&lt;br /&gt;
local row_type = {&lt;br /&gt;
	[&amp;quot;Shield&amp;quot;] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},&lt;br /&gt;
	[&amp;quot;Ranged&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,action_move_speed_penalty,slowdown_on_hit,initial_speed},&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---Return a table row of data cells containing weapon information,&lt;br /&gt;
local function row(weapon, is_header)&lt;br /&gt;
	is_header = is_header or false&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,content in ipairs(row_type[determine_table_type(weapon)]) do&lt;br /&gt;
		wt[#wt+1] = content(weapon,is_header)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---Returns a predetermined weapon list&lt;br /&gt;
local function get_list(frame)&lt;br /&gt;
	if not (frame.args and frame.args[1] and frame.args[2]) then return {} end&lt;br /&gt;
	if not (WD[frame.args[1]] and WD[frame.args[1]][frame.args[2]]) then return {} end&lt;br /&gt;
&lt;br /&gt;
	return WD[frame.args[1]][frame.args[2]]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(frame)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	local item_list = get_list(frame)&lt;br /&gt;
	if count(item_list) == 0 then return &amp;quot;&amp;lt;span style=&#039;font-size:24pt&#039;&amp;gt;There are currently no items of this type.&amp;lt;/span&amp;gt;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = [[&amp;lt;table cellspacing=&amp;quot;0&amp;quot; class=&amp;quot;wikitable sortable jquery-tablesorter&amp;quot; style=&amp;quot;text-align:center; vertical-align:middle;&amp;quot;&amp;gt;]]&lt;br /&gt;
	wt[#wt+1] = row(frame, true)&lt;br /&gt;
	for _,item_name in ipairs(item_list) do&lt;br /&gt;
		wt[#wt+1] = row(WD.Weapon[item_name])&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[###########################################################################&lt;br /&gt;
                Functions for individual weapon pages&lt;br /&gt;
#############################################################################]=]&lt;br /&gt;
local function projectile(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.initialspeed ~= &amp;quot;&amp;quot; then &lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Projectile&amp;lt;/h2&amp;gt;Initial Speed: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.initialspeed)&lt;br /&gt;
	end&lt;br /&gt;
	if weapon.piercecount ~= &amp;quot;&amp;quot; then&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;Pierce: %s&amp;lt;br&amp;gt;&amp;quot;, weapon.piercecount)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function damage_falloff(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if weapon.damagefalloff == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Damage Falloff&amp;lt;/h2&amp;gt;%s has damage falloff depending on airtime of the projectile.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Projectile falloff damage chart:&amp;lt;br&amp;gt;X: Air time (seconds)&amp;lt;br&amp;gt;Y: %% Damage&amp;quot;,weapon_name,weapon.initialspeed)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:500px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = weapon.damagefalloff&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;Learn more at [[Damage_Calculation#Projectile_Falloff|Projectile Falloff]]&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hitbox(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
	if not weapon.hitbox.height then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Hitbox&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;Height: &amp;quot;..weapon.hitbox.height&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Width: &amp;quot;..weapon.hitbox.width&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Depth: &amp;quot;..weapon.hitbox.depth&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and has primary or secondary attacks&lt;br /&gt;
	if not (weapon.types[&amp;quot;Bow&amp;quot;] or weapon.types[&amp;quot;Crossow&amp;quot;] or weapon.types[&amp;quot;ThrowableStuff&amp;quot;])&lt;br /&gt;
			and (weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactzones or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactzones) then&lt;br /&gt;
		local impact_zones = weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactzones or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactzones&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;The weapon&#039;s hitbox doesn&#039;t necessarily determine a weapon&#039;s reach due to different arm extension during attack animations. Thus, shorter weapons can have better reach than longer weapons.&amp;lt;br&amp;gt;[[Impact Zones]] of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;:&amp;lt;div style=&#039;display:flex;align-items:center&#039;&amp;gt;&amp;lt;div&amp;gt;[[File:&amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot; Hitbox.png|x300px]]&amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;&amp;lt;span style=&#039;color:green&#039;&amp;gt;Green Zone deals &amp;quot;&lt;br /&gt;
		wt[#wt+1] = impact_zones[1]&lt;br /&gt;
		wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		if impact_zones[2] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:orange&#039;&amp;gt;Orange Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[2]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		if impact_zones[3] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:red&#039;&amp;gt;Red Zone deals &amp;quot;&lt;br /&gt;
			wt[#wt+1] = impact_zones[3]&lt;br /&gt;
			wt[#wt+1] = &amp;quot; damage&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&#039;&#039;&#039;Disclaimer&#039;&#039;&#039;: The impact zone&#039;s locations are an approximation. The real hitboxes may be slightly larger, closer together, and/or follow the weapon&#039;s shape more closely. Image is not able to be updated automatically. If it is outdated, please report it to the [https://discord.gg/KbsxgACkFS Wiki Discord].&amp;lt;/p&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactpower or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactpower then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Impact Power&#039;&#039;&#039; of &amp;quot;&lt;br /&gt;
		wt[#wt+1] = weapon.name&lt;br /&gt;
		wt[#wt+1] = &amp;quot;: &amp;quot;&lt;br /&gt;
		wt[#wt+1] = tostring(weapon.abilities.Primary[&amp;quot;Attack 1&amp;quot;].impactpower or weapon.abilities.Secondary[&amp;quot;Attack 1&amp;quot;].impactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;Impact power helps with breaking crates, barricades or any other breakables in game. The higher the impact power the faster something breaks.&amp;lt;br&amp;gt;Impact power also affects shield blocking. If the attacking weapon&#039;s impact power is higher than shield&#039;s impact power, the shield holder staggers, and vice versa.&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function list_to_string(l,separator)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(l) do&lt;br /&gt;
		if i~=1 then ret = ret..separator end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo_format(wt,attacks,type)&lt;br /&gt;
	if type == &amp;quot;Other&amp;quot; or type == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
	local first = &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = type&lt;br /&gt;
	wt[#wt+1] = &amp;quot; Attack: &amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; else first = name end&lt;br /&gt;
		wt[#wt+1] = attacks[name].damagetype&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; dealing &amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for i,name in ipairs(attacks.order) do&lt;br /&gt;
		if i ~= 1 then wt[#wt+1] = &amp;quot;/&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = attacks[name].combodamage&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot; damage on each swing, with impact zone: &amp;quot;&lt;br /&gt;
	wt[#wt+1] = list_to_string(attacks[first].impactzones, &amp;quot;/&amp;quot;)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function attack_animation_times(wt,weapon,ability_name,attack_name)&lt;br /&gt;
	local attack = weapon.animationtimes[ability_name][attack_name]&lt;br /&gt;
	if attack[&amp;quot;Windup&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                           attack[&amp;quot;Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Hit&amp;quot;]               then wt[#wt+1] = string.format(&amp;quot;Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                              attack[&amp;quot;Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Windup&amp;quot;]     then wt[#wt+1] = string.format(&amp;quot;Second Windup: %s&amp;lt;br&amp;gt;&amp;quot;,                    attack[&amp;quot;Second Windup&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Second Hit&amp;quot;]        then wt[#wt+1] = string.format(&amp;quot;Second Hit: %s&amp;lt;br&amp;gt;&amp;quot;,                       attack[&amp;quot;Second Hit&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Recover&amp;quot;]           then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Recover: %s&amp;lt;br&amp;gt;&amp;quot;,           attack[&amp;quot;Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Alternate Recover&amp;quot;] then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Alternate Recover: %s&amp;lt;br&amp;gt;&amp;quot;, attack[&amp;quot;Alternate Recover&amp;quot;]) end&lt;br /&gt;
	if attack[&amp;quot;Finish&amp;quot;]            then wt[#wt+1] = string.format(&amp;quot;&amp;amp;nbsp;&amp;amp;#10551; Finish: %s&amp;lt;br&amp;gt;&amp;quot;,            attack[&amp;quot;Finish&amp;quot;]) end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function animation_format(wt, weapon, ability)&lt;br /&gt;
	if ability == &amp;quot;Other&amp;quot; or ability == &amp;quot;Block&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	for i, name in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
		wt[#wt+1] = ability..&amp;quot; Combo: &amp;quot;..name..&amp;quot;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		attack_animation_times(wt,weapon,ability,name)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function combo(wt, page_name)&lt;br /&gt;
	local weapon = WD.Weapon[page_name]&lt;br /&gt;
&lt;br /&gt;
	-- ensure that there is relevant data before continuing.&lt;br /&gt;
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return end&lt;br /&gt;
&lt;br /&gt;
	-- If is not ranged, and isn&#039;t shield, or at least isn&#039;t a Lantern Shield&lt;br /&gt;
	if not (weapon.types.Bow or weapon.types.Crossow or weapon.types.ThrowableStuff)&lt;br /&gt;
		and (not weapon.types.Shield or (weapon.name):match(&amp;quot;Lantern Shield&amp;quot;)) then&lt;br /&gt;
&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Attack Animation Time&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
		for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
			combo_format(wt,weapon.abilities[name],name)&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&#039;&#039;&#039;Attack Times&#039;&#039;&#039;&amp;lt;br&amp;gt;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			for _,name in ipairs(weapon.abilities.order) do&lt;br /&gt;
				animation_format(wt,weapon,name)&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;Reference [[Attack Times]] for a glossary of the terms used above.&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function block(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	if not weapon.impactresistance then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Block&amp;lt;/h2&amp;gt;%s has a block ability with an impact resistance of &#039;&#039;&#039;%s&#039;&#039;&#039;.&amp;lt;br&amp;gt;To learn more about impact resistance see [[Impact Power]].&amp;quot;, weapon.name, weapon.impactresistance)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function action_move_slow_penalty(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Action Movement Slow&amp;lt;/h2&amp;gt;When a player performs certain actions, such as attacking or defending themselves, they move slower.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ability in ipairs(weapon.abilities.order) do&lt;br /&gt;
		for _,attack in ipairs(weapon.abilities[ability].order) do&lt;br /&gt;
			-- There&#039;s a bug where Riposte values are recorded within &amp;quot;Other&amp;quot; abilities&lt;br /&gt;
			-- Since ripose is covered in Special abilities, we will skip it here&lt;br /&gt;
			if attack == &amp;quot;Riposte&amp;quot; then break end&lt;br /&gt;
&lt;br /&gt;
			local movespeedadd = weapon.abilities[ability][attack].effects&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState&lt;br /&gt;
								and weapon.abilities[ability][attack].effects.ItemActivateState.rarity.Global[&amp;quot;Move Speed Add&amp;quot;]&lt;br /&gt;
								or &amp;quot;&amp;quot;&lt;br /&gt;
			local movement_multiplier = weapon.abilities[ability][attack].movementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
			local prepare_movement_multiplier = weapon.abilities[ability][attack].preparemovementmultiplier or &amp;quot;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8; font-size:110%; font-weight:bold&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if ability == &amp;quot;Other&amp;quot; then wt[#wt+1] = attack else wt[#wt+1] = ability end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/span&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:inline-block;margin-left:20px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
			if #prepare_movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Mid Attack:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Otherwise:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = prepare_movement_multiplier&lt;br /&gt;
			elseif #movement_multiplier &amp;gt; 1 then&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;x&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movement_multiplier&lt;br /&gt;
			else&lt;br /&gt;
				wt[#wt+1] = &amp;quot;&amp;lt;span style=&#039;color:#eee8&#039;&amp;gt;Always:&amp;amp;nbsp;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
				wt[#wt+1] = movespeedadd&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
			wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
			-- Everything except attacks within Other has invariant move mults&lt;br /&gt;
			-- So after processessing the first attack, we break the loop&lt;br /&gt;
			if ability ~= &amp;quot;Other&amp;quot; then break end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function artifact(wt,weapon_name)&lt;br /&gt;
	local weapon = WD.Weapon[weapon_name]&lt;br /&gt;
&lt;br /&gt;
	if weapon.isartifact or weapon.artifactname == &amp;quot;&amp;quot; then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = string.format(&amp;quot;&amp;lt;h2&amp;gt;Artifact&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;%s has a powerful [[Artifacts|Artifact]] named [[%s]].&amp;lt;/p&amp;gt;&amp;quot;, weapon.name, weapon.artifactname)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_toggle(title,content,active,tabid)&lt;br /&gt;
	local class = &amp;quot;&amp;quot;&lt;br /&gt;
	if active then&lt;br /&gt;
		class = &amp;quot; class=&#039;selected-tab tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	else&lt;br /&gt;
		class = &amp;quot; class=&#039;tab-toggle tab&#039;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:inline-block&#039; %s data-tabid=&#039;%s&#039; data-tab=&#039;%s&#039; title=&#039;%s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,class,tabid,title,title,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function reference_table(wt,table_type)&lt;br /&gt;
	local types_order = {&amp;quot;Sword&amp;quot;,&amp;quot;Dagger&amp;quot;,&amp;quot;Mace&amp;quot;,&amp;quot;Polearm&amp;quot;,&amp;quot;Axe&amp;quot;,&amp;quot;Throwable&amp;quot;,&amp;quot;Bow&amp;quot;,&amp;quot;Crossbow&amp;quot;,&amp;quot;Firearm&amp;quot;,&amp;quot;MagicStuff&amp;quot;,&amp;quot;Shield&amp;quot;,&amp;quot;LightSource&amp;quot;}&lt;br /&gt;
	local localization = {&lt;br /&gt;
		[&amp;quot;Sword&amp;quot;]        = &amp;quot;Swords&amp;quot;,&lt;br /&gt;
		[&amp;quot;Dagger&amp;quot;]       = &amp;quot;Daggers&amp;quot;,&lt;br /&gt;
		[&amp;quot;Mace&amp;quot;]         = &amp;quot;Maces&amp;quot;,&lt;br /&gt;
		[&amp;quot;Polearm&amp;quot;]      = &amp;quot;Polearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;Axe&amp;quot;]          = &amp;quot;Axes&amp;quot;,&lt;br /&gt;
		[&amp;quot;Throwable&amp;quot;]    = &amp;quot;Throwables&amp;quot;,&lt;br /&gt;
		[&amp;quot;Bow&amp;quot;]          = &amp;quot;Bows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Crossbow&amp;quot;]     = &amp;quot;Crossbows&amp;quot;,&lt;br /&gt;
		[&amp;quot;Firearm&amp;quot;]      = &amp;quot;Firearms&amp;quot;,&lt;br /&gt;
		[&amp;quot;MagicStuff&amp;quot;]   = &amp;quot;Magic Stuff&amp;quot;,&lt;br /&gt;
		[&amp;quot;Shield&amp;quot;]       = &amp;quot;Shields&amp;quot;,&lt;br /&gt;
		[&amp;quot;LightSource&amp;quot;]  = &amp;quot;Light Sources&amp;quot;&lt;br /&gt;
	}&lt;br /&gt;
	local separator = &amp;quot;]] • [[&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:100%;text-align:center;vertical-align:middle;margin-top:15px&#039;&amp;gt;&amp;lt;tr&amp;gt;&amp;lt;td colspan=&#039;2&#039; style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = table_type&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	for _,weapon_type in ipairs(types_order) do&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;tr&amp;gt;&amp;lt;td style=&#039;font-weight:bold; background-color:rgb(220,220,220,0.2); width:10%%&#039;&amp;gt;[[Weapons#%s|%s]]&amp;lt;/td&amp;gt;&amp;lt;td style=&#039;text-align-last:left;padding:10px 25px&#039;&amp;gt;&amp;quot;,localization[weapon_type],localization[weapon_type])&lt;br /&gt;
		if WD[weapon_type][table_type] then&lt;br /&gt;
			wt[#wt+1] = &amp;quot;[[&amp;quot;&lt;br /&gt;
			for i,v in ipairs(WD[weapon_type][table_type]) do&lt;br /&gt;
				if i~=1 then wt[#wt+1] = separator end&lt;br /&gt;
				wt[#wt+1] = v&lt;br /&gt;
			end&lt;br /&gt;
			wt[#wt+1] = &amp;quot;]]&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function references(wt)&lt;br /&gt;
	local table_types = {&amp;quot;Uncraftable&amp;quot;,&amp;quot;Craftable&amp;quot;,&amp;quot;Artifact&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;&amp;lt;h2&amp;gt;References&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;flex-wrap:wrap;justify-content:center;margin-top:15px&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = tab_toggle(table_type,table_type..&amp;quot; Weapons&amp;quot;,i==1,&amp;quot;&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	for i,table_type in ipairs(table_types) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;&amp;quot;..table_type..&amp;quot;-data&#039;&amp;quot;&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;gt;&amp;quot; else wt[#wt+1] = &amp;quot;style=&#039;display:none&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		reference_table(wt,table_type)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.page(f)&lt;br /&gt;
	local page_name = f.args.name&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	projectile(wt,page_name)&lt;br /&gt;
	damage_falloff(wt,page_name)&lt;br /&gt;
	hitbox(wt,page_name)&lt;br /&gt;
	combo(wt,page_name)&lt;br /&gt;
	block(wt,page_name)&lt;br /&gt;
	action_move_slow_penalty(wt,page_name)&lt;br /&gt;
	artifact(wt,page_name)&lt;br /&gt;
	references(wt)&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on wiki with&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Crossbow&amp;quot;,&amp;quot;Uncraftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Shield&amp;quot;,&amp;quot;Craftable&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={&amp;quot;Polearm&amp;quot;,&amp;quot;Artifact&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.page({args={name=&amp;quot;Rapier&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft/doc&amp;diff=65215</id>
		<title>Module:Craft/doc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft/doc&amp;diff=65215"/>
		<updated>2026-04-08T20:46:06Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added new functions to doc&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Overview=&lt;br /&gt;
Functions for making [[Crafting]] table. Data comes from [[Data:Merchant.json]].&lt;br /&gt;
&lt;br /&gt;
=Functions=&lt;br /&gt;
==draw_table==&lt;br /&gt;
Creates a table of craftables&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-| Parameters=	&lt;br /&gt;
* &#039;&#039;merchant&#039;&#039; - Merchant of interest.&lt;br /&gt;
* &#039;&#039;item&#039;&#039; - Singular item for which to create a table.&lt;br /&gt;
* &#039;&#039;ingredient&#039;&#039; - Ingredient name. Used to filter the provided merchant&#039;s crafts for those which use the ingredient.&lt;br /&gt;
&lt;br /&gt;
|-| Examples=	&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|merchant=Weaponsmith}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_table|merchant=Weaponsmith}}&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==draw_ingredient_table==&lt;br /&gt;
Creates a table of craftables filtered for a specific ingredient.&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-| Parameters=	&lt;br /&gt;
* &#039;&#039;ingredient&#039;&#039; - Singular ingredient.&lt;br /&gt;
* &#039;&#039;category&#039;&#039; - Ingredient&#039;s item category. Used to lookup merchant recipe presence.&lt;br /&gt;
&lt;br /&gt;
|-| Examples=&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_ingredient_table|category=Misc|ingredient=Iron Ingot}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_ingredient_table|category=Misc|ingredient=Iron Ingot}}&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==draw_craftable_table==&lt;br /&gt;
Creates a table of craftables filtered for specific item crafting recipes.&lt;br /&gt;
&amp;lt;tabber&amp;gt;&lt;br /&gt;
|-| Parameters=	&lt;br /&gt;
* &#039;&#039;item&#039;&#039; - Singular item for which to create a table.&lt;br /&gt;
* &#039;&#039;category&#039;&#039; - Item&#039;s item category. Used to lookup merchant crafting presence.&lt;br /&gt;
&lt;br /&gt;
|-| Examples=	&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_craftable_table|item=Void Blade|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_craftable_table|item=Void Blade|category=Weapon}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_craftable_table|category=Misc|item=Iron Ingot}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
{{#invoke:Craft|draw_craftable_table|category=Misc|item=Iron Ingot}}&lt;br /&gt;
&amp;lt;/tabber&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65214</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65214"/>
		<updated>2026-04-08T20:28:46Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Made iconbox slightly more dynamic.  Changed merchant td to display iconbox instead of a plain link.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = &amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	if amount then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,merchant_name,false,&amp;quot;-1&amp;quot;,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_ingredient_table(f)&lt;br /&gt;
	local ingredient = f.args.ingredient&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
	local merchants = data[category][ingredient].incraftingrecipefor&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_craftable_table(f)&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
	local merchants = data[category][item].iscraftableby&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[item],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.draw_ingredient_table({args={ingredient=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.draw_craftable_table({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65213</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65213"/>
		<updated>2026-04-08T20:19:51Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added invoke-able functions to generate filtered ingredient and filtered crafting tables.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = 		&amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	wt[#wt+1] = 		&amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = 	&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot; end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;[[&amp;quot;&lt;br /&gt;
	wt[#wt+1] = merchant_name&lt;br /&gt;
	wt[#wt+1] = &amp;quot;]]&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,craft_td in ipairs(ROW.Default) do&lt;br /&gt;
		craft_td(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_craft_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local BASIC_TABLE = { open = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;,&lt;br /&gt;
					  close= &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_craft_list(f.args)&lt;br /&gt;
	local merchant_name = f.args.merchant&lt;br /&gt;
	if #item_list == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,craft_name in ipairs(item_list) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	Weapon    = {Data = &amp;quot;Data:Weapon.json&amp;quot;},&lt;br /&gt;
					Armor     = {Data = &amp;quot;Data:Armor.json&amp;quot;},&lt;br /&gt;
					Accessory = {Data = &amp;quot;Data:Accessory.json&amp;quot;},&lt;br /&gt;
					Utility   = {Data = &amp;quot;Data:Utility.json&amp;quot;},&lt;br /&gt;
					Misc      = {Data = &amp;quot;Data:Misc.json&amp;quot;},&lt;br /&gt;
					Monster   = {Data = &amp;quot;Data:Monster.json&amp;quot;},&lt;br /&gt;
					Prop      = {Data = &amp;quot;Data:Prop.json&amp;quot;}}&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_ingredient_table(f)&lt;br /&gt;
	local ingredient = f.args.ingredient&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
	local merchants = data[category][ingredient].incraftingrecipefor&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Ingredient&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = ingredient&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is used in the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		for _,craft_name in ipairs(get_craft_list({ingredient=ingredient,merchant=merchant_name})) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant_name].crafts[craft_name],merchant_name)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_craftable_table(f)&lt;br /&gt;
	local item = f.args.item&lt;br /&gt;
	local category = f.args.category&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[category].Data)&lt;br /&gt;
	local merchants = data[category][item].iscraftableby&lt;br /&gt;
	if count(merchants) == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;h2&amp;gt;Craftable&amp;lt;/h2&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = item&lt;br /&gt;
	wt[#wt+1] = &amp;quot; is craftable with the following [[crafting]] recipes.&amp;quot;&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.open&lt;br /&gt;
	craft_table_header(wt)&lt;br /&gt;
	for _,merchant_name in ipairs(merchants) do&lt;br /&gt;
		craft_row(wt,false,MD[merchant_name].crafts[item],merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = BASIC_TABLE.close&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Test on the wiki with these console inputs&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ingot&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.draw_ingredient_table({args={ingredient=&amp;quot;Iron Ingot&amp;quot;,category=&amp;quot;Misc&amp;quot;}}))&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.draw_craftable_table({args={item=&amp;quot;Void Blade&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft/doc&amp;diff=65207</id>
		<title>Module:Craft/doc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft/doc&amp;diff=65207"/>
		<updated>2026-04-08T00:25:59Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Created page with &amp;quot;=Overview= Functions for making Crafting table. Data comes from Data:Merchant.json.  =Functions= ==draw_table== Creates a table of craftables  ===Parameters=== * &amp;#039;&amp;#039;merchant&amp;#039;&amp;#039; - Merchant of interest * &amp;#039;&amp;#039;item&amp;#039;&amp;#039; - Singular item for which to create a table * &amp;#039;&amp;#039;ingredient&amp;#039;&amp;#039; - Ingredient name, used to filter the provided merchant&amp;#039;s crafts for those which use the ingredient  ==draw_table examples== ===Void Blade, Weaponsmith=== ---- &amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|item=...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Overview=&lt;br /&gt;
Functions for making [[Crafting]] table. Data comes from [[Data:Merchant.json]].&lt;br /&gt;
&lt;br /&gt;
=Functions=&lt;br /&gt;
==draw_table==&lt;br /&gt;
Creates a table of craftables&lt;br /&gt;
&lt;br /&gt;
===Parameters===&lt;br /&gt;
* &#039;&#039;merchant&#039;&#039; - Merchant of interest&lt;br /&gt;
* &#039;&#039;item&#039;&#039; - Singular item for which to create a table&lt;br /&gt;
* &#039;&#039;ingredient&#039;&#039; - Ingredient name, used to filter the provided merchant&#039;s crafts for those which use the ingredient&lt;br /&gt;
&lt;br /&gt;
==draw_table examples==&lt;br /&gt;
===Void Blade, Weaponsmith===&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Craft|draw_table|item=Void Blade|merchant=Weaponsmith}}&lt;br /&gt;
===Weaponsmith, Iron Ingot===&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Craft|draw_table|merchant=Weaponsmith|ingredient=Iron Ingot}}&lt;br /&gt;
===Weaponsmith===&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Craft|draw_table|merchant=Weaponsmith}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Craft|draw_table|merchant=Weaponsmith}}&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65206</id>
		<title>Module:Craft</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Craft&amp;diff=65206"/>
		<updated>2026-04-08T00:19:45Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Working state&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--Either draw table of all craftable weapons for a merchant, or a singular item table&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local MD = mw.loadJsonData(&amp;quot;Data:Merchant.json&amp;quot;) --Global var holding tables from Data:Merchant.json&lt;br /&gt;
&lt;br /&gt;
local function iconbox(wt,name,amount,rarity,has_caption)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity&amp;quot;..rarity..&amp;quot; rounded relative&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = 		&amp;quot;[[File:&amp;quot;..name..&amp;quot;.png|x80px|link=&amp;quot;..name..&amp;quot;]]&amp;quot;&lt;br /&gt;
	wt[#wt+1] = 		&amp;quot;&amp;lt;span class=&#039;iconamount&#039; style=&#039;pointer-events:none;color:#EEEA;font-size:16px&#039;&amp;gt;&amp;quot;..amount..&amp;quot;&amp;lt;/span&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = 	&amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	if has_caption then wt[#wt+1] = &amp;quot;&amp;lt;br&amp;gt;[[&amp;quot;..name..&amp;quot;|&amp;lt;b class=cr&amp;quot;..rarity..&amp;quot;&amp;gt;&amp;quot;..name..&amp;quot;&amp;lt;/b&amp;gt;]]&amp;quot; end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function name(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Name&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	iconbox(wt,craft.itemname,craft.quantity,craft.rarity,true)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ingredients(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:5%&#039;&amp;gt;Ingredients&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;&amp;quot;&lt;br /&gt;
	for _,ingredient in ipairs(craft.ingredients) do&lt;br /&gt;
		local amount = string.match(ingredient,&amp;quot;^(%d*)-&amp;quot;)&lt;br /&gt;
		local ingredient_name = string.match(ingredient,&amp;quot;-(.*)-&amp;quot;)&lt;br /&gt;
		local rarity = string.match(ingredient,&amp;quot;-.*-(.*)$&amp;quot;)&lt;br /&gt;
		iconbox(wt,ingredient_name,amount,rarity)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function merchant(wt,is_header,craft,merchant_name)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th style=&#039;width:2%&#039;&amp;gt;Merchant&amp;lt;/th&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;td&amp;gt;[[&amp;quot;&lt;br /&gt;
	wt[#wt+1] = merchant_name&lt;br /&gt;
	wt[#wt+1] = &amp;quot;]]&amp;lt;/td&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function affinity_to_unlock(wt,is_header,craft,merchant)&lt;br /&gt;
	if is_header then wt[#wt+1] = &amp;quot;&amp;lt;th class=&#039;tooltip&#039; style=&#039;width:2%&#039;&amp;gt;[[Template:Merchant Affinity|Affinity to Unlock]]&amp;lt;span class=&#039;tooltiptext-left&#039; style=&#039;left:50%; transform:translate(-50%); bottom:75%; width:100%&#039;&amp;gt;Affinity values are collected manually by the community.  If you spot an error, please follow the link and fix it!&amp;lt;/span&amp;gt;&amp;lt;/th&amp;gt;&amp;lt;/tr&amp;gt;&amp;quot; return end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROW = {&lt;br /&gt;
	[&amp;quot;Default&amp;quot;] = {name,ingredients,merchant}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function craft_row(wt,is_header,craft,merchant_name)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;tr&amp;gt;&amp;quot;&lt;br /&gt;
	for _,table_data in ipairs(ROW.Default) do&lt;br /&gt;
		table_data(wt,is_header,craft,merchant_name)&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function craft_table_header(wt)&lt;br /&gt;
	craft_row(wt,true)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function spairs(dict)&lt;br /&gt;
    if not dict then return function() return nil,nil end end&lt;br /&gt;
&lt;br /&gt;
    local set = {}&lt;br /&gt;
    for key,_ in pairs(dict) do&lt;br /&gt;
        if key ~= &amp;quot;order&amp;quot; then&lt;br /&gt;
            set[#set+1] = key&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    table.sort(set,function(a,b)&lt;br /&gt;
    if type(a) ~= type(b) then&lt;br /&gt;
        return tostring(a) &amp;lt; tostring(b)&lt;br /&gt;
    else&lt;br /&gt;
        return a &amp;lt; b&lt;br /&gt;
    end&lt;br /&gt;
    end)&lt;br /&gt;
&lt;br /&gt;
    local i = 0&lt;br /&gt;
    return function()&lt;br /&gt;
        i = i + 1&lt;br /&gt;
        local key = set[i]&lt;br /&gt;
        if key ~= nil then&lt;br /&gt;
            return key, dict[key]&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function has_ingredient(list,ingredient)&lt;br /&gt;
	for _,val in ipairs(list) do&lt;br /&gt;
		if string.match(val,ingredient) then return true end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_list(args)&lt;br /&gt;
	local list = {}&lt;br /&gt;
	if args.item then&lt;br /&gt;
		list[#list+1] = args.item&lt;br /&gt;
	else&lt;br /&gt;
		for craft_name,craft in spairs(MD[args.merchant].crafts) do&lt;br /&gt;
			-- if filtering for ingredients, ensure the craft requires the ingredient&lt;br /&gt;
			if args.ingredient then&lt;br /&gt;
				if has_ingredient(craft.ingredients,args.ingredient) then&lt;br /&gt;
					list[#list+1] = craft_name&lt;br /&gt;
				end&lt;br /&gt;
			-- else include all crafts&lt;br /&gt;
			elseif args.merchant ~= nil then&lt;br /&gt;
				list[#list+1] = craft_name&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return list&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw_table(f)&lt;br /&gt;
	local item_list = get_list(f.args)&lt;br /&gt;
	local merchant = f.args.merchant&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;table cellspacing=&#039;0&#039; class=&#039;wikitable sortable jquery-tablesorter&#039; style=&#039;width:70%;min-width:500px;text-align:center;vertical-align:middle&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	if #item_list ~= 0 then&lt;br /&gt;
		craft_table_header(wt)&lt;br /&gt;
		for _,craft_name in ipairs(item_list) do&lt;br /&gt;
			craft_row(wt,false,MD[merchant].crafts[craft_name],merchant)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;quot;Failure&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/table&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return table.concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- mw.log(p.draw_table({args={item=&amp;quot;Void Blade&amp;quot;,merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;}}))&lt;br /&gt;
-- mw.log(p.draw_table({args={merchant=&amp;quot;Weaponsmith&amp;quot;,ingredient=&amp;quot;Iron Ore&amp;quot;}}))&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65131</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=65131"/>
		<updated>2026-03-25T22:00:12Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Adding margins between stats.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link == &amp;quot;nolink&amp;quot; then&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	elseif args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.name, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, weapon)&lt;br /&gt;
	if count(weapon.stats.order) == 0 or weapon.numenchants[args.rarity] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(weapon.stats.order) do&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,weapon.stats[(stat_name):lower()][tonumber(args.rarity)] or weapon.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,weapon.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.haspassive then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,weapon.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i~=1 then ret = ret..&amp;quot;,&amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if v==type then ret=ret..k end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,accessory)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,utility)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,utility.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,utility.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,utility.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(utility.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,misc)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,misc.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,link=&amp;quot;nolink&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Cinder&amp;quot;,is_active=&amp;quot;8&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64873</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64873"/>
		<updated>2026-03-24T22:47:29Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link == &amp;quot;nolink&amp;quot; then&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	elseif args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.name, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, weapon)&lt;br /&gt;
	if count(weapon.stats.order) == 0 or weapon.numenchants[args.rarity] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(weapon.stats.order) do&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		else         wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,weapon.stats[(stat_name):lower()][tonumber(args.rarity)] or weapon.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,weapon.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.haspassive then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,weapon.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i~=1 then ret = ret..&amp;quot;,&amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if v==type then ret=ret..k end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,accessory)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,utility)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,utility.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,utility.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,utility.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(utility.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,misc)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,misc.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,link=&amp;quot;nolink&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Cinder&amp;quot;,is_active=&amp;quot;8&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox/doc&amp;diff=64861</id>
		<title>Module:Infobox/doc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox/doc&amp;diff=64861"/>
		<updated>2026-03-24T06:07:47Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: /* draw examples */ Example without is_active&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Overview=&lt;br /&gt;
Functions for making [[Iconbox]] table. Data comes from various data jsons: [[Data:Weapon.json]], [[Data:Armor.json]], [[Data:Accessory.json]], [[Data:Utility.json]], [[Data:Misc.json]], [[Data:Monster.json]], [[Data:Prop.json]].&lt;br /&gt;
&lt;br /&gt;
=Functions=&lt;br /&gt;
==draw==&lt;br /&gt;
Creates an iconbox that floats to the right.&lt;br /&gt;
&lt;br /&gt;
===Parameters===&lt;br /&gt;
* &#039;&#039;name&#039;&#039; - &amp;lt;Name&amp;gt;&lt;br /&gt;
* &#039;&#039;is_active&#039;&#039; - {1,2,3,4,5,6,7,8,Common,Elite,Nightmare}&lt;br /&gt;
* &#039;&#039;category&#039;&#039; - {Weapon,Armor,Accessory,Utility,Misc,Monster,Prop}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==draw examples==&lt;br /&gt;
&amp;lt;h3 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Halberd,7,Weapon&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Halberd|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Halberd|category=Weapon}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Cinder,8,Weapon&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Cinder|is_active=8|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Cinder|is_active=8|category=Weapon}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Ranger Hood,5,Armor&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Ranger Hood|is_active=5|category=Armor}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Ranger Hood|is_active=5|category=Armor}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h1 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Code&amp;lt;/h1&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64860</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64860"/>
		<updated>2026-03-24T06:07:12Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Added some data validation for is_active&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link == &amp;quot;nolink&amp;quot; then&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	elseif args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.name, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, weapon)&lt;br /&gt;
	if count(weapon.stats.order) == 0 or weapon.numenchants[args.rarity] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(weapon.stats.order) do&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		else         wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,weapon.stats[(stat_name):lower()][tonumber(args.rarity)] or weapon.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,weapon.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.haspassive then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,weapon.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i~=1 then ret = ret..&amp;quot;,&amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if v==type then ret=ret..k end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,utility)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,utility.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,utility.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,utility.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(utility.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,misc)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,misc.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	if not frame.args.is_active then frame.args.is_active = data.rarities[1] end -- If missing argument, make the first make the first rarity infobox active&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,link=&amp;quot;nolink&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Cinder&amp;quot;,is_active=&amp;quot;8&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox/doc&amp;diff=64859</id>
		<title>Module:Infobox/doc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox/doc&amp;diff=64859"/>
		<updated>2026-03-23T06:48:41Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Fixed sections beginning too high.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Overview=&lt;br /&gt;
Functions for making [[Iconbox]] table. Data comes from various data jsons: [[Data:Weapon.json]], [[Data:Armor.json]], [[Data:Accessory.json]], [[Data:Utility.json]], [[Data:Misc.json]], [[Data:Monster.json]], [[Data:Prop.json]].&lt;br /&gt;
&lt;br /&gt;
=Functions=&lt;br /&gt;
==draw==&lt;br /&gt;
Creates an iconbox that floats to the right.&lt;br /&gt;
&lt;br /&gt;
===Parameters===&lt;br /&gt;
* &#039;&#039;name&#039;&#039; - &amp;lt;Name&amp;gt;&lt;br /&gt;
* &#039;&#039;is_active&#039;&#039; - {1,2,3,4,5,6,7,8,Common,Elite,Nightmare}&lt;br /&gt;
* &#039;&#039;category&#039;&#039; - {Weapon,Armor,Accessory,Utility,Misc,Monster,Prop}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==draw examples==&lt;br /&gt;
&amp;lt;h3 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Halberd,7,Weapon&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Halberd|is_active=7|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Halberd|is_active=7|category=Weapon}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Cinder,8,Weapon&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Cinder|is_active=8|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Cinder|is_active=8|category=Weapon}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Ranger Hood,5,Armor&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Ranger Hood|is_active=5|category=Armor}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Ranger Hood|is_active=5|category=Armor}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h1 style=&amp;quot;width:100%; display:inline-block&amp;quot;&amp;gt;Code&amp;lt;/h1&amp;gt;&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox/doc&amp;diff=64858</id>
		<title>Module:Infobox/doc</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox/doc&amp;diff=64858"/>
		<updated>2026-03-23T06:46:04Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Created page with &amp;quot;=Overview= Functions for making Iconbox table. Data comes from various data jsons: Data:Weapon.json, Data:Armor.json, Data:Accessory.json, Data:Utility.json, Data:Misc.json, Data:Monster.json, Data:Prop.json.  =Functions= ==draw== Creates an iconbox that floats to the right.  ===Parameters=== * &amp;#039;&amp;#039;name&amp;#039;&amp;#039; - &amp;lt;Name&amp;gt; * &amp;#039;&amp;#039;is_active&amp;#039;&amp;#039; - {1,2,3,4,5,6,7,8,Common,Elite,Nightmare} * &amp;#039;&amp;#039;category&amp;#039;&amp;#039; - {Weapon,Armor,Accessory,Utility,Misc,Monster,Prop}...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=Overview=&lt;br /&gt;
Functions for making [[Iconbox]] table. Data comes from various data jsons: [[Data:Weapon.json]], [[Data:Armor.json]], [[Data:Accessory.json]], [[Data:Utility.json]], [[Data:Misc.json]], [[Data:Monster.json]], [[Data:Prop.json]].&lt;br /&gt;
&lt;br /&gt;
=Functions=&lt;br /&gt;
==draw==&lt;br /&gt;
Creates an iconbox that floats to the right.&lt;br /&gt;
&lt;br /&gt;
===Parameters===&lt;br /&gt;
* &#039;&#039;name&#039;&#039; - &amp;lt;Name&amp;gt;&lt;br /&gt;
* &#039;&#039;is_active&#039;&#039; - {1,2,3,4,5,6,7,8,Common,Elite,Nightmare}&lt;br /&gt;
* &#039;&#039;category&#039;&#039; - {Weapon,Armor,Accessory,Utility,Misc,Monster,Prop}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==draw examples==&lt;br /&gt;
&amp;lt;h3&amp;gt;Halberd,7,Weapon&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Halberd|is_active=7|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Halberd|is_active=7|category=Weapon}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3&amp;gt;Cinder,8,Weapon&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Cinder|is_active=8|category=Weapon}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Cinder|is_active=8|category=Weapon}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3&amp;gt;Ranger Hood,5,Armor&amp;lt;/h3&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;pre&amp;gt;{{#invoke:Infobox|draw|name=Ranger Hood|is_active=5|category=Armor}}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{#invoke:Infobox|draw|name=Ranger Hood|is_active=5|category=Armor}}&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=Code=&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64857</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64857"/>
		<updated>2026-03-23T06:31:36Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Fleshed out the item category row functions.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link == &amp;quot;nolink&amp;quot; then&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	elseif args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.name, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, weapon)&lt;br /&gt;
	if count(weapon.stats.order) == 0 or weapon.numenchants[args.rarity] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(weapon.stats.order) do&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		else         wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,weapon.stats[(stat_name):lower()][tonumber(args.rarity)] or weapon.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,weapon.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.haspassive then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,weapon.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i~=1 then ret = ret..&amp;quot;,&amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if v==type then ret=ret..k end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,armor.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,armor.invwidth,armor.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,armor.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,armor.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(armor.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,armor)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,accessory.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,accessory.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,accessory.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(accessory.flavortext)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,utility)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(armor.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,utility.slottype)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,accessory.invwidth,accessory.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,utility.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,utility.sellprices[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(utility.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,misc)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Type&amp;quot;,type_list(misc.types,&amp;quot;Misc&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,misc.invwidth,misc.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,misc.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,misc.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Item Achieve&amp;quot;,misc.ap[args.rarity])&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(misc.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
	error(&amp;quot;monster_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
	error(&amp;quot;prop_rows() needs to be implemented&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,link=&amp;quot;nolink&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Cinder&amp;quot;,is_active=&amp;quot;8&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64856</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64856"/>
		<updated>2026-03-23T06:20:54Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Pivoting from module inheritance to dynamic data loading and bespoke functions for each category.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_h(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function row_v(title, content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v2(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function row_v3(content)&lt;br /&gt;
	if not content or content == &amp;quot;&amp;quot; or content == 0 then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link == &amp;quot;nolink&amp;quot; then&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	elseif args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.name, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, args, weapon)&lt;br /&gt;
	if count(weapon.stats.order) == 0 or weapon.numenchants[args.rarity] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(weapon.stats.order) do&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		else         wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,weapon.stats[(stat_name):lower()][tonumber(args.rarity)] or weapon.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,weapon.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.haspassive then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,weapon.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i~=1 then ret = ret..&amp;quot;,&amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if v==type then ret=ret..k end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function weapon_rows(rows, args, weapon)&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	stat_block(rows,args,weapon)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = hline()&lt;br /&gt;
	rows[#rows+1] = row_v3(weapon.flavortext)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function armor_rows(rows, args, armor)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function accessory_rows(rows, args, accessory)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function utility_rows(rows, args, utility)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function misc_rows(rows, args, misc)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function monster_rows(rows, args, monster)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function prop_rows(rows, args, prop)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local CATEGORY = {	[&amp;quot;Weapon&amp;quot;]    = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Weapon.json&amp;quot;   , [&amp;quot;Function&amp;quot;]=weapon_rows},&lt;br /&gt;
					[&amp;quot;Armor&amp;quot;]     = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Armor.json&amp;quot;    , [&amp;quot;Function&amp;quot;]=armor_rows},&lt;br /&gt;
					[&amp;quot;Accessory&amp;quot;] = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Accessory.json&amp;quot;, [&amp;quot;Function&amp;quot;]=accessory_rows},&lt;br /&gt;
					[&amp;quot;Utility&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Utility.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=utility_rows},&lt;br /&gt;
					[&amp;quot;Misc&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Misc.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=misc_rows},&lt;br /&gt;
					[&amp;quot;Monster&amp;quot;]   = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Monster.json&amp;quot;  , [&amp;quot;Function&amp;quot;]=monster_rows},&lt;br /&gt;
					[&amp;quot;Prop&amp;quot;]      = {[&amp;quot;Data&amp;quot;]=&amp;quot;Data:Prop.json&amp;quot;     , [&amp;quot;Function&amp;quot;]=prop_rows}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function get_rows(args, data)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
&lt;br /&gt;
	if CATEGORY[args.category] then CATEGORY[args.category].Function(rows,args,data)&lt;br /&gt;
	else error(&amp;quot;Module:Infobox received an invalid category.  Ensure it is capitalized and singular.&amp;quot;) end&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args, data)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = get_rows(args, data)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args, data))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args, data)&lt;br /&gt;
	if not search(data.rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local data = mw.loadJsonData(CATEGORY[frame.args.category].Data)&lt;br /&gt;
	data = data[frame.args.category][frame.args.name] -- load data of a specific object&lt;br /&gt;
&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt, data)&lt;br /&gt;
	tab_panels(frame.args, wt, data)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,link=&amp;quot;nolink&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Cinder&amp;quot;,is_active=&amp;quot;8&amp;quot;,category=&amp;quot;Weapon&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64855</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64855"/>
		<updated>2026-03-23T05:11:21Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: Abandoning module inheritance for now.  Doing the dumb way of having a manual category argument handle the differences in infoboxes.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
p.__index = p&lt;br /&gt;
&lt;br /&gt;
local WD = mw.loadJsonData(&amp;quot;Data:Weapon.json&amp;quot;)&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
local function count(t)&lt;br /&gt;
	local c = 0&lt;br /&gt;
	for _,_ in pairs(t) do c = c + 1 end&lt;br /&gt;
	return c&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(t,v)&lt;br /&gt;
	for ind,val in ipairs(t) do&lt;br /&gt;
		if v == val then return true end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p:hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p:row_h(title, content)&lt;br /&gt;
	if not content then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p:row_v(title, content)&lt;br /&gt;
	if not content then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function p:row_v2(content)&lt;br /&gt;
	if not content then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function p:row_v3(content)&lt;br /&gt;
	if not content then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link == &amp;quot;nolink&amp;quot; then&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	elseif args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.name, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = p:getRows(args)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args)&lt;br /&gt;
	if not search(WD.Weapon[args.name].rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
	&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
	&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
	&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args)&lt;br /&gt;
	if not search(WD.Weapon[args.name].rarities,rarity) then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stat_block(wt, weapon, args)&lt;br /&gt;
	if count(weapon.stats.order) == 0 or weapon.numenchants[args.rarity] == 0 then return end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;text-align:center&#039;&amp;gt;&amp;quot;&lt;br /&gt;
	for i,stat_name in ipairs(weapon.stats.order) do&lt;br /&gt;
		if i==1 then wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		else         wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;display:flex;width:100%;flex-direction:row;justify-content:space-between&#039;&amp;gt;&amp;quot; end&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s %s&amp;lt;/div&amp;gt;&amp;quot;,stat_name,weapon.stats[(stat_name):lower()][tonumber(args.rarity)] or weapon.stats[(stat_name):lower()])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.numenchants[tonumber(args.rarity)] then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cr4&#039; style=&#039;margin-top:10px;display:flex; width:100%;flex-direction:row; justify-content:space-between&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;Up to %i extra enchantments&amp;lt;/div&amp;gt;&amp;quot;,weapon.numenchants[tonumber(args.rarity)])&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if weapon.haspassive then&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;margin-top:10px;display:flex;width:100%;flex-direction:row;justify-content:space-between;color:rgb(211,178,125)&#039;&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
		wt[#wt+1] = string.format(&amp;quot;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,weapon.artifactpower)&lt;br /&gt;
		wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;padding:0px 5px&#039; class=&#039;bold&#039;&amp;gt;-&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function class_list(list)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for i,v in ipairs(list) do&lt;br /&gt;
		if i~=1 then ret = ret..&amp;quot;,&amp;quot; end&lt;br /&gt;
		ret = ret..v&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function type_list(t,type)&lt;br /&gt;
	local ret = &amp;quot;&amp;quot;&lt;br /&gt;
	for k,v in pairs(t) do&lt;br /&gt;
		if v==type then ret=ret..k end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p:getRows(args)&lt;br /&gt;
	local rows = {}&lt;br /&gt;
	local weapon = WD.Weapon[args.name]&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = self:hline()&lt;br /&gt;
	stat_block(rows,weapon,args)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = self:hline()&lt;br /&gt;
	rows[#rows+1] = self:row_v(&amp;quot;Required Class&amp;quot;,class_list(weapon.classes))&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Weapon Type&amp;quot;,type_list(weapon.types,&amp;quot;Weapon&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Utility Type&amp;quot;,type_list(weapon.types,&amp;quot;Utility&amp;quot;))&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Slot Type&amp;quot;,weapon.slottype)&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Hand Type&amp;quot;,weapon.handtype)&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Reload Speed&amp;quot;,weapon.reloadtime)&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Quiver Size&amp;quot;,weapon.maxammocount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = self:hline()&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Hitbox&amp;quot;,string.format(&amp;quot;%sx%sx%s&amp;quot;,weapon.hitbox.height,weapon.hitbox.width,weapon.hitbox.depth))&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = self:hline()&lt;br /&gt;
	rows[#rows+1] = self:row_v2(string.format(&amp;quot;[[File:%sx%sInvSlot.jpg]]&amp;quot;,weapon.invwidth,weapon.invheight))&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Gear Score&amp;quot;,weapon.gearscore[args.rarity])&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Sell Price&amp;quot;,weapon.sellprices[args.rarity])&lt;br /&gt;
	rows[#rows+1] = self:row_h(&amp;quot;Stack Size&amp;quot;,weapon.maxcount)&lt;br /&gt;
&lt;br /&gt;
	rows[#rows+1] = self:hline()&lt;br /&gt;
	rows[#rows+1] = self:row_v3(weapon.flavortext)&lt;br /&gt;
&lt;br /&gt;
	return rows&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt)&lt;br /&gt;
	tab_panels(frame.args, wt)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,link=&amp;quot;nolink&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,is_active=&amp;quot;2&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Cinder&amp;quot;,is_active=&amp;quot;8&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
	<entry>
		<id>https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64854</id>
		<title>Module:Infobox</title>
		<link rel="alternate" type="text/html" href="https://darkanddarker.wiki.spellsandguns.com/index.php?title=Module:Infobox&amp;diff=64854"/>
		<updated>2026-03-23T04:14:15Z</updated>

		<summary type="html">&lt;p&gt;Raw Salad: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
p.__index = p&lt;br /&gt;
&lt;br /&gt;
local WD = mw.loadJsonData(&amp;quot;Data:Weapon.json&amp;quot;)&lt;br /&gt;
local concat = table.concat&lt;br /&gt;
&lt;br /&gt;
function p:hline()&lt;br /&gt;
	return &amp;quot;&amp;lt;div class=&#039;line&#039; style=&#039;background-image:linear-gradient(to right,rgb(10,10,10),rgb(100,100,100),rgb(10,10,10))&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p:row_h(title, content)&lt;br /&gt;
	if not content then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:row;justify-content:center;white-space:pre-wrap;color:rgb(160,160,140)&#039;&amp;gt;&amp;lt;div style=&#039;text-align:right; color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p:row_v(title, content)&lt;br /&gt;
	if not content then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;color:rgb(160,160,140);margin-bottom:5px&#039;&amp;gt;&amp;lt;div style=&#039;color:rgb(70,70,70)&#039;&amp;gt;%s: &amp;lt;/div&amp;gt;&amp;lt;div&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, title, content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function p:row_v2(content)&lt;br /&gt;
	if not content then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;margin-top:10px;flex-direction:column;align-items:center;text-align:center;margin-bottom:15px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function p:row_v3(content)&lt;br /&gt;
	if not content then return &amp;quot;&amp;quot; end&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center;text-align:center;color:rgb(115,83,67)&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;,content)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function header(args)&lt;br /&gt;
	local header_content = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.link == &amp;quot;nolink&amp;quot; then&lt;br /&gt;
		header_content = string.format(&amp;quot;&amp;lt;span class=&#039;bold cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;&amp;quot;, args.rarity, args.name)&lt;br /&gt;
	elseif args.link then&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.link, args.rarity, args.name)&lt;br /&gt;
	else&lt;br /&gt;
		header_content = string.format(&amp;quot;[[%s|&amp;lt;span class=&#039;cr%s&#039;&amp;gt;%s&amp;lt;/span&amp;gt;]]&amp;quot;, args.name, args.rarity, args.name)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&lt;br /&gt;
		&amp;quot;&amp;lt;div class=&#039;itemboxheader&#039; style=&#039;width:280px;padding:20px;box-shadow:inset 0px 0px 20px 5px rgb(0 0 0 / 0.5);display:flex;flex-direction:column;align-items:center;background-color:rgb(var(--rarity-%s),0.1);border:2px solid rgb(var(--rarity-%s),0.5)&#039;&amp;gt;&amp;lt;div class=&#039;bold&#039; style=&#039;margin-top:0px!important;font-size:22px&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;,&lt;br /&gt;
		args.rarity, args.rarity, header_content&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function image(args)&lt;br /&gt;
	if not args.name then return end&lt;br /&gt;
	return &amp;quot;&amp;lt;div style=&#039;display:flex;flex-direction:column;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;iconbox&#039; style=&#039;width:max-content;align-items:center&#039;&amp;gt;&amp;lt;div class=&#039;rarity-1 rounded relative&#039;&amp;gt;[[File:&amp;quot;..args.name..&amp;quot;.png|90px|link=]]&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function infobox(args)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;background-color:rgb(var(--background-color),0.3);max-width:470px;float:right&#039;&amp;gt;&amp;quot; --Infobox container&lt;br /&gt;
	wt[#wt+1] = header(args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div style=&#039;width:280px;border:2px solid rgb(70,70,70,0.5);padding:20px&#039;&amp;gt;&amp;quot; -- Content container&lt;br /&gt;
	wt[#wt+1] = image(args)&lt;br /&gt;
&lt;br /&gt;
	-- Category-specific rows&lt;br /&gt;
	local rows = p:getRows(args)&lt;br /&gt;
	for _, row in ipairs(rows) do&lt;br /&gt;
		wt[#wt+1] = row&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function panel(rarity, args)&lt;br /&gt;
	if not WD.Weapon[args.name].rarities[tonumber(rarity)] then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	args.rarity = rarity&lt;br /&gt;
&lt;br /&gt;
	local hidden_panel = &amp;quot;cardtabs__panel--js-hidden&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then hidden_panel = &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-panel=&#039;%s&#039; style=&#039;overflow:auto&#039; class=&#039;cardtabs__panel %s&#039;&amp;gt;%s&amp;lt;/div&amp;gt;&amp;quot;, rarity, hidden_panel, infobox(args))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_panels(args, wt)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;mytabs__panels&#039; data-cardtabs-panels&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;1&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;2&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;3&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;4&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;5&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;6&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;7&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = panel(&amp;quot;8&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local ROMAN_NUMERAL = {&amp;quot;I&amp;quot;,&amp;quot;II&amp;quot;,&amp;quot;III&amp;quot;,&amp;quot;IV&amp;quot;,&amp;quot;V&amp;quot;,&amp;quot;VI&amp;quot;,&amp;quot;VII&amp;quot;,&amp;quot;VIII&amp;quot;}&lt;br /&gt;
&lt;br /&gt;
local function button(rarity, args)&lt;br /&gt;
	if not WD.Weapon[args.name].rarities[tonumber(rarity)] then return &amp;quot;&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	local is_active_button = &amp;quot;&amp;quot;&lt;br /&gt;
	if args.is_active==rarity then is_active_button = &amp;quot; cardtabs__button--active&amp;quot; end&lt;br /&gt;
&lt;br /&gt;
	return string.format(&amp;quot;&amp;lt;div data-cardtabs-btn=&#039;%s&#039; class=&#039;cardtabs__button rarity%s%s&#039;&amp;gt;&amp;amp;nbsp;%s&amp;amp;nbsp;&amp;lt;/div&amp;gt;&amp;quot;, rarity, rarity, is_active_button, ROMAN_NUMERAL[tonumber(rarity)])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tab_buttons(args, wt)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs__buttons&#039; data-cardtabs-btns&amp;gt;&amp;quot;&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;1&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;2&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;3&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;4&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;5&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;6&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;7&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = button(&amp;quot;8&amp;quot;, args)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.draw(frame)&lt;br /&gt;
	local wt = {}&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;div class=&#039;cardtabs&#039; data-cardtabs&amp;gt;&amp;quot;&lt;br /&gt;
	tab_buttons(frame.args, wt)&lt;br /&gt;
	tab_panels(frame.args, wt)&lt;br /&gt;
	wt[#wt+1] = &amp;quot;&amp;lt;/div&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
	return concat(wt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p:inherit()&lt;br /&gt;
	local Subclass = {}&lt;br /&gt;
	Subclass.__index = Subclass&lt;br /&gt;
	return setmetatable(Subclass, self)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;br /&gt;
&lt;br /&gt;
--[=[&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;,link=&amp;quot;nolink&amp;quot;}}))&lt;br /&gt;
mw.log(p.draw({args={name=&amp;quot;Halberd&amp;quot;}}))&lt;br /&gt;
]=]&lt;/div&gt;</summary>
		<author><name>Raw Salad</name></author>
	</entry>
</feed>