From Dark and Darker Wiki

(Added check for primary and secondary attack values.)
(Fixed bug in page blocking section. The existence check wasn't catching all cases.)
 
(One intermediate revision by the same user not shown)
Line 481: Line 481:


if weapon.abilities.Primary or weapon.abilities.Secondary or weapon.abilities.Special then
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'>"
wt[#wt+1] = "<br><b>Attack Times</b><br><div style='display:inline-block;margin-left:20px'>"
for _,name in ipairs(weapon.abilities.order) do
for _,name in ipairs(weapon.abilities.order) do
animation_format(wt,weapon,name)
animation_format(wt,weapon,name)
Line 493: Line 493:
local function p_block(wt,weapon_name)
local function p_block(wt,weapon_name)
local weapon = WD.Weapon[weapon_name]
local weapon = WD.Weapon[weapon_name]
if not weapon.impactresistance then return end
if not weapon.impactresistance or 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)
wt[#wt+1] = string.format("<h2>Block</h2>%s has a block ability with an impact resistance of <b>%s</b>.<br>To learn more about impact resistance see [[Impact Power]].", weapon.name, weapon.impactresistance)
end
end



Latest revision as of 21:32, 15 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 p_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 p_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 p_hitbox(wt,weapon_name)
	local weapon = WD.Weapon[weapon_name]
	if not weapon.hitbox or 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

	local primary_attack = weapon.abilities.Primary and weapon.abilities.Primary["Attack 1"] or {}
	local secondary_attack = weapon.abilities.Secondary and weapon.abilities.Secondary["Attack 1"] or {}
	if #primary_attack == 0 and #secondary_attack == 0 then return end

	-- If is not ranged, and has primary or secondary attacks
	if not (weapon.types["Bow"] or weapon.types["Crossow"] or weapon.types["ThrowableStuff"])
			and (primary_attack.impactzones or secondary_attack.impactzones) then
		local impact_zones = primary_attack.impactzones or secondary_attack.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 primary_attack.impactpower or secondary_attack.impactpower then
		wt[#wt+1] = "<br>'''Impact Power''' of "
		wt[#wt+1] = weapon.name
		wt[#wt+1] = ": "
		wt[#wt+1] = tostring(primary_attack.impactpower or secondary_attack.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, ability_name in ipairs(weapon.abilities[ability].order) do
		wt[#wt+1] = ability.." Combo: "..ability_name.."<br><div style='display:inline-block;margin-left:20px'>"
		attack_animation_times(wt,weapon,ability,ability_name)
		wt[#wt+1] = "</div><br><br>"
	end
end

local function p_combo(wt, page_name)
	local weapon = WD.Weapon[page_name]
	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><b>Attack Times</b><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 p_block(wt,weapon_name)
	local weapon = WD.Weapon[weapon_name]
	if not weapon.impactresistance or weapon.impactresistance == "" then return end

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

local function p_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 p_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 = {}
	p_projectile(wt,page_name)
	p_damage_falloff(wt,page_name)
	p_hitbox(wt,page_name)
	p_combo(wt,page_name)
	p_block(wt,page_name)
	p_action_move_slow_penalty(wt,page_name)
	p_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"}}))