Module:Sandbox/User:Nex Undique

Documentation for this module may be created at Module:Sandbox/User:Nex Undique/doc

-- <nowiki>
local p = {}

local params = require('Module:Paramtest')
local lang = mw.language.getContentLanguage()
local coins_image = require('Module:Coins image')
local curr_image = require('Module:Currency Image')
local exchange = require('Module:Exchange') 
local yesno = require('Module:Yesno')

-- precalculated cached data
local droppeditem_data = mw.loadData('Module:DropsLine/itemData')
local geprices_data = mw.loadData('Module:GEPrices/data')
local highalch_data = mw.loadData('Module:GEHighAlchs/data')

local geprice = exchange._price
local f_gealch = exchange._highalch
local ptitle = mw.title.getCurrentTitle()
local ns = ptitle.nsText
local title = ptitle.fullText
local pgTitle = ptitle.text

local _membs = '&nbsp;<sub title="Members only" style="cursor:help; margin-left:3px;">(m)</sub>'
local _membscols =  {
	[true] = {
		content = '[[File:P2P icon.png|25px|link=Members]]',
		title = 'Members only',
		style = {
			cursor = 'help'
		},
		class = 'members-item'
	},
	[false] = {
		content = '[[File:F2P icon.png|25px|link=Free-to-play]]',
		title = 'Free-to-play',
		style = {
			cursor = 'help'
		},
		class = 'nonmembers-item'
	}
}
local _noted = '&nbsp;<span class="dropsline-noted">(noted)</span>'
local _altval = '<span class="dropsline-altval" style="margin-left:0.3em;">[[File:AltValue.png|link=|frameless|20px]]</span>'
local _gealch = '<span class="dropsline-gealch" style="margin-left:0.3em;">[[File:High_Level_Alchemy_icon.png|link=High Level Alchemy|frameless|20px]]</span>'

local _priceStrings = {
    alch = {
        coins = "%s coin%s",
        ge = "%s coin%s each",
        smw = "%s coin%s each",
        cache = "%s coin%s each.",
        alch = "%s coin%s each",
        alt = "%s %s%s each; this item has a distinct value, even though it cannot be alchemised."
    },
    ge = {
        coins = "%s coin%s",
        ge = "%s coin%s each",
        cache = "%s coin%s each.",
        alch = "%s coin%s each; this is the high alchemy value as this item cannot be traded on the Grand Exchange.",
        alt = "%s %s%s each; this item has a distinct value, even though it cannot be traded on the Grand Exchange or be alchemised."
    }
}

local _version_separator_ = ','

local smwData = nil
function getSMWInfo(item)
    if smwData ~= nil then
        return smwData
    end
    local smw = mw.smw.ask({
        '[['..item..']]',
        '?High Alchemy value',
        '?Is members only'
    })
    if smw and smw[1] then
        smwData = {
            alch = smw[1]['High Alchemy value'],
            members = smw[1]['Is members only']
        }
    else
        smwData = false
    end
    return smwData
end

local dropType = {
    thieving = {
        'Loot from', 'Loot item', 'Loot item variant'
    },
    reward = {
        'Reward from', 'Reward item', 'Reward item variant'
    },
    drop = {
        'Drop from', 'Drops item', 'Drops item variant'
    },
    hunter = {
    	'Hunted from', 'Hunted item', 'Hunted item variant'
    },
    archaeology = {
    	'Excavated from', 'Excavated item', 'Excavated item variant'
    }
}

--bg, txt, sort
local rarities = {
	always = { 'table-bg-blue', 1 },
	common = { 'table-bg-green', 16 },
	uncommon = { 'table-bg-yellow', 64 },
	rare = { 'table-bg-orange', 256 },
	['very rare'] = { 'table-bg-red', 1024 },
	random = { 'table-bg-pink', 4096 },
	varies = { 'table-bg-pink', 4096 },
	_default = { 'table-bg-grey', 65536 }
}

-- arbitrary numbers
local rarities2 = {
	{ 1, 'table-bg-blue' },
	{ 1/16, 'table-bg-green' },
	{ 1/64, 'table-bg-yellow' },
	{ 1/256, 'table-bg-orange' },
	{ 1/1024, 'table-bg-red' }
}

function get_rarity_class(val)
	for i,v in ipairs(rarities2) do
		curr = v
		if val >= v[1] then
			break
		end
	end
	return curr[2]
end

function commas(n)
	if tonumber(n) then
		return lang:formatNum(tonumber(n))
	else
		return n
	end
end

-- list of items to automatically not use gemw
-- this should only be used for items dropped by many monsters
local nogemw = {
	'clue scroll', 'starved ancient effigy', 'rare drop table', 'wilderness shared loot table', 'warpriest', 'court summons'
}
-- list of items to automatically not use high alch
-- this should only be used for items dropped by many monsters
local noalch = {
	'clue scroll', 'starved ancient effigy', 'rare drop table', 'wilderness shared loot table', 'warpriest', 'court summons', 'stone spirit'
}
-- list of items with alch value of 0.2 * value  (runes)
local alch02 = {
	'air rune', 'mind rune', 'water rune', 'earth rune', 'fire rune', 'body rune',
	'cosmic rune', 'chaos rune', 'nature rune', 'law rune', 'death rune', 'astral rune',
	'blood rune', 'soul rune', 'armadyl rune', 'dust rune', 'lava rune', 'mist rune',
	'mud rune', 'smoke rune', 'steam rune'
}

function expr(t)
	local noerr, val = pcall(mw.ext.ParserFunctions.expr, t)
	if noerr then
		return tonumber(val)
	else
		return false
	end
end
function sigfig(n, f)
	f = math.floor(f-1)
	if n == 0 then return 0 end
	local m = math.floor(math.log10(n))
	local v = n / (10^(m-f))
	-- floor(x + 0.5) is standard rounding to one decimal place
	v = math.floor(v+0.5) * 10^(m-f)
	return v
end
p.sigfig = sigfig
function sigfigalt(n)
	if n >= 100 then
		return math.floor(n)
	else
		return sigfig(n, 3)
	end
end

function p.main(frame)
	local args = frame:getParent().args
	local tempArgs = frame.args
	-- Params and defaults
	local name,namenotes,
		quantity,quantitynotes,
		rarity,raritynotes,memsover,
		altcur,monVers,altSource = params.defaults{
					{args.Name,'Item'},
					{args.Namenotes,''},
					{args.Quantity,'Unknown'},
					{args.Quantitynotes,''},
					{args.Rarity,'Unknown'},
					{args.Raritynotes,''},
					{args.Members,''},
					{args.AltCurrency,''},
					{args.Version,''},
					{args.Altsource,'GazBot'},
				}
	if altSource ~= "GazBot" then
		pgTitle = altSource
	end
	local isCoins = name:lower() == 'coins'
	local altname = params.default_to(args.Alt,name)
	local gemwname = params.default_to(args.gemwname,name)
	local _smwname = params.default_to(args.smwname,gemwname)
	--local raritynotes = args.Raritynotes or ''
	-- Remove version number from potions, enchanted jewellery, waterskins etc for smw
	local cleanedName
	local dropVers = ''
	if _smwname:match(' %(%d%)$') then
		cleanedName, dropVers = mw.ustring.match(_smwname, '^(.-) (%(%d%))$')
    elseif _smwname:match('%#') then
        cleanedName, dropVers = mw.ustring.match(_smwname, '^(.-)%#([%w%s%(%)_]+)$')
    else
        cleanedName = mw.ustring.gsub(_smwname, ' %(%d%)$', '')
    end
    cleanedName = mw.text.trim(cleanedName)
    dropVers = mw.text.trim(dropVers)
    
    local smwname = cleanedName 
    if dropVers ~= '' then
        -- get subobject instead
        smwname = cleanedName..'#'..dropVers
    end
	
	local rarity_value
	if rarities[rarity:lower()] then
		rarity = params.ucflc(rarity)
	else
		rarity_value = expr(rarity)
	end
	
	local hasCur = false
	if params.has_content(altcur) and mw.ustring.lower(altcur) ~= 'coins' then
		hasCur = true
	end

	quantity = mw.ustring.lower(quantity)
	local gemw = yesno(args.gemw or 'yes', false)
	local alch = yesno(args.alch or 'yes', false)

	-- automatic no gemw
    for _, v in ipairs(nogemw) do
        -- can't use a plain table lookup because this uses match
		if mw.ustring.match(mw.ustring.lower(name or ''), v) then
			gemw = false
			break
		end
	end
	-- automatic no alch
	for _, v in ipairs(noalch) do
        -- can't use a plain table lookup because this uses match
		if mw.ustring.match(mw.ustring.lower(name or ''), v) then
			alch = false
			break
		end
	end

	-- Test for existance of alch value
    local hasmwalch, smwalchval
    local valueInfo = {
        alch = {
            has = false,
            value = 0,
            from = nil
        },
        ge = {
            has = false,
            value = 0,
            from = nil
        }
    }
    
    local cached_dropdata = droppeditem_data[smwname]
    local cached_members, cached_alch = nil,nil
    if type(cached_dropdata) == 'table' then
    	if cached_dropdata[1] ~= nil and cached_dropdata[2] ~= nil then
    		cached_members = cached_dropdata[1]
    		cached_alch = cached_dropdata[2]
    	elseif cached_dropdata[1] ~= nil then
    		cached_dropdata = cached_dropdata[1]
    		if type(cached_dropdata) == 'boolean' then
    			cached_members = cached_dropdata
    		elseif type(cached_dropdata) == 'number' then
    			cached_alch = cached_dropdata
    		end
    	end
    end
    
    local geprice_frombulk = geprices_data[gemwname]
    if not (type(geprice_frombulk) == 'number' and geprice_frombulk > 0) then
    	geprice_frombulk = nil
    end
    
    if cached_alch == nil then
    	cached_alch = highalch_data[gemwname]
    	if not (type(cached_alch) == 'number' and cached_alch > -1) then
    		cached_alch = nil
    	end
    end
    	
    
    if isCoins then
        -- coins override
		valueInfo = {
            alch = {
                has = true,
                value = 1,
                from = 'coins'
            },
            ge = {
                has = true,
                value = 1,
                from = 'coins'
            }
        }
    elseif alch then
    	if cached_alch ~= nil then
    		valueInfo.alch = {
    			has = true,
    			value = cached_alch,
    			from = 'cache'
    		}
        elseif gemw then
            -- is not on the no-ge list/override
            -- lookup in GEMW
            local hasgealch, gealchval = pcall(f_gealch,gemwname)
        -- is not on the no-alch list or has alch disabled (and is not coins)
            if hasgealch then
            	if gealchval > -1 then
	                -- has a alch value from GEMW
	                valueInfo.alch = {
	                    has = true,
	                    value = tonumber(gealchval),
	                    from = 'ge'
	                }
                end
            end
        end
        if not valueInfo.alch.has then
            -- failed to find alch in GEMW or is on the no-ge list/override
            -- lookup in SMW
            local smwret = getSMWInfo(smwname)
            if smwret and smwret.alch ~= nil then
                -- alch is defined, use it
                valueInfo.alch = {
                    has = true,
                    value = smwret.alch,
                    from = 'smw'
                }
            elseif args.AltValue then
                --AltValue set, no alch from GE or SMW, use alt as regular alch
                valueInfo.alch = {
                    has = true,
                    value = tonumber(args.AltValue),
                    from = 'alt'
                }
                if hasCur then
                    valueInfo.alch.curr = altcur
                end
            else
            	alch = false
            end
        end
    else
        -- is on the no-alch list (and is not coins)
        if args.AltValue then
            --Altvalue set, no alch value
            valueInfo.alch = {
                has = true,
                value = tonumber(args.AltValue),
                from = 'alt'
            }
            if hasCur then
                valueInfo.alch.curr = altcur
            end
        end
    end
	-- Test for existence of an exchange page
    if not isCoins then
        if gemw then
        	if geprice_frombulk ~= nil then
                valueInfo.ge = {
                    has = true,
                    value = geprice_frombulk,
                    from = 'cache'
                }
        	else
	            local hasgemw, gepric = pcall(geprice,gemwname)
	            if hasgemw then
	                valueInfo.ge = {
	                    has = true,
	                    value = gepric,
	                    from = 'ge'
	                }
	            else
	                valueInfo.ge = {
	                    has = true,
	                    value = valueInfo.alch.value,
	                    from = 'alch'
	                }
	            end
        	end
        elseif args.AltValue then
            valueInfo.ge = {
                has = true,
                value = tonumber(args.AltValue),
                from = 'alt'
            }
            if hasCur then
                valueInfo.ge.curr = altcur
            end
        elseif alch then
            valueInfo.ge = {
                has = true,
                value = valueInfo.alch.value,
                from = 'alch'
            }
        end
    end

	-- Check members or F2P
	local members, smwmem
    local hasmwmem = false
    
    if params.has_content(memsover) then
        -- overridden
        members = yesno(memsover)
    elseif cached_members ~= nil then
    	members = cached_members
    else
        local smwret = getSMWInfo(smwname)
        if smwret and smwret.members ~= nil then
            members = smwret.members
            hasmwmem = true
        end
    end

	-- Add to name of item if members item
	if members == true then
		imgmembs = _membs
	else
		imgmembs = ''
	end
	-- Use 'File:<name>.png' if no image param
	-- Use 'File:<image>' if image param; image param will include extension
	-- Special catch for coins
	local image,image_n
	if isCoins then
		image_n = coins_image(quantity)
	else
		image_n = params.default_to(args.Image, name .. '.png')
		image_n = mw.ustring.gsub(image_n, '#.+$', '.png')
	end
	if image_n:lower() == 'no' or params.is_empty(args.Name) then
		image = ''
	else
		image = mw.ustring.format('[[File:%s|link=%s|alt=%s: RS3 %s drops %s with rarity %s in quantity %s]]', image_n, name, image_n, title, name, rarity, quantity)
	end
	-- this only affects the JSON
	local smwjson = string.lower(args.smwjson or '')
	if params.has_content(args.nosmw) then
		smwjson = false
	elseif smwjson == 'no' then
		smwjson = false
	elseif smwjson == 'rdt' then
		smwjson = 'rdt'
	else
		smwjson = true
	end
		
	-- Table row
	local ret =  p._main{ name,
			altname,namenotes,
			quantity,quantitynotes,
			rarity,rarity_value,raritynotes,
			image,members,
			valueInfo,gemw,alch,alt,
			isCoins,
			tempArgs,monVers,
			cleanedName,dropVers,smwname,
			smwjson }

	-- categories for mainspace
	local cats = ''
	
	if ns == '' then
		cats = categories{name,quantity,rarity}
	end
	return ret..cats
end

-- main function to generate the row
function p._main(...)
	local name,altname,namenotes,quantity,quantitynotes,
		rarity,rarity_value,raritynotes,
		image,members,
		valueInfo,gemw,alch,alt,
		isCoins,
		tempArgs,monVers,
		cleanedName,dropVers,smwname,smwjson = unpack(...)

    -- GE value, alch value, quantity cell contents
	local total, alchtotal, vsort, vasort, _h, _l
    quantity, _h, _l = qty(quantity)
    if valueInfo.ge.has then
	    total, vsort = get_total(valueInfo.ge.value,_h,_l)
        total = total or 'Not sold or alchemisable'
    end
    if valueInfo.alch.has then
	    alchtotal, vasort = get_total(valueInfo.alch.value,_h,_l)
        alchtotal = alchtotal or 'Not alchemisable'
    end
    
    -- value sorts
	if type(vsort) ~= 'number' then
		vsort = 0
	end
	if type(vasort) ~= 'number' then
		vasort = 0
	end

    -- quantity notes
	if #quantitynotes > 3 then
		quantity = quantity..quantitynotes
    end
    
    -- rarity cell contents
	local rare_class, rare_sort
	if rarity_value == undefined then
		rare_class, rare_sort = unpack(rarities[rarity:lower()] or rarities._default)
	elseif rarity_value == false then
		rare_class, rare_sort = unpack(rarities._default)
	else
		rare_sort = 1/rarity_value
		rare_class = get_rarity_class(rarity_value)
	end
	local _r = rarity

    -- members cell contents
    -- @TODO
	local membinfo = _membscols[members] or _membscols[false]
	
    -- monster versions
	local monVersT, monVersTRef, monVersTSubobj = {}, {}, {}
	local hasRowwideVersion = false
	local tblVers = tempArgs.Version
    if params.has_content(tblVers) and params.has_content(tblVers) and mw.ustring.lower(tblVers) ~= 'all' then
        -- versions applied to the entire table
        monVersT = mw.text.split(tblVers, _version_separator_)
    end
    if params.has_content(monVers) and mw.ustring.lower(monVers) ~= 'all' then
        -- versions applied to this row
        for i,v in ipairs(mw.text.split(monVers, _version_separator_)) do
            table.insert(monVersT, v)
        end
        hasRowwideVersion = true
    end
    if #monVersT > 0 and hasRowwideVersion then
        -- setup reference for this row
        local refname = {'autod-'}
        for i,v in ipairs(monVersT) do
            v = mw.text.trim(v)

            -- subobjects to insert into
            local cleanV = mw.ustring.gsub(mw.ustring.sub(v,0,5), '%.', '')..mw.ustring.sub(v,6)
            table.insert(monVersTSubobj, cleanV)

            -- reference name
            local cleanref = mw.ustring.gsub(mw.ustring.lower(v), '%W', '') -- remove all non-word characters
            -- add characters to make this unique enough of a reference name
            table.insert(refname, mw.ustring.sub(v, 1, 3))
            table.insert(refname, mw.ustring.sub(v, -3, -1))
        end
        -- create and append the reference
        raritynotes = raritynotes .. mw.getCurrentFrame():extensionTag{ name='ref', content = mw.ustring.format('Only dropped by %s versions.', mw.text.listToText(monVersT, ', ', ', and ')), args = { group='d', name = table.concat(refname, '') } }
    end

	-- Table row creation
    local ret = mw.html.create('tr')
            -- row-wide things
			:addClass(membinfo.class)
            :css('text-align','center')
            -- inventory image
			:tag('td')
				:addClass('inventory-image')
				:attr('data-sort-value',namesort(name,rare_sort))
				:wikitext(image)
            :done()
            -- item name
			:tag('td')
				:css('text-align','left')
				:addClass('item-col')
				:wikitext(string.format('[[%s|%s]]%s%s',name,altname,#namenotes > 3 and namenotes or '',imgmembs))
            :done()
            -- members
			:tag('td')
                :addClass('members-column')
                :attr('title', membinfo.title)
                :css(membinfo.style)
				:wikitext(membinfo.content)
            :done()
            -- quantity
			:tag('td')
				:attr('data-sort-value',_h)
				:wikitext(quantity)
            :done()
    
    -- rarity
	local rarity_cell = ret:tag('td')
	local rarity_span = rarity_cell:tag('span')
	rarity_span:wikitext(rarity)
	rarity_cell:attr('data-sort-value',rare_sort)
				:addClass(rare_class)
	if type(rarity_value) == 'number' then
		rarity_cell:attr('title', string.format('%.3g%%', 100 * rarity_value))
		rarity_span:attr({
			['data-drop-fraction'] = rarity,
			['data-drop-oneover'] = '1/' .. sigfigalt(1/rarity_value),
			['data-drop-percent'] = sigfig(100 * rarity_value, 3),
			['data-drop-permil'] = sigfig(1000 * rarity_value, 3),
			['data-drop-permyriad'] = sigfig(10000 * rarity_value, 3),
		})
	end
	if #raritynotes > 3 then
		rarity_cell:wikitext(raritynotes)
	end

    -- setup GE and alch cells
    local ge_td = ret:tag('td')
    local alch_td = ret:tag('td')

    -- common attributes
    ge_td   :attr('data-sort-value',vsort)
    :addClass('ge-column')
    :css({
        ['text-align'] = 'right',
        cursor = 'help'
    })
    alch_td :attr('data-sort-value',vsort)
            :addClass('alch-column')
            :css({
                ['text-align'] = 'right',
                cursor = 'help'
            })

    local ge_td_title, ge_td_content, alch_td_title, alch_td_content
    --Cases for the GE and alch values
    local smwValue = 0
    if isCoins then
        local coinsStr = lang:plural(vsort, '', 's')
		ge_td_title = mw.ustring.format(_priceStrings.ge.coins, total, coinsStr)
		ge_td_content = total
		alch_td_title = mw.ustring.format(_priceStrings.alch.coins, total, coinsStr)
		alch_td_content = total
    else
        if valueInfo.ge.has then
            if valueInfo.ge.from == 'alt' then
                local _img, _currency = nil,nil
                if valueInfo.ge.curr then
                    _img, _currency = currency(valueInfo.ge.curr,valueInfo.ge.value,vsort)
                end
                ge_td_title = mw.ustring.format(_priceStrings.ge.alt, commas(valueInfo.ge.value) or '', _currency or 'coin', lang:plural(valueInfo.ge.value or 0, '', 's'))
                ge_td_content = total .. (_img or _altval)
                smwValue = valueInfo.ge.value
            else
                ge_td_title = mw.ustring.format(_priceStrings.ge[valueInfo.ge.from], commas(valueInfo.ge.value), lang:plural(valueInfo.ge.value, '', 's'))
                ge_td_content = total
                if valueInfo.ge.from == 'alch' then
                	ge_td_content = ge_td_content .. _gealch
                	smwValue = valueInfo.ge.value
                end
            end
        end
        if valueInfo.alch.has then
            if valueInfo.alch.from == 'alt' then
                local _img, _currency = nil,nil
                if valueInfo.alch.curr then
                    _img, _currency = currency(valueInfo.alch.curr,valueInfo.alch.value,vasort)
                end
                alch_td_title = mw.ustring.format(_priceStrings.alch.alt, commas(valueInfo.alch.value) or '', _currency or 'coin', lang:plural(valueInfo.alch.value or 0, '', 's'))
                alch_td_content = alchtotal .. (_img or _altval)
            else
                alch_td_title = mw.ustring.format(_priceStrings.alch[valueInfo.alch.from], commas(valueInfo.alch.value), lang:plural(valueInfo.alch.value, '', 's'))
                alch_td_content = alchtotal
            end
        end
        
        if gemw and not valueInfo.ge.has then
            -- GEMW should be available but we didn't find it earlier
            ge_td_title = 'Exchange page not found for "'..name..'". Double check that the exact item name and casing is used for the "name" parameter. Add "gemw=no" to this template if this item cannot be traded on the Grand Exchange or ignore this error and wait for the exchange page to be made.'
            ge_td_content = '<span style="color:#FF0000; border-bottom:1px dashed; font-weight:bold;">Error</span>'
        elseif ge_td_content == nil then
            -- nothing else triggered
            ge_td_content = 'Not sold'
            ge_td_title = 'This item cannot be traded on the Grand Exchange nor alchemised and has no applicable value to display.'
            ge_td:css('color', '#999')
        end
        
        if alch and not valueInfo.alch.has then
            -- alch should be available but we didn't find it earlier
            alch_td_title = 'Alchemy value not found for "'..name..'". Double check that the exact item name and casing is used for the "name" parameter. Add "alch=no" to this template if this item cannot be alchemised or ignore this error and wait for the exchange page to be made.'
            alch_td_content = '<span style="color:#FF0000; border-bottom:1px dashed; font-weight:bold;">Error</span>'
        elseif alch_td_content == nil then
            -- nothing else triggered
            alch_td_content = 'Not alchemisable'
            alch_td_title = 'This item cannot be alchemised and has no applicable value to display.'
            alch_td:css('color', '#999')
        end
    end
    ge_td:wikitext(ge_td_content):attr('title', ge_td_title)
    alch_td:wikitext(alch_td_content):attr('title', alch_td_title)

	-- SMW
	if ns == '' then
		local smw = {}
		local smw_sub = {}
		local smw_done = false
		local smw_variant_pattern = '[%(]' -- things to apply the variant property to - if name matches this pattern, apply property
		-- check if applies to all or only a version
        local source = pgTitle
        local varSubobjs
		if #monVersT > 0 then
			--apply to specified versions
			varSubobjs = monVersT --only subobject id
			source = {}
			for _,v in pairs(monVersT) do
				local subobj = pgTitle..'#'..v --add pagename before subobject id
				table.insert(source, subobj)
			end
		end
		--add function to reduce image to File:Some name.png
		local smwImage = mw.text.encode(image)
		local smwNameNote = mw.text.killMarkers(namenotes)
		local smwQuantity = mw.text.killMarkers(quantity)
		smwQuantity = smwQuantity:gsub('<span class="dropsline%-noted">', '')
		smwQuantity = smwQuantity:gsub('</span>', '')
		smwQuantity = smwQuantity:gsub(',', '')
		smwQuantity = smwQuantity:gsub('&nbsp;', ' ')
		smwQuantity = smwQuantity:gsub(';', ',')
		local smwRarityNote = mw.text.killMarkers(raritynotes)
        if smwjson == true then
            smw_sub = {['Dropped item image']=smwImage, ['Dropped item']=smwname, ['Dropped item text']=altname, ['Name Notes']=smwNameNote, ['Drop Quantity']=smwQuantity, ['Quantity High']=_h, ['Quantity Low']=_l, Rarity=rarity, ['Rarity sort']=100/(tonumber(rare_sort) or 1), ['Rarity Notes']=smwRarityNote}
            local smwparams = dropType[tempArgs.dtype] or dropType.drop
            smw_sub[smwparams[1]] = source
            smw[smwparams[2]] = name
            if name:find(smw_variant_pattern) then
                smw[smwparams[3]] = name
            end
            if string.match(raritynotes, 'UNIQ%-%-ref') then
            	smw_sub['RarityNote Ref'] = true
            end
            if string.match(quantity, 'UNIQ%-%-ref') then
            	smw_sub['QuantityNote Ref'] = true
            end
			if smwValue > 0 then
				smw_sub['Drop Value'] = smwValue
			end
            smw_done = true
		elseif smwjson == 'rdt' then
			smw_sub = {['Dropped item image']=smwImage, ['Dropped item from RDT']=smwname, ['Dropped item text']=altname, ['Name Notes']=smwNameNote, ['Drop Quantity']=smwQuantity, ['Quantity High']=_h, ['Quantity Low']=_l, Rarity=rarity, ['Rarity sort']=100/(tonumber(rare_sort) or 1), ['Rarity Notes']=smwRarityNote}
			if tempArgs.dtype and string.lower(tempArgs.dtype) == 'thieving' then
				smw_sub['RDT Loot from'] = source
			elseif tempArgs.dtype and string.lower(tempArgs.dtype) == 'reward' then
				smw_sub['RDT Reward from'] = source
			else
				smw_sub['RDT Drop from'] = source
			end
			smw['Drops item from RDT'] = name
			if name:find(smw_variant_pattern) then
				smw['Drops item variant from RDT'] = name
			end
			if string.match(raritynotes, 'UNIQ%-%-ref') then
				smw_sub['RarityNote Ref'] = true
			end
			if string.match(quantity, 'UNIQ%-%-ref') then
				smw_sub['QuantityNote Ref'] = true
			end
			if smwValue > 0 then
				smw_sub['Drop Value'] = smwValue
			end
			smw_done = true
		end
		if smw_done then
			if type(source) == 'string' then
				-- no variants, source is pagetitle
				mw.smw.set(smw) -- add data to page
				mw.smw.subobject(smw_sub) -- add drop-subobject to page
			else
				-- variants, source contains applicable varints
				mw.smw.subobject(smw_sub) -- drop is subobject of page
				for _,v in pairs(varSubobjs) do
					mw.smw.subobject(smw, v) -- add data to variant-subobject
				end
			end
		end
	end
	
	return tostring(ret)
end

function qty(quantity)
	-- if no quantity is given, return unknown
	if string.lower(quantity) == 'varies' then
		return 'Varies'
	end
	if not quantity or string.lower(quantity) == 'unknown' then
		return 'Unknown'
	end
	-- en dashes are the proper dash for number ranges
	-- replace all hyphens and em dashes with en
	-- strip *all* whitespace
	-- change '(noted)' to '$n' for parsing
	quantity = mw.ustring.gsub(quantity,'[-—]','–')
		:gsub('%s','')
		:gsub('%(noted%)','$n')
	-- split list into table
	local vals = mw.text.split(quantity,'[,;]')
	local low = 2147483648
	local high = 0
	-- recreate the quantity string to ensure consistent formatting
	local numstr = {}
	for i, v in ipairs(vals) do
		local clean = v:gsub('$n','')
		-- if list element contains an en dash (indicating range)
		-- Find the smaller/larger number (just in case)
		-- Compare them to the current min/max
		-- put them in order with desired format
		if mw.ustring.find(v,'–') then
			local splitvals = mw.text.split(clean,'–')
			-- assume a is smaller, b is larger
			local a = tonumber(splitvals[1])
			local b = tonumber(splitvals[2])
			-- Just in case
			if a > b then
				a,b = b,a
			end
			if a < low then
				low = a
			end
			if b > high then
				high = b
			end
			local addx = commas(a)..'–'..commas(b)
			if v:find('$n') then
				addx = addx.._noted
			end
			table.insert(numstr,addx)
		else
			local a = tonumber(clean)
			if a < low then
				low = a
			end
			if a > high then 
				high = a
			end
			local addx = commas(a)
			if v:find('$n') then
				addx = addx.._noted
			end
			table.insert(numstr,addx)
		end
	end
	-- Add a line break if there are too many elements
	-- To keep the tables thin
	if #numstr > 11 then
		local mid = math.floor(#numstr/2)
		numstr[mid] = '<br/>'..numstr[mid]
	end
	-- To prevent any possible confusion with formatted numbers
	-- elements should be separated with semicolons followed by a space
	numstr = table.concat(numstr,'; ')
	-- If no numbers are found in the string, return unknown
	if not numstr:find('%d') then
		return 'Unknown', price
	end

	return numstr, high, low
end

function get_total(value,qhigh,qlow)
	-- if no alch value is given, return unknown
	if not value or string.lower(value) == 'unknown' then
		return value
	end
	-- if value is negative (from smw/ge) it cannot be alched
	if tonumber(value) and tonumber(value) < 0 then
		return false
	end
	-- if no numbers return not alchemisable
	if not tonumber(value) and not value:find('%d') then
		return false
	end

	-- en dashes are the proper dash for number ranges
	-- replace all hyphens and em dashes with en
	-- strip *all* whitespace
	value = mw.ustring.gsub(value,'[-—]','–')
		:gsub('%s','')
	-- split list into table
	local vals = mw.text.split(value,'[,;]')
	-- All value ranges will be a range
	-- e.g. if items valued at 100 coins are dropped in quantities of 1, 3, 5
	-- the value returned will be 100–500 rather than 100; 300; 500
	-- If low and high vars are the same in the end, only 1 value is displayed
	local low = 2147483648
	local high = 0
	-- recreate the alchval string to ensure consistent formatting
	for i, v in ipairs(vals) do
		local clean = v:gsub('$n','')
		-- if list element contains an en dash (indicating range)
		-- Find the smaller/larger number (just in case)
		-- Compare them to the current min/max
		-- put them in order with desired format
		if mw.ustring.find(v,'–') then
			local splitvals = mw.text.split(clean,'–')
			-- assume a is smaller, b is larger
			local a = tonumber(splitvals[1])
			local b = tonumber(splitvals[2])
			-- Just in case
			if a > b then
				a,b = b,a
			end
			if a < low then
				low = a
			end
			if b > high then
				high = b
			end
		else
			local a = tonumber(clean)
			if a < low then
				low = a
			end
			if a > high then 
				high = a
			end
		end
	end

	local valret, sort
	if not qhigh or not qlow then
		sort = high
		valret = commas(high)
	else
		local lower = qlow * low
		local higher = qhigh * high
		if higher == lower then
			valret = commas(higher)
		else
			valret = commas(lower)..'-'..commas(higher)
		end
		sort = higher
	end

	return valret, sort
end

-- function to get the currency image and name (singular vs plural)
function currency(altcur,price,total)
	price = tostring(price)
	total = tostring(total)
	-- body
	local lowcur = string.lower(altcur)
	local clean = price:gsub('%W','')
	local retcur, img
	if tonumber(clean) and tonumber(clean) ~= 1 then
		retcur = altcur
	else
		if lowcur == 'zemomark' or lowcur == 'tokkul' or lowcur == 'teci' then
			retcur = altcur
		elseif lowcur == 'pieces of eight' then
			retcur = 'piece of eight'
		else
			retcur = string.sub(altcur,1,(string.len(altcur)-1))
		end
	end
	img = curr_image(altcur,total) or 'AltValue.png'
	img = '<span class="dropsline-altval style="margin-left:0.3em;">[[File:'..img..'|link=|frameless|20px]]</span>'
	return img, retcur
end

-- match -> category
-- pattern matching allowed
-- all lowercase
-- DO NOT INFLATE THIS TABLE
-- If a specific item does not fall into the category you want it to
-- then oh well
-- get over it
-- if items fall into a category they shouldn't, that's fine too
-- these aren't directly visible and are only used a rough sort
local sortnames = {
	runes = { ' rune$' },
	talismans = { 'talisman' },
	armour = { 'platebody', 'platelegs', 'plateskirt', 'helm', 'kiteshield', 'shield ', 'shield$', 'gloves', 'boots', 
			'chainbody', 'cape', 'cuirass', 'gauntlets' },
	weapons = { 'axe', 'sword', 'staff', 'javelin', 'bow', 'hatchet', 'wand', 'spear', 'halberd', 'claw',
			'mace', 'warhammer', 'whip', 'dagger', 'throwing', 'dart', 'hasta', 'scimitar' },
	ammunition = { 'arrow', 'bolt', 'guam tar', 'tarromin tar', 'harralander tar' },
	logs = { 'logs' },
	herbs = { 'grimy', '^clean' },
	seeds = { 'seed', 'spore' },
	spirits = {'stone spirit'},
	ores = { ' ore$', 'coal' },
	bars = { ' bar$' },
	gems = { 'uncut' },
	raw = { '^raw' },
	potions = { 'potion', 'antipoison', 'serum', 'guthix rest', 'guthix balance', 'oil', 'fishing explosive', 'super attack',
			'vial of stench', 'super energy', 'super strength', 'super restore', 'super hunter', 'super defence', 'antifire',
			'super divination', 'weapon poison', 'super runecrafting', 'zamorak brew', 'extreme hunter', 'saradomin brew',
			'super invention', 'extreme attack', 'extreme strength', 'extreme divination', 'extreme defence', 'extreme magic',
			'extreme runecrafting', 'extreme ranging', 'super prayer', 'prayer renewal', 'extreme invention', 'overload', 'mix',
			'overload salve' },
	salvage = {'salvage'}, -- don't think there's any other drops with word salvage in them
	teleport = { 'teleport' },
	godpage = { '^ancient page %d', '^bandos page', '^armadyl page', '^zamorak page', '^guthix page', '^saradomin page' }, -- God book pages
	['!!bones'] = {'bones', 'impious ashes', 'accursed ashes', 'infernal ashes', 'tortured ashes', 'searing ashes'},
	-- Sorting of common items to consistent places
	-- sort rare drop table to back
	zzz = { 'rare drop table' },
	-- sort wilderness shared loot table to back
	zzzz = { 'wilderness shared loot table' },
	-- sort coins to front
	['!!!'] = { '^coins' },
}

-- adds a category in front of the name to give it a slightly improved sorting function
function namesort(arg,rarg)
	local arg = string.lower(arg or '')
	local cat = '!other' -- "!" to keep in front

	for w, v in pairs(sortnames) do
		for _, x in ipairs(v) do
			if mw.ustring.match(arg,x) then
				cat = w
				break
			end
		end
	end

	return string.format('%s %s %s',cat,rarg,arg)
end

-- adding categories to mainspace
function categories(...)
	local name,quantity,rarity = unpack(...)
	local ret = ''
	name = name:lower()
	quantity = quantity:lower()
	if name:find('effigy') then
		ret = ret .. '[[Category:Effigy dropping monsters]]'
	elseif name:find('clue scroll') then
		ret = ret .. '[[Category:Clue scroll dropping monsters]]'
	elseif name:find('rare drop table') then
		ret = ret .. '[[Category:Monsters with access to the rare drop table]]'
	elseif name:find('wilderness shared loot table') then
		ret = ret .. '[[Category:Monsters with access to the Wilderness shared loot table]]'
	end
	if not rarities[rarity:lower()] then
		ret = ret .. '[[Category:Needs drop rarity added]]'
	end
	if quantity:find('Unknown') then
		ret = ret .. '[[Category:Needs drop quantity added]]'
	end
	return ret
end

return p
-- </nowiki>