From Dark and Darker Wiki

(Moved references out from page invoke)
mNo edit summary
 
Line 578: Line 578:
local separator = "]] • [["
local separator = "]] • [["


wt[#wt+1] = "<table cellspacing='0' class='wikitable sortable jquery-tablesorter' style='width:100%;text-align:center;vertical-align:middle;margin-top:15px'><tr><td colspan='2' style='font-weight:bold; background-color:rgb(220,220,220,0.2)'>"
wt[#wt+1] = "<table cellspacing='0' class='wikitable sortable jquery-tablesorter' style='width:100%;text-align:center;vertical-align:middle;margin-top:15px'>"


for _,weapon_type in ipairs(types_order) do
for _,weapon_type in ipairs(types_order) do

Latest revision as of 10:34, 13 April 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
135%/135%
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
135%/135%
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
135%/135%
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
Bard
Warlock
Sorcerer
Main-Hand
One Handed
-23
Physical Base Weapon Damage
33
Projectile Damage Reduction
2%
File:Obsidian Cutlass Hitbox.pngPrimary Attack 1
100%/90% + 4

Primary Attack 2
100%/90% + 4

Primary Attack 3
100%/90% + 4

Primary Attack 4
100%/90% + 4

Special Riposte Attack 1
100%/90% + 4

Special Riposte Attack 2
100%/90% + 4

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

Special Attacks
Slash/Slash
135%/135%
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
-25/-25/-25/-25 for 0.35/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
135%/135%
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
135%/135%
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
135%/135%
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
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
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
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
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
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
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
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
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
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
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
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.
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
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
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
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
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
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
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
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

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,
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
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

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



--[=[###########################################################################
                Functions for individual weapon pages
#############################################################################]=]
local function projectile(wt,weapon_name)
	local weapon = WD.Weapon[weapon_name]
	if weapon.initialspeed ~= "" then 
		wt[#wt+1] = string.format("<h2>Projectile</h2>Initial Speed: %s<br>", weapon.initialspeed)
	end
	if weapon.piercecount ~= "" then
		wt[#wt+1] = string.format("Pierce: %s<br>", weapon.piercecount)
	end
end

local function damage_falloff(wt,weapon_name)
	local weapon = WD.Weapon[weapon_name]
	if weapon.damagefalloff == "" then return end

	wt[#wt+1] = string.format("<h2>Damage Falloff</h2>%s has damage falloff depending on airtime of the projectile.<br><br>Projectile falloff damage chart:<br>X: Air time (seconds)<br>Y: %% Damage",weapon_name,weapon.initialspeed)
	wt[#wt+1] = "<div style='width:500px'>"
	wt[#wt+1] = weapon.damagefalloff
	wt[#wt+1] = "</div><br>Learn more at [[Damage_Calculation#Projectile_Falloff|Projectile Falloff]]"
end

local function hitbox(wt,weapon_name)
	local weapon = WD.Weapon[weapon_name]
	if not weapon.hitbox.height then return end

	wt[#wt+1] = "<h2>Hitbox</h2>"
	wt[#wt+1] = "Height: "..weapon.hitbox.height
	wt[#wt+1] = "<br>Width: "..weapon.hitbox.width
	wt[#wt+1] = "<br>Depth: "..weapon.hitbox.depth

	-- If is not ranged, and has primary or secondary attacks
	if not (weapon.types["Bow"] or weapon.types["Crossow"] or weapon.types["ThrowableStuff"])
			and (weapon.abilities.Primary["Attack 1"].impactzones or weapon.abilities.Secondary["Attack 1"].impactzones) then
		local impact_zones = weapon.abilities.Primary["Attack 1"].impactzones or weapon.abilities.Secondary["Attack 1"].impactzones
		wt[#wt+1] = "<br><br>The weapon's hitbox doesn't necessarily determine a weapon's reach due to different arm extension during attack animations. Thus, shorter weapons can have better reach than longer weapons.<br>[[Impact Zones]] of "
		wt[#wt+1] = weapon.name
		wt[#wt+1] = ":<div style='display:flex;align-items:center'><div>[[File:"
		wt[#wt+1] = weapon.name
		wt[#wt+1] = " Hitbox.png|x300px]]</div><div><span style='color:green'>Green Zone deals "
		wt[#wt+1] = impact_zones[1]
		wt[#wt+1] = " damage</span>"
		if impact_zones[2] then
			wt[#wt+1] = "<br><br><span style='color:orange'>Orange Zone deals "
			wt[#wt+1] = impact_zones[2]
			wt[#wt+1] = " damage</span>"
		end
		if impact_zones[3] then
			wt[#wt+1] = "<br><br><span style='color:red'>Red Zone deals "
			wt[#wt+1] = impact_zones[3]
			wt[#wt+1] = " damage</span>"
		end
		wt[#wt+1] = "</div></div><p><br>'''Disclaimer''': The impact zone's locations are an approximation. The real hitboxes may be slightly larger, closer together, and/or follow the weapon'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].</p>"
	end

	if weapon.abilities.Primary["Attack 1"].impactpower or weapon.abilities.Secondary["Attack 1"].impactpower then
		wt[#wt+1] = "<br>'''Impact Power''' of "
		wt[#wt+1] = weapon.name
		wt[#wt+1] = ": "
		wt[#wt+1] = tostring(weapon.abilities.Primary["Attack 1"].impactpower or weapon.abilities.Secondary["Attack 1"].impactpower)
		wt[#wt+1] = "<br>Impact power helps with breaking crates, barricades or any other breakables in game. The higher the impact power the faster something breaks.<br>Impact power also affects shield blocking. If the attacking weapon's impact power is higher than shield's impact power, the shield holder staggers, and vice versa."
	end
end

local function list_to_string(l,separator)
	local ret = ""
	for i,v in ipairs(l) do
		if i~=1 then ret = ret..separator end
		ret = ret..v
	end
	return ret
end

local function combo_format(wt,attacks,type)
	if type == "Other" or type == "Block" then return end
	local first = ""

	wt[#wt+1] = "<span style='color:#eee8'>"
	wt[#wt+1] = type
	wt[#wt+1] = " Attack: </span>"

	for i,name in ipairs(attacks.order) do
		if i ~= 1 then wt[#wt+1] = "/" else first = name end
		wt[#wt+1] = attacks[name].damagetype
	end

	wt[#wt+1] = " dealing "

	for i,name in ipairs(attacks.order) do
		if i ~= 1 then wt[#wt+1] = "/" end
		wt[#wt+1] = attacks[name].combodamage
	end

	wt[#wt+1] = " damage on each swing, with impact zone: "
	wt[#wt+1] = list_to_string(attacks[first].impactzones, "/")
	wt[#wt+1] = "<br>"
end

local function attack_animation_times(wt,weapon,ability_name,attack_name)
	local attack = weapon.animationtimes[ability_name][attack_name]
	if attack["Windup"]            then wt[#wt+1] = string.format("Windup: %s<br>",                           attack["Windup"]) end
	if attack["Hit"]               then wt[#wt+1] = string.format("Hit: %s<br>",                              attack["Hit"]) end
	if attack["Second Windup"]     then wt[#wt+1] = string.format("Second Windup: %s<br>",                    attack["Second Windup"]) end
	if attack["Second Hit"]        then wt[#wt+1] = string.format("Second Hit: %s<br>",                       attack["Second Hit"]) end
	if attack["Recover"]           then wt[#wt+1] = string.format("&nbsp;&#10551; Recover: %s<br>",           attack["Recover"]) end
	if attack["Alternate Recover"] then wt[#wt+1] = string.format("&nbsp;&#10551; Alternate Recover: %s<br>", attack["Alternate Recover"]) end
	if attack["Finish"]            then wt[#wt+1] = string.format("&nbsp;&#10551; Finish: %s<br>",            attack["Finish"]) end
end

local function animation_format(wt, weapon, ability)
	if ability == "Other" or ability == "Block" then return end

	for i, name in ipairs(weapon.abilities[ability].order) do
		wt[#wt+1] = ability.." Combo: "..name.."<br><div style='display:inline-block;margin-left:20px'>"
		attack_animation_times(wt,weapon,ability,name)
		wt[#wt+1] = "</div><br><br>"
	end
end

local function combo(wt, page_name)
	local weapon = WD.Weapon[page_name]

	-- ensure that there is relevant data before continuing.
	if not (weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special) then return end

	-- If is not ranged, and isn't shield, or at least isn't a Lantern Shield
	if not (weapon.types.Bow or weapon.types.Crossow or weapon.types.ThrowableStuff)
		and (not weapon.types.Shield or (weapon.name):match("Lantern Shield")) then

		wt[#wt+1] = "<h2>Attack Animation Time</h2>"

		for _,name in ipairs(weapon.abilities.order) do
			combo_format(wt,weapon.abilities[name],name)
		end

		if weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special then
			wt[#wt+1] = "<br>'''Attack Times'''<br><div style='display:inline-block;margin-left:20px'>"
			for _,name in ipairs(weapon.abilities.order) do
				animation_format(wt,weapon,name)
			end
			wt[#wt+1] = "</div>"
		end
		wt[#wt+1] = "Reference [[Attack Times]] for a glossary of the terms used above.<br>"
	end
end

local function block(wt,weapon_name)
	local weapon = WD.Weapon[weapon_name]

	if not weapon.impactresistance then return end

	wt[#wt+1] = string.format("<h2>Block</h2>%s has a block ability with an impact resistance of '''%s'''.<br>To learn more about impact resistance see [[Impact Power]].", weapon.name, weapon.impactresistance)
end

local function action_move_slow_penalty(wt,weapon_name)
	local weapon = WD.Weapon[weapon_name]

	wt[#wt+1] = "<h2>Action Movement Slow</h2>When a player performs certain actions, such as attacking or defending themselves, they move slower."
	wt[#wt+1] = "<div style='display:inline-block;margin-left:20px'>"
	for _,ability in ipairs(weapon.abilities.order) do
		for _,attack in ipairs(weapon.abilities[ability].order) do
			-- There's a bug where Riposte values are recorded within "Other" abilities
			-- Since ripose is covered in Special abilities, we will skip it here
			if attack == "Riposte" then break end

			local movespeedadd = weapon.abilities[ability][attack].effects
								and weapon.abilities[ability][attack].effects.ItemActivateState
								and weapon.abilities[ability][attack].effects.ItemActivateState.rarity.Global["Move Speed Add"]
								or ""
			local movement_multiplier = weapon.abilities[ability][attack].movementmultiplier or ""
			local prepare_movement_multiplier = weapon.abilities[ability][attack].preparemovementmultiplier or ""

			wt[#wt+1] = "<span style='color:#eee8; font-size:110%; font-weight:bold'>"
			if ability == "Other" then wt[#wt+1] = attack else wt[#wt+1] = ability end
			wt[#wt+1] = "</span><br>"
			wt[#wt+1] = "<div style='display:inline-block;margin-left:20px'>"
			if #prepare_movement_multiplier > 1 then
				wt[#wt+1] = "<span style='color:#eee8'>Mid Attack:&nbsp;</span>x"
				wt[#wt+1] = movement_multiplier
				wt[#wt+1] = "<br><span style='color:#eee8'>Otherwise:&nbsp;</span>x"
				wt[#wt+1] = prepare_movement_multiplier
			elseif #movement_multiplier > 1 then
				wt[#wt+1] = "<span style='color:#eee8'>Always:&nbsp;</span>x"
				wt[#wt+1] = movement_multiplier
			else
				wt[#wt+1] = "<span style='color:#eee8'>Always:&nbsp;</span>"
				wt[#wt+1] = movespeedadd
			end
			wt[#wt+1] = "</div>"
			wt[#wt+1] = "<br><br>"

			-- Everything except attacks within Other has invariant move mults
			-- So after processessing the first attack, we break the loop
			if ability ~= "Other" then break end
		end
	end
	wt[#wt+1] = "</div>"
end

local function artifact(wt,weapon_name)
	local weapon = WD.Weapon[weapon_name]

	if weapon.isartifact or weapon.artifactname == "" then return end

	wt[#wt+1] = string.format("<h2>Artifact</h2><p>%s has a powerful [[Artifacts|Artifact]] named [[%s]].</p>", weapon.name, weapon.artifactname)
end

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

local function reference_table(wt,table_type)
	local types_order = {"Sword","Dagger","Mace","Polearm","Axe","Throwable","Bow","Crossbow","Firearm","MagicStuff","Shield","LightSource"}
	local localization = {
		["Sword"]        = "Swords",
		["Dagger"]       = "Daggers",
		["Mace"]         = "Maces",
		["Polearm"]      = "Polearms",
		["Axe"]          = "Axes",
		["Throwable"]    = "Throwables",
		["Bow"]          = "Bows",
		["Crossbow"]     = "Crossbows",
		["Firearm"]      = "Firearms",
		["MagicStuff"]   = "Magic Stuff",
		["Shield"]       = "Shields",
		["LightSource"]  = "Light Sources"
	}
	local separator = "]] • [["

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

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

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

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

function p.page(f)
	local page_name = f.args.name

	local wt = {}
	projectile(wt,page_name)
	damage_falloff(wt,page_name)
	hitbox(wt,page_name)
	combo(wt,page_name)
	block(wt,page_name)
	action_move_slow_penalty(wt,page_name)
	artifact(wt,page_name)
	return concat(wt)
end

return p


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


-- mw.log(p.page({args={name="Rapier"}}))