Path of Exile Wiki

Please consider helping keep the wiki up to date. Check the to-do list of updates needed for version 3.14.0.

Game data exports will becoming later as the technical changes in addition to regular changes take some more time.

READ MORE

Path of Exile Wiki
No edit summary
No edit summary
Line 466: Line 466:
 
:done()
 
:done()
 
:tag('td')
 
:tag('td')
:wikitext(args.reward)
+
:tag('div')
  +
:attr('class', 'itemboxstats')
  +
:wikitext(args.reward)
  +
:done()
 
:done()
 
:done()
 
:tag('td')
 
:tag('td')

Revision as of 17:48, 17 July 2015

Template info icon Module documentation[view] [edit] [history] [purge]

The item module is used for handling item data and displaying it in various ways including infoboxes, inline links, and tables.

Usage

Each item has its own page that stores stats and other data pertaining to that item. This is accomplished by invoking the item function from this module.

<onlyinclude>{{#invoke:item|item
|name = Item Name

... pass in other arguments ...

}}</onlyinclude>

This displays an infobox for the item and sets up the item data for inclusion on other pages. See Wurm's Molt for an example of this setup.

Parameters

All parameters except for Name are optional. They can be excluded or left blank.

Parameter Description
Name Name of the item
SortKey The sort key for sorting the item in categories and tables – Defaults to Name
PageName Name of the item's wiki page – Defaults to Name
Image File name of the item graphic – Defaults to PageName.png
Size Size of the item in grid units – Possible values are, but not limited to: 1x1, 2x2, and 2x4. Defaults to a "best guess" based on the item's type, subtype, and requirements.
Rarity Rarity of the item – This only applies to weapons, armour, accessories, flasks, and maps. Possible values are: Normal, Magic, Rare, and Unique. Defaults to Normal.
Type Type of the item, for formatting purposes – Possible values are: Weapon, Armour, Accessory, Flask, Map, Currency, Quest and Microtrans.
Subtype Subtype of the item – Possible values are, but not limited to: Axe, Staff, Gloves, Body Armour, and Ring.
BaseItem Base item page name, for rares and uniques
RequiredLevel Level requirement of the item
RequiredStrength Strength requirement of the item
RequiredDexterity Dexterity requirement of the item
RequiredIntelligence Intelligence requirement of the item
PhysicalDamage Physical damage of the weapon
FireDamage Fire damage of the weapon
ColdDamage Cold damage of the weapon
LightningDamage Lightning damage of the weapon
ChaosDamage Chaos damage of the weapon
AverageDamage Override for normal average damage calculation; used for weapons such as Dyadus
CriticalStrikeChance Critical strike chance of the weapon
AttacksPerSecond Attacks per second of the weapon
Armour Armour rating of the armour item
Evasion Evasion rating of the armour item
EnergyShield Energy shield of the armour item
Block Block chance of the shield
MapLevel Map level of the map item
MapTier Map tier of the map item
ItemQuantity Item quantity modifier of the map item
StackSize Maximum stack size of the currency item
Effect Effect of the usable utility flask, currency item, or microtransaction item
FlaskLife Life restoration on use of the flask
FlaskMana Mana restoration on use of the flask
FlaskDuration Life or mana restoration time or utility effect duration of the flask
FlaskCharges Total "charge" capacity of the flask
FlaskChargesUsed "Charges" consumed on use of the flask
ImplicitMods Implicit mods of the item – Separate each line with <br>.
RandomMods Random mods of the item – Separate each line with <br>.
Cosmetic Cosmetic mods of the item – Separate each line with <br>.
FlavourText Flavour text of the item
HelpText Help text of the item

Numerical ranges

There are two types of numerical ranges for items: damage ranges and random value ranges. See the discussion about distinguishing between ranges.

Damage ranges are two numbers separated by an en dash (&ndash;), indicating a range of possible values for damage calculation. These appear on the wiki very similar to how they appear in game.

  • Physical Damage: 15–23
  • Adds 48–72 Fire Damage

Random value ranges are two numbers or damage ranges separated by the word "to" and enclosed in parentheses, indicating a range of possible values for item stat rolls. These do not appear in game at all, but are shown on the wiki for informational purposes.

  • Evasion Rating: (80 to 91)
  • +(60 to 80) to maximum Life
  • (10 to 20)% increased Attack Speed
  • Adds (15–25 to 24–35) Chaos Damage

local p = {}
local h = {}
local views = {}
local getArgs, roundAndPad

---------------------------------------------------------------------
-- Implements {{item}}
---------------------------------------------------------------------
function p.item(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	local args = getArgs(frame, {
		parentFirst = true
	})
	args.view = args.view or args.View or 'full'
	if not views[args.view] then
		error('The requested view "' .. args.view .. '" does not exist')
	end
	args.format = args.format or args.Format
	args.name = args.name or args.Name
	args.sortKey = args.sortKey or args.SortKey or args.name
	args.pageName = args.pageName or args.PageName or args.name
	args.image = args.image or args.Image or args.pageName .. '.png'
	args.art = args.art or args.Art or args.pageName .. ' card art.png'
	args.size = args.size or args.Size or '2x_'
	args.rarity = args.rarity or args.Rarity or 'Normal'
	args.type = args.type or args.Type
	args.subtype = args.subtype or args.Subtype
	args.baseItem = args.baseItem or args.BaseItem
	args.baseItemPage = args.baseItemPage or args.BaseItemPage or args.baseItem
	args.level = args.level or args.RequiredLevel
	args.strength = args.strength or args.RequiredStrength
	args.dexterity = args.dexterity or args.RequiredDexterity
	args.intelligence = args.intelligence or args.RequiredIntelligence
	args.physical = args.physicalDamage or args.PhysicalDamage
	args.fire = args.fire or args.FireDamage
	args.cold = args.cold or args.ColdDamage
	args.lightning = args.lightning or args.LightningDamage
	args.chaos = args.chaos or args.ChaosDamage
	args.averageDamage = args.averageDamage or args.AverageDamage
	args.critChance = args.critChance or args.CriticalStrikeChance
	args.attacksPerSecond = args.attacksPerSecond or args.AttacksPerSecond
	args.blockChance = args.blockChance or args.Block
	args.armour = args.armour or args.Armour
	args.evasion = args.evasion or args.Evasion
	args.energyShield = args.energyShield or args.EnergyShield
	args.mapLevel = args.mapLevel or args.MapLevel
	args.itemQuantity = args.itemQuantity or args.itemQuantity
	args.radius = args.radius or args.Radius
	args.limit = args.limit or args.Limit
	args.stackSize = args.stackSize or args.StackSize
	args.effect = args.effect or args.Effect
	args.life = args.life or args.FlaskLife
	args.mana = args.mana or args.FlaskMana
	args.duration = args.duration or args.FlaskDuration
	args.chargeCap = args.chargeCap or args.FlaskCharges
	args.chargeUse = args.chargeUse or args.FlaskChargesUsed
	args.reward = args.reward or args.Reward
	args.drop = args.drop or args.Drop
	args.implicitMods = args.implicitMods or args.ImplicitMods
	args.randomMods = args.randomMods or args.RandomMods
	args.cosmeticMods = args.cosmeticMods or args.CosmeticMods
	args.helpText = args.helpText or args.HelpText
	args.flavourText = args.flavourText or args.FlavourText
	return views[args.view](args)
end

---------------------------------------------------------------------
-- Views
---------------------------------------------------------------------
function views.full(args)
	args.stats = h.statsBuilder(args)
	args.class = 'itembox-full'
	args.name = '[[' .. args.pageName .. '|' .. args.name .. ']]'
	args.baseItem = args.baseItem and ('[[' .. args.baseItemPage .. '|' .. args.baseItem .. ']]') or nil
	local types = {
		['Weapon'] = {
			['Bow'] = 'Bows',
			['Claw'] = 'Claws',
			['Dagger'] = 'Daggers',
			['Wand'] = 'Wands',
			['Fishing Rod'] = 'Fishing rods',
			['Staff'] = 'Staves',
			['One Handed Axe'] = 'One-handed axes',
			['Two Handed Axe'] = 'Two-handed axes',
			['One Handed Mace'] = 'One-handed maces',
			['Two Handed Mace'] = 'Two-handed maces',
			['One Handed Sword'] = 'One-handed swords',
			['Two Handed Sword'] = 'Two-handed swords'
		},
		['Armour'] = {
			['Body Armour'] = 'Body armours',
			['Helmet'] = 'Helmets',
			['Shield'] = 'Shields',
			['Boots'] = 'Boots',
			['Gloves'] = 'Gloves'
		},
		['Accessory'] = {
			['Amulet'] = 'Amulets',
			['Belt'] = 'Belts',
			['Quiver'] = 'Quivers',
			['Ring'] = 'Rings'
		},
		['Flask'] = {
			['Life'] = 'Life flasks',
			['Mana'] = 'Mana flasks',
			['Hybrid'] = 'Hybrid flasks',
			['Utility'] = 'Utility flasks'
		},
		['Map'] = {
			['Fragment'] = 'Map fragments',
			['_default'] = 'Maps'
		},
		['Jewel'] = 'Jewels',
		['Currency'] = 'Currency items',
		['Card'] = 'Divination cards',
		['Quest'] = 'Quest items',
		['Microtrans'] = 'Microtransaction features'
	}
	local category = types[args.type]
	if type(category) == 'table' then
		category = types[args.type][args.subtype or '_default']
	end
	if category then
		if args.rarity == 'Unique' then
			category = 'Unique ' .. string.lower(category)
		end
	else
		category = 'Items with invalid types'
	end
	if args.type == 'Card' then
		args.belowStats = '[[Category:' .. category .. ']]'
	else
		args.belowStats = mw.html.create('div')
			:attr('class', 'itemboximage')
			:wikitext( '[[File:' .. args.image .. '|' .. p._itemsize({args.size}) .. '|]]' )
			:wikitext( '[[Category:' .. category .. ']]' )
	end
	return tostring( p._itembox(args) )
end

function views.standard(args)
	args.stats = h.statsBuilder(args)
	args.name = '[[' .. args.pageName .. '|' .. args.name .. ']]'
	args.baseItem = args.baseItem and ('[[' .. args.baseItemPage .. '|' .. args.baseItem .. ']]') or nil
	return tostring( p._itembox(args) )
end

function views.inline(args)
	args.stats = h.statsBuilder(args)
	args.class = 'itemboxhover itemboxhoverhide itemboxhovernojs'
	args.isHover = true
	args.itemLinkText = args.itemLinkText or args.name
	args.itemLinkArt = args.itemLinkArt and string.gsub(args.image, '%.(%a+)$', ' ' .. args.itemLinkArt .. '.%1') or args.image
	local container = mw.html.create('span')
		:tag('span')
			:attr('class', 'itemhover')
			:wikitext(
				args.itemLinkLarge
					and '[[' .. args.pageName .. '|' .. args.itemLinkText .. ']] <br> [[File:' .. args.itemLinkArt .. '|' .. p._itemsize({args.size, '39'}) .. '|link=' .. args.pageName .. '|alt=]]'
					or '[[File:' .. args.itemLinkArt .. '|16x16px|link=|alt=]][[' .. args.pageName .. '|' .. args.itemLinkText .. ']]'
			)
			:done()
		:node( p._itembox(args) )
	if args.type ~= 'Card' then
		container
			:tag('span')
				:attr('class', 'itemboxhovericon itemboxhoverhide itemboxhovernojs')
				:wikitext( '[[File:' .. args.itemLinkArt .. '|' .. p._itemsize({args.size}) .. '|link=|alt=]]' )
				:done()
	end
	return tostring(container)
end

function views.tablerow(args)
	if not roundAndPad then
		roundAndPad = require('Module:Decimals')._main
	end
	local otherPage = mw.title.getCurrentTitle().fullText ~= args.pageName
	local flags = args.flags and mw.text.split(args.flags, ' ', true) or {}
	for i=1, #flags do
		flags[ flags[i] ] = true
	end
	local function newNA(val)
		local cell = mw.html.create('td')
			:attr('class', 'table-na')
			:wikitext('<small>N/A</small>')
		if val ~= nil then
			cell
				:attr('data-sort-value', val)
		end
		return cell
	end
	local row = mw.html.create('tr')
	if otherPage then
		row
			:attr('id', args.name)
	end
	if flags.name then
		row
			:tag('td')
				:attr('data-sort-value', args.sortKey)
				:wikitext( (otherPage and '[[' .. args.pageName .. '|' .. args.name .. ']]' or args.name) .. ' <br> [[File:' .. args.image .. '|' .. p._itemsize({args.size, '39'}) .. '|link=' .. (otherPage and args.pageName or '') .. '|alt=]]' )
				:done()
	end
	if flags.base then
		row
			:tag('td')
				:attr('data-sort-value', args.baseItemPage)
				:wikitext( '[[' .. args.baseItemPage .. '|' .. args.baseItem .. ']]' )
				:done()
	end
	if flags.level then
		if args.level then
			row
				:tag('td')
					:wikitext(args.level)
					:done()
		else
			row
				:node( newNA(0) )
		end
	end
	if args.type == 'Weapon' or args.type == 'Armour' then
		if flags.str then
			if args.strength then
				row
					:tag('td')
						:wikitext(args.strength)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
		if flags.dex then
			if args.dexterity then
				row
					:tag('td')
						:wikitext(args.dexterity)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
		if flags.int then
			if args.intelligence then
				row
					:tag('td')
						:wikitext(args.intelligence)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
	end
	if args.type == 'Weapon' then
		if flags.weapon then
			local avgDmg = tonumber(args.averageDamage) or ( (args.physical and p._itemRangeAverage({args.physical}) or 0) + (args.fire and p._itemRangeAverage({args.fire}) or 0) + (args.cold and p._itemRangeAverage({args.cold}) or 0) + (args.lightning and p._itemRangeAverage({args.lightning}) or 0) + (args.chaos and p._itemRangeAverage({args.chaos}) or 0) )
			local avgCrt = args.critChance and p._itemRangeAverage({args.critChance}) or 0
			local avgAps = args.attacksPerSecond and p._itemRangeAverage({args.attacksPerSecond}) or 0
			local damage = mw.html.create('td')
				:attr('data-sort-value', avgDmg)
			if avgDmg == 0 then
				damage
					:wikitext('0')
			else
				if args.physical then
					damage
						:node( h.newColor('physical', args.physical) )
						:tag('br'):done()
				end
				if args.fire then
					damage
						:node( h.newColor('fire', args.fire) )
						:tag('br'):done()
				end
				if args.cold then
					damage
						:node( h.newColor('cold', args.cold) )
						:tag('br'):done()
				end
				if args.lightning then
					damage
						:node( h.newColor('lightning', args.lightning) )
						:tag('br'):done()
				end
				if args.chaos then
					damage
						:node( h.newColor('chaos', args.chaos) )
				end
			end
			row
				:node(damage)
				:tag('td')
					:attr('data-sort-value', avgCrt)
					:wikitext(args.critChance)
					:done()
				:tag('td')
					:attr('data-sort-value', avgAps)
					:wikitext(args.attacksPerSecond)
					:done()
				:tag('td')
					:wikitext( roundAndPad( avgDmg * avgAps * (1 - avgCrt / 100) + avgDmg * avgAps * avgCrt / 100 * 1.5, 2 ) )
					:done()
		end
	elseif args.type == 'Armour' then
		if flags.block then
			if args.blockChance then
				row
					:tag('td')
						:attr( 'data-sort-value', p._itemRangeAverage({args.blockChance}) )
						:wikitext(args.blockChance)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
		if flags.armour then
			if args.armour then
				row
					:tag('td')
						:attr( 'data-sort-value', p._itemRangeAverage({args.armour}) )
						:wikitext(args.armour)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
		if flags.evasion then
			if args.evasion then
				row
					:tag('td')
						:attr( 'data-sort-value', p._itemRangeAverage({args.evasion}) )
						:wikitext(args.evasion)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
		if flags.energyshield then
			if args.energyShield then
				row
					:tag('td')
						:attr( 'data-sort-value', p._itemRangeAverage({args.energyShield}) )
						:wikitext(args.energyShield)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
	elseif args.type == 'Flask' then
		if flags.life then
			if args.life then
				row
					:tag('td')
						:attr( 'data-sort-value', p._itemRangeAverage({args.life}) )
						:wikitext(args.life)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
		if flags.mana then
			if args.mana then
				row
					:tag('td')
						:attr( 'data-sort-value', p._itemRangeAverage({args.mana}) )
						:wikitext(args.mana)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
		if flags.effect then
			if args.effect then
				row
					:tag('td')
						:attr('class', 'text-mod')
						:node(args.effect)
						:done()
			else
				row
					:node( newNA() )
			end
		end
		row
			:tag('td')
				:attr( 'data-sort-value', p._itemRangeAverage({args.duration}) )
				:wikitext(args.duration)
				:done()
			:tag('td')
				:attr( 'data-sort-value', p._itemRangeAverage({args.chargeCap}) )
				:wikitext(args.chargeCap)
				:done()
			:tag('td')
				:attr( 'data-sort-value', p._itemRangeAverage({args.chargeUse}) )
				:wikitext(args.chargeUse)
				:done()
	elseif args.type == 'Map' then
		row
			:tag('td')
				:wikitext(args.mapLevel)
				:done()
		if flags.qty then
			if args.itemQuantity then
				row
					:tag('td')
						:attr( 'data-sort-value', p._itemRangeAverage({args.itemQuantity}) )
						:wikitext(args.itemQuantity)
						:done()
			else
				row
					:node( newNA(0) )
			end
		end
	elseif args.type == 'Jewel' then
		if args.limit then
			row
				:tag('td')
					:wikitext(args.limit)
					:done()
		else
			row
				:node( newNA(999) )
		end
		if args.radius then
			row
				:tag('td')
					:wikitext(args.radius)
					:done()
		else
			row
				:node( newNA('zzz') )
		end
	elseif args.type == 'Currency' then
		row
			:tag('td')
				:wikitext(args.stackSize)
				:done()
		if flags.effect then
			if args.effect then
				row
					:tag('td')
						:attr('class', 'text-mod')
						:wikitext(args.effect)
						:done()
			else
				row
					:node( newNA() )
			end
		end
	elseif args.type == 'Card' then
		row
			:tag('td')
				:wikitext(args.stackSize)
				:done()
			:tag('td')
				:tag('div')
					:attr('class', 'itemboxstats')
					:wikitext(args.reward)
					:done()
				:done()
			:tag('td')
				:wikitext(args.drop)
				:done()
	end
	if flags.mods then
		if args.implicitMods or args.randomMods or args.cosmeticMods then
			local stats = mw.html.create('div')
				:attr('class', 'itemboxstats')
			if args.implicitMods then
				stats
					:node(
						h.newGroup('text-mod')
							:wikitext(args.implicitMods)
					)
			end
			if args.randomMods then
				stats
					:node(
						h.newGroup('text-mod')
							:wikitext(args.randomMods)
					)
			end
			if args.cosmeticMods then
				stats
					:node(
						h.newGroup('text-mod')
							:wikitext(args.cosmeticMods)
					)
			end
			row
				:tag('td')
					:node(stats)
					:done()
		else
			row
				:node( newNA() )
		end
	end
	if flags.flavour then
		if args.flavourText then
			row
				:tag('td')
					:attr('class', 'text-flavour')
					:wikitext(args.flavourText)
					:done()
		else
			row
				:node( newNA() )
		end
	end
	if flags.help then
		if args.helpText then
			row
				:tag('td')
					:attr('class', 'text-help')
					:wikitext(args.helpText)
					:done()
		else
			row
				:node( newNA() )
		end
	end
	return row
end

---------------------------------------------------------------------
-- Implements {{itembox}}
---------------------------------------------------------------------
function p.itembox(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	local args = getArgs(frame, {
		wrappers = 'Template:Itembox'
	})
	return p._itembox(args)
end
function p._itembox(args)
	local frames = {
		['Currency'] = 'currency',
		['Microtrans'] = 'currency',
		['Gem'] = 'gem',
		['Quest'] = 'quest',
		['Card'] = 'divicard'
	}
	local container = mw.html.create(args.isHover and 'span' or 'div')
		:attr( 'class', 'itembox-' .. ( string.lower(args.frame or frames[args.type] or args.rarity or 'normal') ) .. ' ' .. (args.class or '') )
	if args.type == 'Card' then
		container
			:tag('span')
				:attr( 'class', 'divicard-wrapper')
				:tag('span')
					:attr('class', 'divicard-art')
					:wikitext( '[[File:' .. args.art .. '|link=|alt=]]' )
					:done()
				:tag('span')
					:attr('class', 'divicard-frame')
					:wikitext( '[[File:Divination card frame.png|link=|alt=]]' )
					:done()
				:tag('span')
					:attr('class', 'divicard-header')
					:wikitext(args.name)
					:done()
				:tag('span')
					:attr('class', 'divicard-stack')
					:wikitext(args.stackSize)
					:done()
				:tag('span')
					:attr('class', 'divicard-reward')
					:tag('span')
						:wikitext(args.reward)
						:done()
					:done()
				:tag('span')
					:attr('class', 'divicard-flavour text-flavour')
					:tag('span')
						:wikitext(args.flavourText)
						:done()
					:done()
				:wikitext(args.belowStats)
				:done()
	else
		container
			:tag('span')
				:attr( 'class', 'itemboxheader-' .. (args.baseItem and 'double' or 'single') )
				:tag('span')
					:attr('class', 'itemboxheaderleft')
					:done()
				:tag('span')
					:attr('class', 'itemboxheaderright')
					:done()
				:tag('span')
					:attr('class', 'itemboxheadertext')
					:wikitext( args.name .. (args.baseItem and '<br>' .. args.baseItem or '') )
					:done()
				:done()
		if type(args.aboveStats) == 'table' then
			container:node(args.aboveStats)
		elseif type(args.aboveStats) == 'string' then
			container:wikitext(args.aboveStats)
		end
		local stats = mw.html.create('span')
			:attr('class', 'itemboxstats')
		if type(args.stats) == 'table' then
			stats:node(args.stats)
		elseif type(args.stats) == 'string' then
			stats:wikitext(args.stats)
		end
		container:node(stats)
		if type(args.belowStats) == 'table' then
			container:node(args.belowStats)
		elseif type(args.belowStats) == 'string' then
			container:wikitext(args.belowStats)
		end
	end
	return container
end

---------------------------------------------------------------------
-- Implements {{il}}
---------------------------------------------------------------------
function p.itemLink(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	local args = getArgs(frame, {
		parentOnly = true
	})
	local save = 'itemlink__' .. string.gsub(args[1], '%W+', '') .. '_' .. (args.art or args.image or '1') .. (args.large and '_large' or '')
	if frame:callParserFunction('#varexists', {save}) == '1' then
		return frame:callParserFunction('#var', {save})
	end
	return frame:callParserFunction('#vardefineecho', save, frame:expandTemplate{
		title = ':' .. args[1],
		args = {
			view = 'inline',
			itemLinkText = args[2],
			itemLinkArt = args.art or args.image,
			itemLinkLarge = args.large
		}
	})
end

---------------------------------------------------------------------
-- Implements {{item table}}
---------------------------------------------------------------------
function p.itemTable(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	local args = getArgs(frame, {
		parentOnly = true
	})
	args.flags = args.flags or args.format
	local flags = args.flags and mw.text.split(args.flags, ' ', true) or {}
	for i=1, #flags do
		flags[ flags[i] ] = true
	end
	local function newAbbr(text, title)
		return mw.html.create('abbr')
			:attr('class', 'nounderline')
			:attr('title', title)
			:wikitext(text)
	end
	local tbl = mw.html.create('table')
		:attr('class', 'wikitable sortable')
		:attr('style', 'text-align:center; ' .. ( args.width and 'width:' .. args.width or '' ) )
	local hdr = mw.html.create('tr')
		:tag('th')
			:wikitext('Name')
			:done()
	if flags.base then
		hdr
			:tag('th')
				:wikitext('Base Item')
				:done()
	end
	if flags.level then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:node( newAbbr('[[File:Level_up_icon_small.png|link=|Lvl.]]', 'Required Level') )
				:done()
	end
	if flags.str then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:node( newAbbr('[[File:StrengthIcon_small.png|link=|Str.]]', 'Required Strength') )
				:done()
	end
	if flags.dex then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:node( newAbbr('[[File:DexterityIcon_small.png|link=|Dex.]]', 'Required Dexterity') )
				:done()
	end
	if flags.int then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:node( newAbbr('[[File:IntelligenceIcon_small.png|link=|Int.]]', 'Required Intelligence') )
				:done()
	end
	if flags.weapon then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Damage')
				:done()
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Critical Strike Chance')
				:done()
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Attacks per Second')
				:done()
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Damage per Second')
				:done()
	end
	if flags.block then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Chance to Block')
				:done()
	end
	if flags.armour then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Armour Rating')
				:done()
	end
	if flags.evasion then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Evasion Rating')
				:done()
	end
	if flags.energyshield then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Energy Shield')
				:done()
	end
	if flags.flask then
		if flags.life or flags.mana then
			if flags.life then
				hdr
					:tag('th')
						:attr('data-sort-type', 'number')
						:node( newAbbr('Life', 'Life Recovered over Time per Sip') )
						:done()
			end
			if flags.mana then
				hdr
					:tag('th')
						:attr('data-sort-type', 'number')
						:node( newAbbr('Mana', 'Mana Recovered over Time per Sip') )
						:done()
			end
			hdr
				:tag('th')
					:attr('data-sort-type', 'number')
					:node( newAbbr(
						'Time', 
						(flags.life and 'Life' or '') .. ( (flags.life and flags.mana) and ' and ' or '' ) .. (flags.mana and 'Mana' or '') .. ' Recovery Time in Seconds'
					) )
					:done()
		end
		if flags.effect then
			hdr
				:tag('th')
					:attr('class', 'unsortable')
					:wikitext('Effects')
					:done()
				:tag('th')
					:attr('data-sort-type', 'number')
					:wikitext('Duration')
					:done()
		end
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:node( newAbbr('Capacity', 'Total Amount of Charges the Flask Can Hold') )
				:done()
			:tag('th')
				:attr('data-sort-type', 'number')
				:node( newAbbr('Usage', 'Amount of Charges Used per Sip') )
				:done()
	end
	if flags.map then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Map Level')
				:done()
		if flags.qty then
			hdr
				:tag('th')
					:attr('data-sort-type', 'number')
					:wikitext('Item Quantity')
					:done()
		end
	end
	if flags.jewel then
		hdr
			:tag('th')
				:wikitext('Limit')
				:done()
		hdr
			:tag('th')
				:wikitext('Radius')
				:done()
	end
	if flags.currency then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:wikitext('Stack Size')
				:done()
		if flags.effect then
			hdr
				:tag('th')
					:attr('class', 'unsortable')
					:wikitext('Effects')
					:done()
		end
	end
	if flags.card then
		hdr
			:tag('th')
				:attr('data-sort-type', 'number')
				:node( newAbbr('Set', 'Amount needed to complete a set') )
				:done()
			:tag('th')
				:attr('class', 'unsortable')
				:wikitext('Reward')
				:done()
			:tag('th')
				:attr('class', 'unsortable')
				:wikitext('Drop Location(s)')
				:done()
	end
	if flags.mods then
		hdr
			:tag('th')
				:attr('class', 'unsortable')
				:wikitext('Modifiers')
				:done()
	end
	if flags.flavour then
		hdr
			:tag('th')
				:attr('class', 'unsortable')
				:wikitext('Flavour Text')
				:done()
	end
	if flags.help then
		hdr
			:tag('th')
				:attr('class', 'unsortable')
				:wikitext('Help Text')
				:done()
	end
	tbl:node(hdr)
	local i = 1
	while args[i] do
		tbl
			:node(
				frame:expandTemplate{
					title = ':' .. args[i],
					args = {
						view = 'tablerow',
						flags = 'name ' .. args.flags
					}
				}
			)
		i = i + 1
	end
	return tostring(tbl)
end

---------------------------------------------------------------------
-- Implements {{item range average}}
---------------------------------------------------------------------
function p.itemRangeAverage(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	local args = getArgs(frame, {
		wrappers = 'Template:Item range average'
	})
	return p._itemRangeAverage(args)
end
function p._itemRangeAverage(args)
	if args[1] == nil then
		return nil
	end
	local s = {}
	s.mod = string.match(args[1], '>([^><]+)</') or args[1]
	s.sign = string.find(s.mod, '&minus;', 0, true) and -1 or 1
	s.min = string.match(s.mod, '%((%S+)%s*to') or s.mod
	s.max = string.match(s.mod, 'to%s*(%S+)%)') or s.min
	s.minLow = tonumber( string.match(s.min, '(%d*%.?%d+)') )
	s.minHigh = tonumber( string.match(s.min, '&ndash;(%d*%.?%d+)') or s.minLow )
	s.maxLow = tonumber( string.match(s.max, '(%d*%.?%d+)') )
	s.maxHigh = tonumber( string.match(s.max, '&ndash;(%d*%.?%d+)') or s.maxLow )
	if #{s.minLow, s.minHigh, s.maxLow, s.maxHigh} == 0 then
		return nil
	end
	return ( ( (s.minLow + s.minHigh) / 2 ) + ( (s.maxLow + s.maxHigh) / 2 ) ) / 2 * s.sign
end

---------------------------------------------------------------------
-- Implements {{itemsize}}
---------------------------------------------------------------------
function p.itemsize(frame)
	if not getArgs then
		getArgs = require('Module:Arguments').getArgs
	end
	local args = getArgs(frame, {
		wrappers = 'Template:Itemsize'
	})
	return p._itemsize(args)
end
function p._itemsize(args)
	local size = args[1] or '1x_'
	local grid = args[2] or 78
	local dim = mw.text.split(size, 'x', true)
	if dim[1] == '_' then
		if dim[2] == '_' then
			dim[1] = grid
		else
			dim[1] = ''
		end
	else
		dim[1] = dim[1] * grid
	end
	if dim[2] == '_' then
		dim[2] = ''
	else
		dim[2] = 'x' .. dim[2] * grid
	end
	return dim[1] .. dim[2] .. 'px'
end

---------------------------------------------------------------------
-- Helper functions
---------------------------------------------------------------------
function h.statsBuilder(args)
	local container = mw.html.create('span')
	local group
	if args.type == 'Weapon' then
		group = h.newGroup()
			:wikitext(args.subtype)
			:tag('br'):done()
		if args.physical then
			group
				:wikitext('Physical Damage: ')
				:node( h.newColor('value', args.physical) )
				:tag('br'):done()
		end
		if args.fire or args.cold or args.lightning then
			local elementalDamage = {}
			if args.fire then
				table.insert( elementalDamage, tostring( h.newColor('fire', args.fire) ) )
			end
			if args.cold then
				table.insert( elementalDamage, tostring( h.newColor('cold', args.cold) ) )
			end
			if args.lightning then
				table.insert( elementalDamage, tostring( h.newColor('lightning', args.lightning) ) )
			end
			group
				:wikitext('Elemental Damage: ')
				:wikitext( table.concat(elementalDamage, ', ') )
				:tag('br'):done()
		end
		if args.chaos then
			group
				:wikitext('Chaos Damage: ')
				:node( h.newColor('chaos', args.chaos) )
				:tag('br'):done()
		end
		group
			:wikitext('Critical Strike Chance: ')
			:node( h.newColor('value', args.critChance) )
			:tag('br'):done()
			:wikitext('Attacks per Second: ')
			:node( h.newColor('value', args.attacksPerSecond) )
		container:node(group)
	elseif args.type == 'Armour' then
		if args.blockChance or args.armour or args.evasion or args.energyShield then
			group = h.newGroup()
			if args.blockChance then
				group
					:wikitext('Chance to Block: ')
					:node( h.newColor('value', args.blockChance) )
					:tag('br'):done()
			end
			if args.armour then
				group
					:wikitext('Armour: ')
					:node( h.newColor('value', args.armour) )
					:tag('br'):done()
			end
			if args.evasion then
				group
					:wikitext('Evasion: ')
					:node( h.newColor('value', args.evasion) )
					:tag('br'):done()
			end
			if args.energyShield then
				group
					:wikitext('Energy Shield: ')
					:node( h.newColor('value', args.energyShield) )
			end
			container:node(group)
		end
	elseif args.type == 'Map' then
		if args.mapLevel then
			group = h.newGroup()
				:wikitext('Map Level: ')
				:node( h.newColor('value', args.mapLevel) )
				:tag('br'):done()
			if args.itemQuantity then
				group
					:wikitext('Item Quantity: ')
					:node( h.newColor('value', args.itemQuantity) )
			end
			container:node(group)
		end
	elseif args.type == 'Jewel' then
		if args.limit or args.radius then
			group = h.newGroup()
			if args.limit then
				group
					:wikitext('Limited to: ')
					:node( h.newColor('value', args.limit) )
			end
			if args.radius then
				group
					:wikitext('Radius: ')
					:node( h.newColor('value', args.radius) )
			end
			container:node(group)
		end
	elseif args.type == 'Currency' then
		group = h.newGroup()
			:wikitext('Stack Size: ')
			:node( h.newColor('value', args.stackSize) )
		container:node(group)
		if args.effect then
			group = h.newGroup('textwrap text-mod')
				:wikitext(args.effect)
			container:node(group)
		end
	elseif args.type == 'Flask' then
		group = h.newGroup()
		if args.life then
			group
				:wikitext('Recovers ')
				:node( h.newColor('value', args.life) )
				:wikitext(' Life over ')
				:node( h.newColor('value', args.duration) )
				:wikitext(' Seconds')
				:tag('br'):done()
		end
		if args.mana then
			group
				:wikitext('Recovers ')
				:node( h.newColor('value', args.mana) )
				:wikitext(' Mana over ')
				:node( h.newColor('value', args.duration) )
				:wikitext(' Seconds')
				:tag('br'):done()
		end
		if args.effect then
			group
				:wikitext('Lasts ')
				:node( h.newColor('value', args.duration) )
				:wikitext(' Seconds')
				:tag('br'):done()
		end
		group
			:wikitext('Consumes ')
			:node( h.newColor('value', args.chargeUse) )
			:wikitext(' of ')
			:node( h.newColor('value', args.chargeCap) )
			:wikitext(' Charges on use')
			:tag('br'):done()
		if args.effect then
			group
				:wikitext(args.effect)
		end
		container:node(group)
	elseif args.type == 'Microtrans' then
		group = h.newGroup()
		if args.subtype or args.stackSize then
			group
				:wikitext(args.subtype)
				:tag('br'):done()
			if args.stackSize then
				group
					:wikitext('Stack Size: ')
					:node( h.newColor('value', args.stackSize) )
			end
			container:node(group)
		end
		group = h.newGroup('textwrap text-mod')
			:wikitext(args.effect)
		container:node(group)
	end
	if args.level or args.strength or args.dexterity or args.intelligence then
		local requirements = {}
		local attrLabel
		if args.level then
			table.insert( requirements, 'Level ' .. tostring( h.newColor('value', args.level) ) )
		end
		if args.strength then
			attrLabel = ' Strength'
			if args.level or args.dexterity or args.intelligence then
				attrLabel = ' Str'
			end
			table.insert( requirements, tostring( h.newColor('value', args.strength) ) .. attrLabel )
		end
		if args.dexterity then
			attrLabel = ' Dexterity'
			if args.level or args.strength or args.intelligence then
				attrLabel = ' Dex'
			end
			table.insert( requirements, tostring( h.newColor('value', args.dexterity) ) .. attrLabel )
		end
		if args.intelligence then
			attrLabel = ' Intelligence'
			if args.level or args.strength or args.dexterity then
				attrLabel = ' Int'
			end
			table.insert( requirements, tostring( h.newColor('value', args.intelligence) ) .. attrLabel )
		end
		group = h.newGroup()
			:wikitext('Requires ')
			:wikitext( table.concat(requirements, ', ') )
		container:node(group)
	end
	if args.implicitMods then
		group = h.newGroup('text-mod')
			:wikitext(args.implicitMods)
		container:node(group)
	end
	if args.randomMods then
		group = h.newGroup('text-mod')
			:wikitext(args.randomMods)
		container:node(group)
	end
	if args.cosmeticMods then
		group = h.newGroup('text-cosmetic')
			:wikitext(args.cosmeticMods)
		container:node(group)
	end
	if args.flavourText then
		group = h.newGroup('textwrap text-flavour')
			:wikitext(args.flavourText)
		container:node(group)
	end
	if args.helpText then
		group = h.newGroup('textwrap text-help')
			:wikitext(args.helpText)
		container:node(group)
	end
	return container
end

function h.newColor(label, text)
	if text == nil or text == '' then
		return nil
	end
	return mw.html.create('span')
		:attr('class', 'text-' .. label)
		:wikitext(text)
end

function h.newGroup(class)
	return mw.html.create('span')
		:attr( 'class', 'itemboxstatsgroup ' .. (class or '') )
end

return p