Module:Sandbox

local p = {};

local function materialPlus( n ) return { Wooden = n + 0, Stone = n + 1, Iron = n + 2, Diamond = n + 3, Golden = n + 0, Netherite = n + 4 }; end

-- This is the value of the generic.attackSpeed attribute modifier. -- Translate from the value shown in the tooltip by subtracting 4. local attackSpeed = { Sword = -2.4, Trident = -2.9, Shovel = -3.0, Pickaxe = -2.8, Axe = { Wooden = -3.2, Stone = -3.2, Iron = -3.1, Diamond = -3.0, Golden = -3.0, Netherite = -3.0 },   Hoe = { Wooden = -3, Stone = -2, Iron = -1, Diamond = 0, Golden = -3, Netherite = 0 },   Other = 0, };

-- This is the value of the generic.attackDamage attribute modifier. -- Translate from the value shown in the tooltip by subtracting 1. local attackDamage = { Sword = materialPlus( 3 ), Trident = 8, Shovel = materialPlus( 1.5 ), Pickaxe = materialPlus( 1.0 ), Axe = { Wooden = 6, Stone = 8, Iron = 8, Diamond = 8, Golden = 6, Netherite = 9 },   Hoe = 0, Other = 0, };

local allTools = 'Sword;Trident;Shovel;Pickaxe;Axe;Hoe;Other'; local allMaterials = { 'Wooden', 'Stone', 'Iron', 'Diamond', 'Golden', 'Netherite' }; local numberMaterials = #allMaterials local dpsNote = ' ';

local function parseInfoArg( what, arg ) local ret = arg;

if type( ret ) == 'string' then local vals = mw.text.split( ret, '%s*,%s*' ); if #vals == 1 then ret = tonumber( vals[1] ); if not ret then error( 'Value for ' .. what .. ' must be either a single number or five numbers separated by commas', 3 ); end elseif #vals == 5 then ret = {} for i, v in ipairs( vals ) do               v = tonumber( v ); if not v then error( 'Value for ' .. what .. ' must be either a single number or five numbers separated by commas', 3 ); end ret[allMaterials[i]] = v;           end else error( 'Value for ' .. what .. ' must be either a single number or five numbers separated by commas', 3 ); end end

if type( ret ) == 'number' then ret = { same = ret } for _, material in ipairs( allMaterials ) do           ret[material] = ret.same; end end

return ret; end

function p.infoTableRow( frame ) local r = {}; local args = frame.args or frame; local tool = args.tool or error( 'Tool must be specified', 2 ); local speed = attackSpeed[tool] or attackSpeed.Other;

if args.showname then if tool == 'Other' then table.insert( r, '! Other' ); else table.insert( r, '! ' .. tool .. '' ); end end

local speed = parseInfoArg( 'speed', args.speed or attackSpeed[tool] or attackSpeed.Other ); if speed.same then table.insert( r, '|colspan=' .. numberMaterials .. '| ' .. speed.same + 4 ); else for _, material in ipairs( allMaterials ) do           table.insert( r, '|  ' .. speed[material] + 4 ); end end

local damage = parseInfoArg( 'damage', args.damage or attackDamage[tool] or attackDamage.Other ); if damage.same then table.insert( r, '|colspan=' .. numberMaterials .. '| ' ); else for _, material in ipairs( allMaterials ) do           table.insert( r, '|  ' ); end end

if speed.same and damage.same then local c = '|colspan=' .. numberMaterials .. '| ' .. ( damage.same + 1 ) * ( speed.same + 4 ); if speed.same > -2 then local d = ( damage.same + 1 ) * 2; c = c .. string.format( dpsNote, d, d ); end table.insert( r, c ); else for _, material in ipairs( allMaterials ) do           local c = '|  ' .. ( damage[material] + 1 ) * ( speed[material] + 4 ); if speed[material] > -2 then local d = ( damage[material] + 1 ) * 2; c = c .. string.format( dpsNote, d, d ); end table.insert( r, c ); end end

r = table.concat( r, '\n' ); if type( frame.preprocess ) == 'function' then r = frame:preprocess( r ); end return r; end

local function makeInfoTableHeader( t, showname ) table.insert( t, '{| class="wikitable" style="text-align:center"' ); table.insert( t, '|-' ); if showname then table.insert( t, '! Tool ' ); end table.insert( t, '!colspan=' .. numberMaterials .. '| Attack speed !!colspan=' .. numberMaterials   	.. '| Attack damage !!colspan=' .. numberMaterials .. '| Damage/Second (DPS)' ); end

function p.infoTableHeader( frame ) local t = {}; local showname = frame.args.showname or false

makeInfoTableHeader( t, showname );

return frame:preprocess( table.concat( t, '\n' ) ); end

function p.infoTable( frame ) local t = {}; local tools = frame.args.tools or allTools; local showname = frame.args.showname or string.find( tools, ';', 0, true ) makeInfoTableHeader( t, showname );

for tool in mw.text.gsplit( tools, '%s*;%s*' ) do       table.insert( t, '|-' ); table.insert( t, p.infoTableRow{ tool = tool, showname = showname } ); end

table.insert( t, '|}' );

return frame:preprocess( table.concat( t, '\n' ) ); end

function p.damageReductionTable( frame ) local formula; if frame.args.enchantment then formula = function ( t, speed ) return ( ( t + 0.5 ) * ( 4 + speed ) / 20 ); end; else formula = function ( t, speed ) return 0.2 + ( ( t + 0.5 ) * ( 4 + speed ) / 20 ) ^ 2 * 0.8; end; end

local tools = frame.args.tools or allTools; local minSpeed = 0; local rows = {}; for tool in mw.text.gsplit( tools, '%s*;%s*' ) do       local speeds = attackSpeed[tool] or attackSpeed.Other; if type( speeds ) == 'table' then local materials = {}; local rowSpeeds = {}; for _, material in ipairs( allMaterials ) do               local speed = speeds[material]; if not materials[speed] then materials[speed] = ''; table.insert( rowSpeeds, speed ); minSpeed = math.min( minSpeed, speed ); end materials[speed] = materials[speed] .. '';           end table.insert( rows, { tool, materials, rowSpeeds } ); else table.insert( rows, { tool, nil, { speeds } } ); minSpeed = math.min( minSpeed, speeds ); end end

local maxTicks = 0; if frame.args.maxTicks then maxTicks = tonumber( frame.args.maxTicks ) or error( 'Value for maxTicks must be a number', 2 ) maxTicks = math.ceil( maxTicks ); else maxTicks = 20 / ( 4 + minSpeed ); while formula( maxTicks, minSpeed ) < 0.99995 do           maxTicks = maxTicks + 1; end end

local t = {};

table.insert( t, '{| class="wikitable" style="text-align:center"' ); table.insert( t, '|-' ); table.insert( t, '!rowspan=2 colspan=2| Item' ); table.insert( t, '!colspan=' .. maxTicks + 1 .. '| Time (seconds)' ); table.insert( t, '|-' ); for i = 0, maxTicks do       table.insert( t, string.format('! %.2f', i / 20 ) ); end

for _, row in ipairs( rows ) do       local tool, materials, speeds = unpack( row ); for _, speed in ipairs( speeds ) do           table.insert( t, '|-' ); if tool then if tool ~= 'Other' then tool =  .. tool .. ; end if materials then table.insert( t, '|rowspan=' .. #speeds .. '| ' .. tool ); else table.insert( t, '|colspan=2| ' .. tool ); end tool = nil; end if materials then table.insert( t, '|style="white-space:nowrap"| ' .. materials[speed] ); end for i = 0, maxTicks do               local v = formula( i, speed ); if v >= 0.99995 then table.insert( t, string.format('|colspan=%d style="text-align:left"| 100%%', maxTicks - i + 1 ) ); break; else table.insert( t, string.format('| %.2f%%', v * 100 ) ); end end end end

table.insert( t, '|}' );

return frame:preprocess( table.concat( t, '\n' ) ); end

return p;