From Dark and Darker Wiki

(Added firearms to ranged type tables.)
No edit summary
 
(6 intermediate revisions by the same user not shown)
Line 71: Line 71:
end
end


return "<td><div class='iconbox'><div class='rarity"..color.." rounded relative'>[[File:"..item.name..".png|"..size(item).."|link="..item.name.."]]"
return "<td><div class='iconbox' style='width:max-content;align-items:center'><div class='rarity"..color.." rounded relative'>[[File:"..item.name..".png|"..size(item).."|link="..item.name.."]]"
..icon_amount
..icon_amount
.."</div><br>[["..item.name..bold_link.."</b>]]</div></td>"
.."</div><br>[["..item.name..bold_link.."</b>]]</div></td>"
Line 135: Line 135:
for j,attack in ipairs(weapon.abilities[ability].order) do
for j,attack in ipairs(weapon.abilities[ability].order) do
if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then
if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then
wt = wt.."<br><span style='color:#eee8'>"..ability.." "..attack.."</span><br>"
wt = wt.."<span style='color:#eee8'>"..ability.." "..attack.."</span><br>"
for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do
for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do
if k~=1 then wt = wt.."/" end
if k~=1 then wt = wt.."/" end
wt = wt..impact_zone
wt = wt..impact_zone
end
end
wt = wt.." + "..weapon.abilities[ability][attack].impactpower.."<br>"
wt = wt.." + "..weapon.abilities[ability][attack].impactpower.."<br><br>"
end
end
end
end
end
end
end
end
return "<td>"..wt.."<br></td>"
if weapon.impactresistance~="" then
wt = wt.."<span style='color:#eee8'>Impact Resist</span><br>"..weapon.impactresistance
end
return "<td>"..wt.."</td>"
end
end


Line 177: Line 180:
local function action_move_speed_penalty(weapon,is_header)
local function action_move_speed_penalty(weapon,is_header)
if is_header then return [=[<th style="width:10%">[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]</th>]=] end
if is_header then return [=[<th style="width:10%">[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]</th>]=] end
local order = {} --reconstruct order to exclude the other ability group
for _,ability in ipairs(weapon.abilities.order) do
if ability ~= "Other" then order[#order+1] = ability end
end


local wt = ""
local wt = ""
for i,ability_name in ipairs(weapon.abilities.order) do
for i,ability_name in ipairs(order) do
if i~=1 then wt = wt.."<br><br>" end
if i~=1 then wt = wt.."<br><br>" end


Line 289: Line 297:
return "<td>"..wt..duration.."s</td>"
return "<td>"..wt..duration.."s</td>"
end
end


---Formats a table cell containing the projectile's initial speed
---Formats a table cell containing the projectile's initial speed
Line 308: Line 317:
if is_header then return [[<th style="width:10%">Hitbox + Impact Resist</th>]] end
if is_header then return [[<th style="width:10%">Hitbox + Impact Resist</th>]] end


if weapon.impactresistance~="" then
return "<td>[[File:"..weapon.name.." Hitbox.png|link=|"..size(weapon).."]]</td>"
return "<td>[[File:"..weapon.name.." Hitbox.png|link=|"..size(weapon).."]]<br><span style='color:#eee8'>Impact Resist</span><br>"..weapon.impactresistance.."</td>"
else
return "<td>[[File:"..weapon.name.." Hitbox.png|link=|"..size(weapon).."]]</td>"
end
end
end


Line 351: Line 356:
end
end
return "<td>"..wt.."</td>"
return "<td>"..wt.."</td>"
end
---@param weapon table
---@return string
local function determine_table_type(weapon)
if weapon.args then
if weapon.args[1] == "Shield" then return "Shield"
elseif (weapon.args[1]):lower():match("bow") or weapon.args[1] == "Firearm" or weapon.args[1] == "Throwable" then return "Ranged" end
elseif weapon.types then
if weapon.types.Shield then return "Shield"
elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm or weapon.types.Throwable then return "Ranged" end
end
return "Default"
end
end


Line 357: Line 377:
local row_type = {
local row_type = {
["Shield"] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},
["Shield"] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},
["Ranged"] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,animation_time,action_move_speed_penalty,slowdown_on_hit,initial_speed},
["Ranged"] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,action_move_speed_penalty,slowdown_on_hit,initial_speed},
["Default"] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}
["Default"] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}
}
}
Line 367: Line 387:
local function row(weapon, is_header)
local function row(weapon, is_header)
is_header = is_header or false
is_header = is_header or false
local key = "Default"
if is_header then
if weapon.args[1] == "Shield" then key = "Shield" elseif (weapon.args[1]):lower():match("bow") or weapon.args[1]=="Firearm" then key = "Ranged" end
else
if weapon.types.Shield then key = "Shield" elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm then key = "Ranged" end
end


local wt = {}
local wt = {}
wt[#wt+1] = "<tr>"
wt[#wt+1] = "<tr>"
for _,content in ipairs(row_type[key]) do
for _,content in ipairs(row_type[determine_table_type(weapon)]) do
wt[#wt+1] = content(weapon,is_header)
wt[#wt+1] = content(weapon,is_header)
end
end
Line 400: Line 413:
function p.draw_table(frame)
function p.draw_table(frame)
local wt = {}
local wt = {}
local item_list = get_list(frame)
if count(item_list) == 0 then return "<span style='font-size:24pt'>There are currently no items of this type.</span>" end


wt[#wt+1] = [[<table cellspacing="0" class="wikitable sortable jquery-tablesorter" style="text-align:center; vertical-align:middle;">]]
wt[#wt+1] = [[<table cellspacing="0" class="wikitable sortable jquery-tablesorter" style="text-align:center; vertical-align:middle;">]]
wt[#wt+1] = row(frame, true)
wt[#wt+1] = row(frame, true)
for _,item_name in ipairs(get_list(frame)) do
for _,item_name in ipairs(item_list) do
wt[#wt+1] = row(WD.Weapon[item_name])
wt[#wt+1] = row(WD.Weapon[item_name])
end
end

Latest revision as of 08:31, 15 March 2026

Overview

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

Functions

draw_table

Creates a table of weapons

Parameters

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


draw_table examples

Sword, Craftable


{{#invoke:Weapon|draw_table|Sword|Craftable}}


NameClassSlotMovement SpeedDamage on HitStatsHitbox + Impact ResistImpact Zones + Impact PowerComboAction Movement PenaltySlowdown On Hit
Fighter
Warlock
Sorcerer
Main-Hand
Two Handed
-30
Physical Base Weapon Damage
38
Undead Race Damage Bonus
15%
Blade of Righteousness Hitbox.pngPrimary Attack 1
100%/90% + 4

Primary Attack 2
100%/90% + 4

Primary Attack 3
100%/90% + 4

Special Riposte Attack 1
100%/90% + 4

Special Riposte Attack 2
100%/90% + 4

Impact Resist
3
Primary Attacks
Pierce/Slash/Slash
100%/105%/110%

Special Attacks
Slash/Slash
150%/150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%/85%
Hitslow
-30/-30/-30 for 0.35/0.35/0.35s
Ranger
Rogue
Bard
Main-Hand
One Handed
-15
Physical Base Weapon Damage
26
Magical Base Weapon Damage
1
Demon's Glee Hitbox.pngPrimary Attack 1
100%/90% + 2

Primary Attack 2
100%/90% + 2

Primary Attack 3
100%/90% + 2

Primary Attack 4
100%/90% + 2

Special Riposte Attack 1
100%/90% + 2

Special Riposte Attack 2
100%/90% + 2

Impact Resist
3
Primary Attacks
Pierce/Pierce/Pierce/Pierce
100%/105%/110%/115%

Special Attacks
Slash/Pierce
150%/150%
Primary Attacks
Mid Attack: x60%/60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%/85%
Hitslow
-15/-15/-15/-15 for 0.2/0.2/0.2/0.2s
Fighter
Warlock
Sorcerer
Main-Hand
Two Handed
-30
Physical Base Weapon Damage
39
Undead Race Damage Bonus
15%
Divine Blade Hitbox.pngPrimary Attack 1
100%/90% + 4

Primary Attack 2
100%/90% + 4

Primary Attack 3
100%/90% + 4

Special Riposte Attack 1
100%/90% + 4

Special Riposte Attack 2
100%/90% + 4

Impact Resist
3
Primary Attacks
Pierce/Slash/Slash
100%/105%/110%

Special Attacks
Slash/Slash
150%/150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%/85%
Hitslow
-30/-30/-30 for 0.35/0.35/0.35s
Fighter
Rogue
Ranger
Bard
Off-Hand
One Handed
-15
Physical Base Weapon Damage
26
Undead Race Damage Bonus
15%
Divine Short Sword Hitbox.pngSecondary Attack 1
100%/90% + 2

Secondary Attack 2
100%/90% + 2

Secondary Attack 3
100%/90% + 2

Special Riposte Attack 1
100%/90% + 2

Special Riposte Attack 2
100%/90% + 2

Impact Resist
3
Secondary Attacks
Slash/Slash/Pierce
100%/105%/110%

Special Attacks
Slash/Pierce
150%/150%
Secondary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%/85%
Hitslow
-15/-15/-30 for 0.2/0.2/0.2s
Fighter
Bard
Warlock
Sorcerer
Main-Hand
One Handed
-25
Physical Base Weapon Damage
37
Armor Penetration
2~3%
Falchion of Honor Hitbox.pngPrimary Attack 1
100%/90% + 4

Primary Attack 2
100%/90% + 4

Primary Attack 3
100%/90% + 4

Special Riposte Attack 1
100%/90% + 4

Impact Resist
3
Primary Attacks
Slash/Slash/Slash
100%/105%/110%

Special Attacks
Slash
150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%
Hitslow
-25/-25/-25 for 0.35/0.35/0.35s
Wizard
Warlock
Sorcerer
Main-Hand
Two Handed
-25
Physical Base Weapon Damage
13
Magical Base Weapon Damage
18
Action Speed
2%
Frostlight Crystal Sword Hitbox.pngPrimary Attack 1
100%/90% + 3

Primary Attack 2
100%/90% + 3

Primary Attack 3
100%/90% + 3

Special Riposte Attack 1
100%/90% + 3

Impact Resist
3
Primary Attacks
Slash/Slash/Slash
100%/105%/110%

Special Attacks
Slash
150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x85%

Special Attacks
Always: x85%
Hitslow
-25/-25/-25 for 0.35/0.35/0.35s
Fighter
Barbarian
Main-Hand
One Handed
-20
Physical Base Weapon Damage
33
Luck
10
Magical Damage Reduction
2%
Golden Viking Sword Hitbox.pngPrimary Attack 1
100%/90% + 3

Primary Attack 2
100%/90% + 3

Primary Attack 3
100%/90% + 3

Special Riposte Attack 1
100%/90% + 3

Impact Resist
3
Primary Attacks
Slash/Slash/Pierce
100%/105%/110%

Special Attacks
Slash
150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%
Hitslow
-20/-20/-20 for 0.35/0.35/0.35s
Fighter
Barbarian
Warlock
Main-Hand
Two Handed
-40
Physical Base Weapon Damage
45~46
Physical Power
5
Undead Race Damage Bonus
30%
Grimslayer Hitbox.pngPrimary Attack 1
100%/90% + 4

Primary Attack 2
100%/90% + 4

Primary Attack 3
100%/90% + 4

Special Riposte Attack 1
100%/90% + 4

Impact Resist
3
Primary Attacks
Slash/Slash/Slash
100%/105%/110%

Special Attacks
Slash
125%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x85%

Special Attacks
Always: x85%
Hitslow
-40/-40/-40 for 0.35/0.35/0.35s
Fighter
Rogue
Ranger
Bard
Off-Hand
One Handed
-15
Physical Base Weapon Damage
25
Undead Race Damage Bonus
15%
Short Sword of Righteousness Hitbox.pngSecondary Attack 1
100%/90% + 2

Secondary Attack 2
100%/90% + 2

Secondary Attack 3
100%/90% + 2

Special Riposte Attack 1
100%/90% + 2

Special Riposte Attack 2
100%/90% + 2

Impact Resist
3
Secondary Attacks
Slash/Slash/Pierce
100%/105%/110%

Special Attacks
Slash/Pierce
150%/150%
Secondary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%/85%
Hitslow
-15/-15/-30 for 0.2/0.2/0.2s
Wizard
Warlock
Sorcerer
Main-Hand
Two Handed
-25
Physical Base Weapon Damage
13
Magical Base Weapon Damage
18
Outgoing Magical Healing Add
2
Move Speed Bonus
2%
Sovereign's Ghostblade Hitbox.pngPrimary Attack 1
100%/90% + 3

Primary Attack 2
100%/90% + 3

Primary Attack 3
100%/90% + 3

Special Riposte Attack 1
100%/90% + 3

Impact Resist
3
Primary Attacks
Slash/Slash/Slash
100%/105%/110%

Special Attacks
Slash
150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x85%

Special Attacks
Always: x85%
Hitslow
-25/-25/-25 for 0.35/0.35/0.35s
Fighter
Warlock
Sorcerer
Main-Hand
Two Handed
-30
Physical Base Weapon Damage
40
Action Speed
5%
Headshot Damage Modifier
5%
File:Spectral Blade Hitbox.pngPrimary Attack 1
100%/90% + 4

Primary Attack 2
100%/90% + 4

Primary Attack 3
100%/90% + 4

Special Riposte Attack 1
100%/90% + 4

Special Riposte Attack 2
100%/90% + 4

Impact Resist
3
Primary Attacks
Pierce/Slash/Slash
100%/105%/110%

Special Attacks
Slash/Slash
150%/150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%/85%
Hitslow
-30/-30/-30 for 0.35/0.35/0.35s
Fighter
Warlock
Sorcerer
Main-Hand
Two Handed
-30
Physical Base Weapon Damage
37
Undead Race Damage Bonus
15%
Sterling Blade Hitbox.pngPrimary Attack 1
100%/90% + 4

Primary Attack 2
100%/90% + 4

Primary Attack 3
100%/90% + 4

Special Riposte Attack 1
100%/90% + 4

Special Riposte Attack 2
100%/90% + 4

Impact Resist
3
Primary Attacks
Pierce/Slash/Slash
100%/105%/110%

Special Attacks
Slash/Slash
150%/150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%/85%
Hitslow
-30/-30/-30 for 0.35/0.35/0.35s
Fighter
Rogue
Ranger
Bard
Off-Hand
One Handed
-15
Physical Base Weapon Damage
24
Undead Race Damage Bonus
15%
Sterling Short Sword Hitbox.pngSecondary Attack 1
100%/90% + 2

Secondary Attack 2
100%/90% + 2

Secondary Attack 3
100%/90% + 2

Special Riposte Attack 1
100%/90% + 2

Special Riposte Attack 2
100%/90% + 2

Impact Resist
3
Secondary Attacks
Slash/Slash/Pierce
100%/105%/110%

Special Attacks
Slash/Pierce
150%/150%
Secondary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%/85%
Hitslow
-15/-15/-30 for 0.2/0.2/0.2s
Fighter
Bard
Warlock
Sorcerer
Main-Hand
One Handed
-25
Physical Base Weapon Damage
37
Debuff Duration Bonus
1.5%
File:Tidal Falchion Hitbox.pngPrimary Attack 1
100%/90% + 4

Primary Attack 2
100%/90% + 4

Primary Attack 3
100%/90% + 4

Special Riposte Attack 1
100%/90% + 4

Impact Resist
3
Primary Attacks
Slash/Slash/Slash
100%/105%/110%

Special Attacks
Slash
150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%
Hitslow
-25/-25/-25 for 0.35/0.35/0.35s
Fighter
Warlock
Sorcerer
Main-Hand
Two Handed
-30
Physical Base Weapon Damage
41
Magical Base Weapon Damage
2
True Magical Damage
2
Void Blade Hitbox.pngPrimary Attack 1
100%/90% + 4

Primary Attack 2
100%/90% + 4

Primary Attack 3
100%/90% + 4

Special Riposte Attack 1
100%/90% + 4

Special Riposte Attack 2
100%/90% + 4

Impact Resist
3
Primary Attacks
Pierce/Slash/Slash
100%/105%/110%

Special Attacks
Slash/Slash
150%/150%
Primary Attacks
Mid Attack: x60%/60%/60%
Otherwise: x92.5%/92.5%/92.5%

Block Actions
Always: x97%

Special Attacks
Always: x85%/85%
Hitslow
-30/-30/-30 for 0.35/0.35/0.35s


Firearm, Uncraftable


{{#invoke:Weapon|draw_table|Firearm|Uncraftable}}

Code

NameClassSlotMovement SpeedDamage on HitStatsImpact Zones + Impact PowerAction Movement PenaltySlowdown On HitInitial Projectile Speed (m/s)
Fighter
Ranger
Main-Hand
Two Handed
-30
Physical Base Weapon Damage
45~46
47~48
49~50
51~52
53~54
55~56
57~58
True Physical Damage
10
Primary Attack 1
100% + 6

Impact Resist
3
Primary Attacks
Always: x50%

Block Actions
Always: x85%
Hitslow
-135 for 1.5s
38

local p = {}
local WD = mw.loadJsonData("Data:Weapon.json") --Global var holding tables from Data:Weapon.json
local concat = table.concat


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


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


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

	local wt = ""
	local newline = ""
	for i, key in ipairs(weapon.rarities) do
		wt = wt..newline.."<span class='cr"..key.."'>"..tostring(values[tonumber(key)] or "").."</span>"
		if i == 1 then newline = "<br>" end
	end
	return wt
end


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


---Formats a table cell for the iconbox
---@param item table
---@return string
local function name(item, is_header)
	if is_header then return [[<th style="width:5%">Name</th>]] end

	local color = "2"
	local bold_link = "|<b>"..item.name
	if count(item.rarities) == 1 then
		color = item.rarities[1]
		bold_link = "|<b class=cr"..color..">"..item.name
	end

	local icon_amount = ""
	if item.maxammocount > 0 then
		icon_amount = "<span class='iconamount' style='pointer-events:none;color:#EEEA;font-size:16px'>"..item.maxammocount.."</span>"
	end

	return "<td><div class='iconbox' style='width:max-content;align-items:center'><div class='rarity"..color.." rounded relative'>[[File:"..item.name..".png|"..size(item).."|link="..item.name.."]]"
				..icon_amount
			.."</div><br>[["..item.name..bold_link.."</b>]]</div></td>"
end


---Formats the weapon's list of classes into a table cell of classes separated by linebreaks
--- @param weapon table
--- @param is_header boolean
--- @return string
local function classes(weapon,is_header)
	if is_header then return [[<th style="width:5%">Class</th>]] end

	local wt = ""
	local newline = ""
	for i, class in ipairs(weapon.classes) do
		wt = wt..newline..class
		if i == 1 then newline = "<br>" end -- Subsequent loops will prefix a linebreak
	end
	return "<td>"..wt.."</td>"
end


---Formats a table cell of slottype and handtype
--- @param weapon table
--- @param is_header boolean
--- @return string
local function slot_type(weapon,is_header)
	if is_header then return [[<th style="width:5%">Slot</th>]] end

	return "<td>"..weapon.slottype.."<br>"..weapon.handtype.."</td>"
end


---Formats a table cell of move speeds
--- @param weapon table
--- @param is_header boolean
--- @return string
local function move_speed(weapon,is_header)
	if is_header then return [[<th style="width:5%">Movement Speed</th>]] end

	return "<td>"..color_values(weapon,weapon.stats["move speed"]).."</td>"
end


---Formats a table cell of Armor rating and Magical Resistance
--- @param weapon table
--- @param is_header boolean
--- @return string
local function armor_rating(weapon,is_header)
	if is_header then return [[<th style="width:15%">Armor Rating</th>]] end

	return "<td>"..inline_block(weapon,"Armor Rating")..inline_block(weapon,"Magical Resistance").."</td>"
end


local function impact_zones(weapon,is_header)
	if is_header then return [[<th style="width:10%">Impact Zones + Impact Power</th>]] end

	local wt= ""
	for i,ability in ipairs(weapon.abilities.order) do
		if ability ~= "Other" then
			for j,attack in ipairs(weapon.abilities[ability].order) do
				if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then
					wt = wt.."<span style='color:#eee8'>"..ability.." "..attack.."</span><br>"
					for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do
						if k~=1 then wt = wt.."/" end
						wt = wt..impact_zone
					end
					wt = wt.." + "..weapon.abilities[ability][attack].impactpower.."<br><br>"
				end
			end
		end
	end
	if weapon.impactresistance~="" then
		wt = wt.."<span style='color:#eee8'>Impact Resist</span><br>"..weapon.impactresistance
	end
	return "<td>"..wt.."</td>"
end


---Helper function: Collects move mult and prepare move mult for display
---@param ability table
---@return string
---@return string
local function collect_multipliers(ability)
	local move, prep_move = "",""
	for i,attack_name in ipairs(ability.order) do
		if ability[attack_name].movementmultiplier then
			if i ~= 1 then
				move = move.."/"
				prep_move = prep_move.."/"
			end
			move = move..(ability[attack_name].movementmultiplier or "")
			prep_move = prep_move..(ability[attack_name].preparemovementmultiplier or "")
			i = i + 1
		end
	end

	return move,prep_move
end


---Formats a table cell containing speed penalties for each action
---@param weapon table
---@param is_header boolean
---@return string
local function action_move_speed_penalty(weapon,is_header)
	if is_header then return [=[<th style="width:10%">[[Weapons#Movement_Multiplier_Explanation|Action Movement Penalty]]</th>]=] end

	local order = {} --reconstruct order to exclude the other ability group
	for _,ability in ipairs(weapon.abilities.order) do
		if ability ~= "Other" then order[#order+1] = ability end
	end

	local wt = ""
	for i,ability_name in ipairs(order) do
		if i~=1 then wt = wt.."<br><br>" end

		wt = wt.."<span style='color:#eee8; font-size:110%; font-weight:bold'>"
		if ability_name=="Other" or ability_name=="Block" then wt = wt..ability_name.." Actions" else wt = wt..ability_name.." Attacks" end
		wt = wt.."</span><br>"

		local movement_multiplier,prepare_movement_multiplier = collect_multipliers(weapon.abilities[ability_name])
		if #prepare_movement_multiplier > 3 then
			wt = wt.."<span style='color:#eee8'>Mid Attack:&nbsp;</span>x"..movement_multiplier
			.."<br><span style='color:#eee8'>Otherwise:&nbsp;</span>x"..prepare_movement_multiplier
		else
			wt = wt.."<span style='color:#eee8'>Always:&nbsp;</span>x"..movement_multiplier
		end
	end
	return "<td>"..wt.."</td>"
end


---Formats a table cell containing base Physical and Magical damage, if present.
---@param weapon any
---@param is_header any
---@return string
local function damage_on_hit(weapon,is_header)
	if is_header then return [[<th style="width:25%">Damage on Hit</th>]] end

	local wt = ""
	wt = wt..inline_block(weapon,"Physical Base Weapon Damage")
			..inline_block(weapon,"Magical Base Weapon Damage")
	return "<td>"..wt.."</td>"
end


---These stats get their own table cells, as such they are excluded from the stats cell to avoid needless redundancy
local exclude_stat = {["Physical Base Weapon Damage"]=true,["Magical Base Weapon Damage"]=true,["Armor Rating"]=true,["Magical Resistance"]=true,["Move Speed"]=true}


---Formats a table cell containing any stats not covered by the rest of the row
---@param weapon table
---@param is_header boolean
---@return string
local function stats(weapon,is_header)
	if is_header then return [[<th style="width:12%">Stats</th>]] end

	local wt = ""
	for i,stat in ipairs(weapon.stats.order) do
		if not exclude_stat[stat] then
			wt = wt..inline_block(weapon,stat)
		end
	end
	return "<td>"..wt.."</td>"
end


---Formats a table cell containing animation times for ranges weapons
---@param weapon table
---@param is_header boolean
---@return string
local function animation_time(weapon,is_header)
	if is_header then return [[<th style="width:10%">Animation Times</th>]] end

	local windup = "<br>Windup:"
	local finish = "<br>Finish:"
	local reload = "<br><br><span style='color:#eee8'>Reload</span><br>"

	if weapon.animationtimes["Attack 1"] then
		-- windup = windup..weapon.animationtimes["Attack 1"].Windup
		-- finish = finish..weapon.animationtimes["Attack 1"].Finish
	else
		windup = ""
		finish = ""
	end
	if weapon.animationtimes["Other"] then
		-- reload = reload..weapon.animationtimes["Other"].Reload
	else
		reload = ""
	end

	if #windup == 0 and #finish == 0 and #reload == 0 then
		return ""
	else
		return "<td><span style='color:#eee8'>Primary Attack</span>"..windup..finish..reload.."</td>"
	end
end


---Formats a table cell containing hitslow for each attack
---@param weapon table
---@param is_header boolean
---@return string
local function slowdown_on_hit(weapon,is_header)
	if is_header then return [[<th style="width:8%">Slowdown On Hit</th>]] end
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return "<td></td>" end

	local ability_type = "Primary"
	if weapon.slottype == "Off-Hand" then ability_type = "Secondary" end

	local wt = "<span style='color:#eee8'>Hitslow</span><br>"
	local duration = " for "
	for i,attack in ipairs(weapon.abilities[ability_type].order) do
		if i~=1 then wt = wt.."/" end
		wt = wt..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global["Move Speed Add"]
				or weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global["Move Speed Bonus"] or "")
	end
	for i,attack in ipairs(weapon.abilities[ability_type].order) do
		if i~=1 then duration = duration.."/" end
		duration = duration..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global["Duration"] or "")
	end
	return "<td>"..wt..duration.."s</td>"
end


---Formats a table cell containing the projectile's initial speed
---@param weapon table
---@param is_header boolean
---@return string
local function initial_speed(weapon,is_header)
	if is_header then return [[<th style="width:5%">Initial Projectile Speed (m/s)</th>]] end

	return "<td>"..weapon.initialspeed.."</td>"
end


---Formats a table cell containing the hitbox image resized by inventory size
---@param weapon table
---@param is_header boolean
---@return string
local function hitbox(weapon,is_header)
	if is_header then return [[<th style="width:10%">Hitbox + Impact Resist</th>]] end

	return "<td>[[File:"..weapon.name.." Hitbox.png|link=|"..size(weapon).."]]</td>"
end


---Helps format a string containing either Combo Damage or damagetype values
---@param weapon table
---@param ability string
---@param value string
---@return string
local function format_combo(weapon,ability,value)
	local wt = ""
	for i,attack in ipairs(weapon.abilities[ability].order) do
		if i~=1 then wt = wt.."/" end
		wt = wt..(weapon.abilities[ability][attack][value] or "")
	end
	return wt
end


---Formats a table cell containing ability combo values per attack
---@param weapon table
---@param is_header boolean
---@return string
local function combo(weapon,is_header)
	if is_header then return [[<th style="width:8%">Combo</th>]] end

	local wt = ""
	if weapon.abilities["Primary"] then
		wt = wt.."<span style='color:#eee8'>Primary Attacks</span><br>"..format_combo(weapon,"Primary","damagetype").."<br>"..format_combo(weapon,"Primary","combodamage")
	end
	if weapon.abilities["Secondary"] then
		if #wt ~= 0 then wt = wt.."<br><br>" end
		wt = wt.."<span style='color:#eee8'>Secondary Attacks</span><br>"..format_combo(weapon,"Secondary","damagetype").."<br>"..format_combo(weapon,"Secondary","combodamage")
	end
	if weapon.abilities["Special"] then
		if wt:match("Primary") or wt:match("Secondary") then wt = wt.."<br><br>" end
		wt = wt.."<span style='color:#eee8'>Special Attacks</span><br>"..format_combo(weapon,"Special","damagetype").."<br>"..format_combo(weapon,"Special","combodamage")
	end
	return "<td>"..wt.."</td>"
end


---@param weapon table
---@return string
local function determine_table_type(weapon)
	if weapon.args then
		if weapon.args[1] == "Shield" then return "Shield"
		elseif (weapon.args[1]):lower():match("bow") or weapon.args[1] == "Firearm" or weapon.args[1] == "Throwable" then return "Ranged" end
	elseif weapon.types then
		if weapon.types.Shield then return "Shield"
		elseif weapon.types.Bow or weapon.types.Crossbow or weapon.types.Firearm or weapon.types.Throwable then return "Ranged" end
	end

	return "Default"
end


---Determines which table data goes into the table row
local row_type = {
	["Shield"] = {name,classes,slot_type,move_speed,armor_rating,impact_zones,action_move_speed_penalty},
	["Ranged"] = {name,classes,slot_type,move_speed,damage_on_hit,stats,impact_zones,action_move_speed_penalty,slowdown_on_hit,initial_speed},
	["Default"] = {name,classes,slot_type,move_speed,damage_on_hit,stats,hitbox,impact_zones,combo,action_move_speed_penalty,slowdown_on_hit}
}


---Return a table row of data cells containing weapon information,
--- @param weapon table
--- @return string
local function row(weapon, is_header)
	is_header = is_header or false

	local wt = {}
	wt[#wt+1] = "<tr>"
	for _,content in ipairs(row_type[determine_table_type(weapon)]) do
		wt[#wt+1] = content(weapon,is_header)
	end
	wt[#wt+1] = "</tr>"

	return concat(wt)
end

---Returns a predetermined weapon list
---@param frame table contains args, first key is determined by types, second key must be {Uncraftable, Craftable, Artifact}
---@return table
local function get_list(frame)
	if not (frame.args and frame.args[1] and frame.args[2]) then return {} end
	if not (WD[frame.args[1]] and WD[frame.args[1]][frame.args[2]]) then return {} end

	return WD[frame.args[1]][frame.args[2]]
end


--- @param frame any
--- @return string
function p.draw_table(frame)
	local wt = {}
	local item_list = get_list(frame)
	if count(item_list) == 0 then return "<span style='font-size:24pt'>There are currently no items of this type.</span>" end

	wt[#wt+1] = [[<table cellspacing="0" class="wikitable sortable jquery-tablesorter" style="text-align:center; vertical-align:middle;">]]
	wt[#wt+1] = row(frame, true)
	for _,item_name in ipairs(item_list) do
		wt[#wt+1] = row(WD.Weapon[item_name])
	end
	wt[#wt+1] = "</table>"

	return concat(wt)
end


return p
---- Test on wiki with:  mw.log(p.draw_table({args={"Crossbow","Uncraftable"}}))
---- Test on wiki with:  mw.log(p.draw_table({args={"Shield","Craftable"}}))
---- Test on wiki with:  mw.log(p.draw_table({args={"Polearm","Artifact"}}))