Module:Sandbox/User:Artoire/1: Difference between revisions

From Brighter Shores Wiki
Jump to navigation Jump to search
Content added Content deleted
mNo edit summary
mNo edit summary
 
(14 intermediate revisions by the same user not shown)
Line 1: Line 1:
--[[
mw.log(p._main{
current_xp = 24,
target_xp = 37,
ingot = 'Coarse Deathstone (Etched)',
profession = 'Stonemason', -- 'Bonewright'/'Stonemason'/'Blacksmith'
ore_buy = 'true',
pole_buy = 'none', -- 'none'/'logs'/'pole'
})
]]

require('strict')
require('strict')
local persistant = require('Module:Break Isolation').get_module_store('Module:Location Table')
require('Module:Mw.html extension')
local parse = require('Module:Param Parse')
local xp = require('Module:Experience')
local edit = 'Unknown <span class="small plainlinks">' .. require('Module:Edit button')() .. '</span>'
local currency = require('Module:Currency')
local lang = mw.language.getContentLanguage()


local _ingots = {}
persistant.counter = persistant.counter or 0
local function lookup_ingots(profession)
if _ingots[profession] then
return _ingots[profession]
end
_ingots[profession] = {
recipes = {},
order = {}
}
for _, result in ipairs(mw.smw.ask{
({
Blacksmith = '[[Uses facility::Goblin Smelter||Gnome Smelter]]',
Stonemason = '[[Uses facility::T.E.A. Machine]]',
Bonewright = '[[Uses facility::B.R.E.W.S. Vat]]'
})[profession],
['?Recipe JSON'] = '',
mainlabel = '-',
sort = 'Profession Level A',
limit = '500'
}) do
result = mw.text.jsonDecode(result[1])
local ingot = result.output[1].name
_ingots[profession].recipes[ingot] = {
xp = result.xp,
facility = result.facility,
ingot = result.output[1].name,
ore = result.materials[1].name,
level = result.level
}
table.insert(_ingots[profession].order, ingot)
end
return _ingots[profession]
end


local function lookup_weapons(profession)
local p = {}
local ingots = lookup_ingots(profession)

local weapons = {}
function p.head(frame)
for _, result in ipairs(mw.smw.ask{
local args = frame.args
({
assert(not persistant.in_table, 'Second {{LocTableHead}} (Missing {{LocTableBottom}}?)')
Blacksmith = '[[Uses facility::Goblin Forge||Gnome Forge]]',
persistant.in_table = true
Stonemason = '[[Category:Stonemason]][[Category:Pages with recipes]][[Uses facility::!T.E.A. Machine]]',
persistant.json = {}
Bonewright = '[[Category:Bonewright]][[Category:Pages with recipes]][[Uses facility::!B.R.E.W.S. Vat]]'
persistant.version = args.version
})[profession],
persistant.location_object = mw.title.getCurrentTitle().fullText
['?Recipe JSON'] = '',
if args.version then
mainlabel = '-',
persistant.location_object = persistant.location_object .. '#' .. args.version
sort = 'Profession Level A',
limit = '500'
}) do
local lvl = result['Profession Level A']
result = mw.text.jsonDecode(result[1])
if not result.passive and result.profession == profession then
local ingot
local pole
for _, material in ipairs(result.materials) do
if ingots.recipes[material.name] then
assert(not ingot)
ingot = material
else
assert(not pole)
pole = material
end
end
weapons[ingot.name] = weapons[ingot.name] or {}
table.insert(weapons[ingot.name], {
facility = result.facility,
level = result.level,
ingot = ingot,
pole = pole,
name = result.output[1].name,
xp = result.xp
})
end
end
end
return weapons
end
end


function p.line(frame)
local function _lookup_price_uncached(item)
local result = mw.smw.ask{
assert(persistant.in_table, '{{LocLine}} when not in a location table (Missing {{LocTableHead}}?)')
('[[Sold item::%s]]'):format(item),
local args = frame.args
['?Shop buy price'] = 'buy',
local loc = args.loc
['?Shop sell price'] = 'sell',
local qty = args.quantity
mainlabel = '-'
qty = tonumber(qty or '')
}[1]
persistant.json[loc] = qty or -1
return {
local loc_link = ('[[%s]]'):format(loc)
buy = tonumber(result.buy),
local episode = mw.smw.ask{loc_link, '?Episode#=', 'mainlabel=-'}
sell = tonumber(result.sell)
episode = episode and episode[1][1]
}
end


local _lookup_price_cache = {}
persistant.counter = persistant.counter + 1
local function lookup_price(item)
local id = 'LOC_' .. persistant.counter
_lookup_price_cache[item] = _lookup_price_cache[item] or _lookup_price_uncached(item)
return _lookup_price_cache[item]
end


local function _lookup_pole_uncached(pole)
mw.smw.subobject({
local pole_data = mw.text.jsonDecode(mw.smw.ask{
['Location object'] = persistant.location_object,
('[[%s]]'):format(pole),
['Located in'] = loc,
['Location quantity'] = qty or 'Unknown'
['?Recipe JSON'] = '',
mainlabel = '-'
}, id)
}[1][1])
local post = pole_data.materials[1].name
local post_data = mw.text.jsonDecode(mw.smw.ask{
('[[%s]]'):format(post),
['?Recipe JSON'] = '',
mainlabel = '-'
}[1][1])
return {
level = pole_data.level,
pole = pole,
post = post,
log = post_data.materials[1].name
}
end


local _lookup_pole_cache = {}
return mw.html.create('tr')
local function lookup_pole(pole)
:attr{ id = id }
_lookup_pole_cache[pole] = _lookup_pole_cache[pole] or _lookup_pole_uncached(pole)
:tag('td'):wikitext(loc_link):done()
return _lookup_pole_cache[pole]
:tag('td'):wikitext(parse.episode_func(episode)):done()
:tag('td'):wikitext(qty or edit):done()
:done()
end
end


function p.bottom(frame)
local function _lookup_ore_uncached(ore)
local data = mw.smw.ask{
assert(persistant.in_table, '{{LocTableBottom}} when not in a location table (Missing {{LocTableHead}}?)')
('[[%s]]'):format(ore),
['?Profession Level A'] = '',
mainlabel = '-'
}[1]
return {
level = tonumber(data[1])
}
end


local _lookup_ore_cache = {}
if persistant.version then
local function lookup_ore(ore)
mw.smw.subobject({ ['Location JSON'] = mw.text.jsonEncode(persistant.json) }, persistant.version)
_lookup_ore_cache[ore] = _lookup_ore_cache[ore] or _lookup_ore_uncached(ore)
return _lookup_ore_cache[ore]
end

local function _lookup_log_uncached(log)
local data = mw.smw.ask{
('[[%s]]'):format(log),
['?Profession Level A'] = '',
mainlabel = '-'
}[1]
return {
level = tonumber(data[1])
}
end

local _lookup_log_cache = {}
local function lookup_log(log)
_lookup_log_cache[log] = _lookup_log_cache[log] or _lookup_log_uncached(log)
return _lookup_log_cache[log]
end

local p = {}

local p = {}

function p.ingots(frame)
return table.concat(lookup_ingots(frame.args.profession).order, ',')
end

function p.main(frame)
return p._main(frame:getParent().args)
end

function p._main(args)
local profession = args.profession

local current_xp = tonumber(args.current_xp) or 0
local current_lvl
if current_xp <= 500 then
current_lvl = current_xp
current_xp = xp._total_xp(current_lvl)
else
else
current_lvl = xp._level_at(current_xp)
mw.smw.set{ ['Location JSON'] = mw.text.jsonEncode(persistant.json) }
end

local target_xp = tonumber(args.target_xp) or 0
local target_lvl
if target_xp <= 500 then
target_lvl = target_xp
target_xp = xp._total_xp(target_lvl)
else
target_lvl = xp._level_at(target_xp)
end

local remaining_xp = target_xp - current_xp

local buying_ores = args.ore_buy ~= 'false'

local buying_poles = args.pole_buy
local chopping_logs = buying_poles == 'none'
local buying_logs = buying_poles == 'logs'
buying_poles = not (buying_logs or chopping_logs)

local ingot = lookup_ingots(profession).recipes[args.ingot]

local result = mw.html.create()

result:wikitext(('To get from %s xp (level %s) to %s xp (level %s) requires %s experience'):format(lang:formatNum(current_xp), current_lvl, lang:formatNum(target_xp), target_lvl, lang:formatNum(remaining_xp)))

local tbl = result:tag('table')
:addClass('wikitable sortable')

local function skillclickpic(profession)
return ('[[File:%s small icon.png|15px|link=%s|%s level]]'):format(profession, profession, profession)
end
local colspan = 23
if chopping_logs then
colspan = colspan + 1
end
if chopping_logs or buying_logs then
colspan = colspan + 3
end

tbl
:tag('tr')
:th(skillclickpic(({
Blacksmith = 'Miner',
Stonemason = 'Miner',
Bonewright = 'Gatherer'
})[profession])):done()
:th{ ({
Blacksmith = 'Ore',
Stonemason = 'Rock',
Bonewright = 'Bone'
})[profession], attr = { colspan = '3' } }:done()
:th{ ({
Blacksmith = 'Ingot',
Stonemason = 'Etched',
Bonewright = 'Brewed'
})[profession], attr = { colspan = '2' } }:done()
:IF(chopping_logs)
:th(skillclickpic('Woodcutter')):done()
:END()
:IF(chopping_logs or buying_logs)
:th{ 'Log', attr = { colspan = '3' } }:done()
:th(skillclickpic('Carpenter')):done()
:END()
:th{ 'Pole', attr = { colspan = '3' } }:done()
:th(skillclickpic(profession)):done()
:th{ 'Product', attr = { colspan = '3' } }:done()
:th{ 'Cost', attr = { colspan = '10' } }:done()
:done()

for _, weapon in ipairs(lookup_weapons(profession)[ingot.ingot]) do
if not ingot.xp then
tbl
:tr()
:td{
'Missing XP for preparing [[' .. ingot.ingot .. ']]; Please [' .. tostring(mw.uri.fullUrl(ingot.ingot, 'action=edit&section=1')) .. ' edit the page] to add the experience earned (after |exp =)',
attr = { colspan = colspan }
}
:done()
break
end
if not weapon.xp then
tbl
:tr()
:td{
'Missing XP for creating [[' .. weapon.name .. ']]; Please ['.. tostring(mw.uri.fullUrl(weapon.name, 'action=edit&section=1')) .. ' edit the page] to add the experience earned (after |exp =)',
attr = { colspan = colspan }
}
:done()
else
local xp_per = weapon.xp + ingot.xp * weapon.ingot.quantity
local needed = math.ceil(remaining_xp / xp_per)

local needed_ores = needed * weapon.ingot.quantity
local row = tbl
:tag('tr')
:td(lookup_ore(ingot.ore).level):done()
:td{ needed_ores .. '&times;', css = { ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right', ['data-sort-value'] = needed_ores } }:done()
:td{ ('[[File:%s.png|link=%s|30px]]'):format(ingot.ore, ingot.ore), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border' }:done()
:td{ '[[' .. ingot.ore .. ']]', addClass = 'plinkt-link no-border' }:done()
:td{ ('[[File:%s.png|link=%s|30px]]'):format(ingot.ingot, ingot.ingot), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border', ['data-sort-value'] = needed_ores }:done()
:td{ '[[' .. ingot.ingot .. ']]', addClass = 'plinkt-link no-border' }:done()

local pole_info
local needed_logs, needed_poles

if weapon.pole then
needed_poles = needed * weapon.pole.quantity
if chopping_logs or buying_logs then
pole_info = lookup_pole(weapon.pole.name)
needed_logs = math.ceil(needed_poles / 2)
end
if chopping_logs then
row
:td(lookup_log(pole_info.log).level):done()
end
if chopping_logs or buying_logs then
row
:td{ needed_logs .. '&times;', css = { ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right' }, attr = { ['data-sort-value'] = needed_logs } }:done()
:td{ ('[[File:%s.png|link=%s|30px]]'):format(pole_info.log, pole_info.log), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border' }:done()
:td{ '[[' .. pole_info.log .. ']]', addClass = 'plinkt-link no-border' }:done()
:td(pole_info.level):done()
end

row
:td{ needed_poles .. '&times;', css = { ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right' }, attr = { ['data-sort-value'] = needed_poles } }:done()
:td{ ('[[File:%s.png|link=%s|30px]]'):format(weapon.pole.name, weapon.pole.name), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border' }:done()
:td{ '[[' .. weapon.pole.name .. ']]', addClass = 'plinkt-link no-border' }:done()
else
if chopping_logs then
row
:na()
end
if chopping_logs or buying_logs then
row
:td{ '<small>N/A</small>', addClass = 'table-na', attr = { ['data-sort-value'] = '0', colspan = '3' } }:done()
:na()
end
row
:td{ '<small>N/A</small>', addClass = 'table-na', attr = { ['data-sort-value'] = '0', colspan = '3' } }:done()
end

row
:td(weapon.level)
:td{ needed .. '&times;', css = { ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right' }, attr = { ['data-sort-value'] = needed } }:done()
:td{ ('[[File:%s.png|link=%s|30px]]'):format(weapon.name, weapon.name), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border' }:done()
:td{ '[[' .. weapon.name .. ']]', addClass = 'plinkt-link no-border' }

local cost = needed_ores * lookup_price(ingot.ore)[buying_ores and 'buy' or 'sell']
if chopping_logs then
if needed_logs then
cost = cost + needed_logs * lookup_price(pole_info.log).sell
end
elseif buying_logs then
if needed_logs then
cost = cost + needed_logs * lookup_price(pole_info.log).buy
end
else
if weapon.pole then
cost = cost + needed_poles * lookup_price(weapon.pole.name).buy
end
end

row:node(currency._cell(cost, { html = 'yes' }))
end
end
end


return result
persistant.in_table = nil
persistant.json = nil
persistant.version = nil
persistant.location_object = nil
end
end



Latest revision as of 20:30, 21 December 2024

Documentation for this module may be created at Module:Sandbox/User:Artoire/1/doc

--[[
mw.log(p._main{
	current_xp = 24,
	target_xp = 37,
	ingot = 'Coarse Deathstone (Etched)',
	profession = 'Stonemason',  -- 'Bonewright'/'Stonemason'/'Blacksmith'
	ore_buy = 'true',
	pole_buy = 'none',  -- 'none'/'logs'/'pole'
})
]]

require('strict')
require('Module:Mw.html extension')
local xp = require('Module:Experience')
local currency = require('Module:Currency')
local lang = mw.language.getContentLanguage()

local _ingots = {}
local function lookup_ingots(profession)
	if _ingots[profession] then
		return _ingots[profession]
	end
	_ingots[profession] = {
		recipes = {},
		order = {}
	}
	for _, result in ipairs(mw.smw.ask{
		({
			Blacksmith = '[[Uses facility::Goblin Smelter||Gnome Smelter]]',
			Stonemason = '[[Uses facility::T.E.A. Machine]]',
			Bonewright = '[[Uses facility::B.R.E.W.S. Vat]]'
		})[profession],
		['?Recipe JSON'] = '',
		mainlabel = '-',
		sort = 'Profession Level A',
		limit = '500'
	}) do
		result = mw.text.jsonDecode(result[1])
		local ingot = result.output[1].name
		_ingots[profession].recipes[ingot] = {
			xp = result.xp,
			facility = result.facility,
			ingot = result.output[1].name,
			ore = result.materials[1].name,
			level = result.level
		}
		table.insert(_ingots[profession].order, ingot)
	end
	return _ingots[profession]
end

local function lookup_weapons(profession)
	local ingots = lookup_ingots(profession)
	local weapons = {}
	for _, result in ipairs(mw.smw.ask{
		({
			Blacksmith = '[[Uses facility::Goblin Forge||Gnome Forge]]',
			Stonemason = '[[Category:Stonemason]][[Category:Pages with recipes]][[Uses facility::!T.E.A. Machine]]',
			Bonewright = '[[Category:Bonewright]][[Category:Pages with recipes]][[Uses facility::!B.R.E.W.S. Vat]]'
		})[profession],
		['?Recipe JSON'] = '',
		mainlabel = '-',
		sort = 'Profession Level A',
		limit = '500'
	}) do
		local lvl = result['Profession Level A']
		result = mw.text.jsonDecode(result[1])
		if not result.passive and result.profession == profession then
			local ingot
			local pole
			for _, material in ipairs(result.materials) do
				if ingots.recipes[material.name] then
					assert(not ingot)
					ingot = material
				else
					assert(not pole)
					pole = material
				end
			end
			weapons[ingot.name] = weapons[ingot.name] or {}
			table.insert(weapons[ingot.name], {
				facility = result.facility,
				level = result.level,
				ingot = ingot,
				pole = pole,
				name = result.output[1].name,
				xp = result.xp
			})
		end
	end
	return weapons
end

local function _lookup_price_uncached(item)
	local result = mw.smw.ask{
		('[[Sold item::%s]]'):format(item),
		['?Shop buy price'] = 'buy',
		['?Shop sell price'] = 'sell',
		mainlabel = '-'
	}[1]
	return {
		buy = tonumber(result.buy),
		sell = tonumber(result.sell)
	}
end

local _lookup_price_cache = {}
local function lookup_price(item)
	_lookup_price_cache[item] = _lookup_price_cache[item] or _lookup_price_uncached(item)
	return _lookup_price_cache[item]
end

local function _lookup_pole_uncached(pole)
	local pole_data = mw.text.jsonDecode(mw.smw.ask{
		('[[%s]]'):format(pole),
		['?Recipe JSON'] = '',
		mainlabel = '-'
	}[1][1])
	local post = pole_data.materials[1].name
	local post_data = mw.text.jsonDecode(mw.smw.ask{
		('[[%s]]'):format(post),
		['?Recipe JSON'] = '',
		mainlabel = '-'
	}[1][1])
	return {
		level = pole_data.level,
		pole = pole,
		post = post,
		log = post_data.materials[1].name
	}
end

local _lookup_pole_cache = {}
local function lookup_pole(pole)
	_lookup_pole_cache[pole] = _lookup_pole_cache[pole] or _lookup_pole_uncached(pole)
	return _lookup_pole_cache[pole]
end

local function _lookup_ore_uncached(ore)
	local data = mw.smw.ask{
		('[[%s]]'):format(ore),
		['?Profession Level A'] = '',
		mainlabel = '-'
	}[1]
	return {
		level = tonumber(data[1])
	}
end

local _lookup_ore_cache = {}
local function lookup_ore(ore)
	_lookup_ore_cache[ore] = _lookup_ore_cache[ore] or _lookup_ore_uncached(ore)
	return _lookup_ore_cache[ore]
end

local function _lookup_log_uncached(log)
	local data = mw.smw.ask{
		('[[%s]]'):format(log),
		['?Profession Level A'] = '',
		mainlabel = '-'
	}[1]
	return {
		level = tonumber(data[1])
	}
end

local _lookup_log_cache = {}
local function lookup_log(log)
	_lookup_log_cache[log] = _lookup_log_cache[log] or _lookup_log_uncached(log)
	return _lookup_log_cache[log]
end

local p = {}

local p = {}

function p.ingots(frame)
	return table.concat(lookup_ingots(frame.args.profession).order, ',')
end

function p.main(frame)
	return p._main(frame:getParent().args)
end

function p._main(args)
	local profession = args.profession

	local current_xp = tonumber(args.current_xp) or 0
	local current_lvl
	if current_xp <= 500 then
		current_lvl = current_xp
		current_xp = xp._total_xp(current_lvl)
	else
		current_lvl = xp._level_at(current_xp)
	end

	local target_xp = tonumber(args.target_xp) or 0	
	local target_lvl
	if target_xp <= 500 then
		target_lvl = target_xp
		target_xp = xp._total_xp(target_lvl)
	else
		target_lvl = xp._level_at(target_xp)
	end

	local remaining_xp = target_xp - current_xp

	local buying_ores = args.ore_buy ~= 'false'

	local buying_poles = args.pole_buy
	local chopping_logs = buying_poles == 'none'
	local buying_logs = buying_poles == 'logs'
	buying_poles = not (buying_logs or chopping_logs)

	local ingot = lookup_ingots(profession).recipes[args.ingot]

	local result = mw.html.create()

	result:wikitext(('To get from %s xp (level %s) to %s xp (level %s) requires %s experience'):format(lang:formatNum(current_xp), current_lvl, lang:formatNum(target_xp), target_lvl, lang:formatNum(remaining_xp)))

	local tbl = result:tag('table')
		:addClass('wikitable sortable')

	local function skillclickpic(profession)
		return ('[[File:%s small icon.png|15px|link=%s|%s level]]'):format(profession, profession, profession)
	end
	
	local colspan = 23
	if chopping_logs then
		colspan = colspan + 1
	end
	if chopping_logs or buying_logs then
		colspan = colspan + 3
	end

	tbl
		:tag('tr')
			:th(skillclickpic(({
				Blacksmith = 'Miner',
				Stonemason = 'Miner',
				Bonewright = 'Gatherer'
			})[profession])):done()
			:th{ ({
				Blacksmith = 'Ore',
				Stonemason = 'Rock',
				Bonewright = 'Bone'
			})[profession], attr = { colspan = '3' } }:done()
			:th{ ({
				Blacksmith = 'Ingot',
				Stonemason = 'Etched',
				Bonewright = 'Brewed'
			})[profession], attr = { colspan = '2' } }:done()
			:IF(chopping_logs)
				:th(skillclickpic('Woodcutter')):done()
			:END()
			:IF(chopping_logs or buying_logs)
				:th{ 'Log', attr = { colspan = '3' } }:done()
				:th(skillclickpic('Carpenter')):done()
			:END()
			:th{ 'Pole', attr = { colspan = '3' } }:done()
			:th(skillclickpic(profession)):done()
			:th{ 'Product', attr = { colspan = '3' } }:done()
			:th{ 'Cost', attr = { colspan = '10' } }:done()
		:done()

	for _, weapon in ipairs(lookup_weapons(profession)[ingot.ingot]) do
		if not ingot.xp then
			tbl
				:tr()
					:td{
						'Missing XP for preparing [[' .. ingot.ingot .. ']]; Please [' .. tostring(mw.uri.fullUrl(ingot.ingot, 'action=edit&section=1')) .. ' edit the page] to add the experience earned (after |exp =)',
						attr = { colspan = colspan }
					}
				:done()
			break
		end
		if not weapon.xp then
			tbl
				:tr()
					:td{
						'Missing XP for creating [[' .. weapon.name .. ']]; Please ['.. tostring(mw.uri.fullUrl(weapon.name, 'action=edit&section=1')) .. ' edit the page] to add the experience earned (after |exp =)',
						attr = { colspan = colspan }
					}
				:done()
		else
			local xp_per = weapon.xp + ingot.xp * weapon.ingot.quantity
			local needed = math.ceil(remaining_xp / xp_per)

			local needed_ores = needed * weapon.ingot.quantity
			local row = tbl
				:tag('tr')
					:td(lookup_ore(ingot.ore).level):done()
					:td{ needed_ores .. '&times;', css = { ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right', ['data-sort-value'] = needed_ores } }:done()
					:td{ ('[[File:%s.png|link=%s|30px]]'):format(ingot.ore, ingot.ore), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border' }:done()
					:td{ '[[' .. ingot.ore .. ']]', addClass = 'plinkt-link no-border' }:done()
					:td{ ('[[File:%s.png|link=%s|30px]]'):format(ingot.ingot, ingot.ingot), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border', ['data-sort-value'] = needed_ores }:done()
					:td{ '[[' .. ingot.ingot .. ']]', addClass = 'plinkt-link no-border' }:done()

			local pole_info
			local needed_logs, needed_poles

			if weapon.pole then
				needed_poles = needed * weapon.pole.quantity
				if chopping_logs or buying_logs then
					pole_info = lookup_pole(weapon.pole.name)
					needed_logs = math.ceil(needed_poles / 2)
				end
				if chopping_logs then
					row
						:td(lookup_log(pole_info.log).level):done()
				end
				if chopping_logs or buying_logs then
					row
						:td{ needed_logs .. '&times;', css = { ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right' }, attr = { ['data-sort-value'] = needed_logs } }:done()
						:td{ ('[[File:%s.png|link=%s|30px]]'):format(pole_info.log, pole_info.log), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border' }:done()
						:td{ '[[' .. pole_info.log .. ']]', addClass = 'plinkt-link no-border' }:done()
						:td(pole_info.level):done()
				end

				row
					:td{ needed_poles .. '&times;', css = { ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right' }, attr = { ['data-sort-value'] = needed_poles } }:done()
					:td{ ('[[File:%s.png|link=%s|30px]]'):format(weapon.pole.name, weapon.pole.name), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border' }:done()
					:td{ '[[' .. weapon.pole.name .. ']]', addClass = 'plinkt-link no-border' }:done()
			else
				if chopping_logs then
					row
						:na()
				end
				if chopping_logs or buying_logs then
					row
						:td{ '<small>N/A</small>', addClass = 'table-na', attr = { ['data-sort-value'] = '0', colspan = '3' } }:done()
						:na()
				end
				row
					:td{ '<small>N/A</small>', addClass = 'table-na', attr = { ['data-sort-value'] = '0', colspan = '3' } }:done()
			end

			row
				:td(weapon.level)
				:td{ needed .. '&times;', css = { ['border-right'] = '0', ['padding-right'] = '0', ['text-align'] = 'right' }, attr = { ['data-sort-value'] = needed } }:done()
				:td{ ('[[File:%s.png|link=%s|30px]]'):format(weapon.name, weapon.name), css = { ['border-left'] = '0', ['padding-left'] = '0' }, addClass = 'plinkt-image no-border' }:done()
				:td{ '[[' .. weapon.name .. ']]', addClass = 'plinkt-link no-border' }

			local cost = needed_ores * lookup_price(ingot.ore)[buying_ores and 'buy' or 'sell']
			if chopping_logs then
				if needed_logs then
					cost = cost + needed_logs * lookup_price(pole_info.log).sell
				end
			elseif buying_logs then
				if needed_logs then
					cost = cost + needed_logs * lookup_price(pole_info.log).buy
				end
			else
				if weapon.pole then
					cost = cost + needed_poles * lookup_price(weapon.pole.name).buy
				end
			end

			row:node(currency._cell(cost, { html = 'yes' }))
		end
	end

	return result
end

return p