From Dark and Darker Wiki

(Testing minimal order lists again)
Tags: Undo Reverted
No edit summary
 
(9 intermediate revisions by the same user not shown)
Line 2: Line 2:
local WD = mw.loadJsonData("Data:Weapon.json") --Global var holding tables from Data:Weapon.json
local WD = mw.loadJsonData("Data:Weapon.json") --Global var holding tables from Data:Weapon.json
local concat = table.concat
local concat = table.concat
---Iterate keys in ascending order, with numbers going before strings.  Iterator returns: (key, value).
---@param dict table
---@return function
local function spairs(dict)
if not dict then return function() return nil,nil end end
local set = {}
for key,_ in pairs(dict) do
if key ~= "order" then
set[#set+1] = key
end
end
--Sort such that numbers and strings are in ascending order, with all numbers going before strings
--Slightly different from alphanumerical order in that numbers aren't compared as strings.
--For example, 24 doesn't go ahead of 3.
table.sort(set,function(a,b)
if type(a) ~= type(b) then
return tostring(a) < tostring(b)
else
return a < b
end
end)
local i = 0
return function()
i = i + 1
local key = set[i]
if key ~= nil then
return key, dict[key]
end
end
end




Line 106: 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 166: Line 131:


local wt= ""
local wt= ""
for ability_name,ability in spairs(weapon.abilities) do
for i,ability in ipairs(weapon.abilities.order) do
if ability_name ~= "Other" then
if ability ~= "Other" then
for attack_name,attack in spairs(ability) do
for j,attack in ipairs(weapon.abilities[ability].order) do
if attack.impactpower and attack.impactzones then
if weapon.abilities[ability][attack].impactpower and weapon.abilities[ability][attack].impactzones then
wt = wt.."<br><span style='color:#eee8'>"..ability_name.." "..attack_name.."</span><br>"
wt = wt.."<span style='color:#eee8'>"..ability.." "..attack.."</span><br>"
for i,v in ipairs(attack.impactzones) do
for k,impact_zone in ipairs(weapon.abilities[ability][attack].impactzones) do
if i~=1 then wt = wt.."/" end
if k~=1 then wt = wt.."/" end
wt = wt..v
wt = wt..impact_zone
end
end
wt = wt.." + "..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




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




--TODO not always the same for each attack
---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)
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 = ""
local i = 1
for i,ability_name in ipairs(order) do
for ability_name,ability in spairs(weapon.abilities) do
if i~=1 then wt = wt.."<br><br>" end
if i~=1 then wt = wt.."<br><br>" end
i = i+1


-- ability identifier
wt = wt.."<span style='color:#eee8; font-size:110%; font-weight:bold'>"
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
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>"
wt = wt.."</span><br>"


local movement_multiplier,prepare_movement_multiplier = collect_multipliers(ability)
local movement_multiplier,prepare_movement_multiplier = collect_multipliers(weapon.abilities[ability_name])
if #prepare_movement_multiplier > 3 then
if #prepare_movement_multiplier > 3 then
wt = wt.."<span style='color:#eee8'>Mid Attack:&nbsp;</span>x"..movement_multiplier
wt = wt.."<span style='color:#eee8'>Mid Attack:&nbsp;</span>x"..movement_multiplier
Line 310: Line 286:
local wt = "<span style='color:#eee8'>Hitslow</span><br>"
local wt = "<span style='color:#eee8'>Hitslow</span><br>"
local duration = " for "
local duration = " for "
local i = 1
for i,attack in ipairs(weapon.abilities[ability_type].order) do
for attack_name,attack in spairs(weapon.abilities[ability_type]) do
if i~=1 then wt = wt.."/" end
if i~=1 then wt = wt.."/" end
i = i+1
wt = wt..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global["Move Speed Add"]
wt = wt..(attack.effects.HitSlow.rarity.Global["Move Speed Add"] or attack.effects.HitSlow.rarity.Global["Move Speed Bonus"] or "")
or weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global["Move Speed Bonus"] or "")
end
end
i=1
for i,attack in ipairs(weapon.abilities[ability_type].order) do
for attack_name,attack in spairs(weapon.abilities[ability_type]) do
if i~=1 then duration = duration.."/" end
if i~=1 then duration = duration.."/" end
i = i+1
duration = duration..(weapon.abilities[ability_type][attack].effects.HitSlow.rarity.Global["Duration"] or "")
duration = duration..(attack.effects.HitSlow.rarity.Global["Duration"] or "")
end
end
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 343: 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 353: Line 323:
---Helps format a string containing either Combo Damage or damagetype values
---Helps format a string containing either Combo Damage or damagetype values
---@param weapon table
---@param weapon table
---@param attack string
---@param ability string
---@param value string
---@param value string
---@return string
---@return string
local function format_combo(weapon,attack,value)
local function format_combo(weapon,ability,value)
local wt = ""
local wt = ""
for _,data in spairs(weapon.abilities[attack]) do
for i,attack in ipairs(weapon.abilities[ability].order) do
if #wt ~= 0 then wt = wt.."/" end
if i~=1 then wt = wt.."/" end
wt = wt..(data[value] or "")
wt = wt..(weapon.abilities[ability][attack][value] or "")
end
end
return wt
return wt
Line 386: 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 392: 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},
["Bow or Crossbow"] = {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 402: 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") then key = "Bow or Crossbow" end
else
if weapon.types.Shield then key = "Shield" elseif weapon.types.Bow or weapon.types.Crossbow then key = "Bow or Crossbow" 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 435: 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"}}))