Module:DropsLineMulti

From WIDEVERSE Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:DropsLineMulti/doc

-- <nowiki>
local p = {}

local VariablesLua = mw.ext.VariablesLua
local dropsline = require('Module:DropsLine')
local params = require('Module:Paramtest')
local yesno = require('Module:Yesno')

local geprices_data = mw.loadData('Module:GEPrices/data')
local highalch_data = mw.loadData('Module:GEHighAlchs/data')
local multidata = mw.loadData('Module:DropsLineMulti/data')
local multidrops = multidata.all

local ptitle = mw.title.getCurrentTitle()
local ns = ptitle.nsText
local title = ptitle.fullText
local _membs = '&nbsp;<sub title="Members only" style="cursor:help; margin-left:3px;">(m)</sub>'
local _nmnote = 'One of the following items is dropped: '
local _defrarnote = 'The chance of rolling a %s drop is %s, and the chance of getting a particular item is %s.'

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 }
}

function p.main(frame)
	local args = frame:getParent().args
	local tempArgs = frame.args
	
	if not params.has_content(tempArgs.dtype) then
		tempArgs.dtype = 'drop'
	end
	
	local dropnm = string.lower(args.Name or args[1] or '')
	local drop = multidrops[dropnm]
	if not drop then
		return error('Drop type not found. See [[Template:DropsLineMulti]] for valid types, and instructions on adding one.')
	end
	
	-- Params and defaults
	local namenotes,
		quantity,quantitynotes,
		rarity,raritynotes,memsover,
		altcur,monVers,altSource = params.defaults{
					{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 name = drop.name
	local altname = drop.alt or name
	altname = params.default_to(args.Alt,altname)
	local gemwname = name
	local _smwname = params.default_to(args.smwname,gemwname)
	local isCoins = false
	--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
    
    if args.dtype and params.has_content(args.dtype) then
    	tempArgs.dtype = args.dtype
    end
	
	local rarity_value
	if rarities[rarity:lower()] then
		rarity = params.ucflc(rarity)
	else
		rarity_value = expr(rarity)
	end
	
	 -- Generate rarity note
    local ad_raritynote
    local note_rare = rarity
    local note_item_rare = rarity_value or rarity
    if rarity_value and tonumber(rarity_value) then
    	local fract = mw.text.split(rarity,'%s*/%s*')
    	local denom
    	local itemcnt = 0
    	for i,v in ipairs(drop.items) do
    		itemcnt = itemcnt + 1
    	end
    	if fract[2] then
    		denom = fract[2] * itemcnt
    	else
    		denom = itemcnt
    	end
    	local def_rarity = fract[1]..'/'..denom
    	local calced_rarity = tonumber(rarity_value) / itemcnt
    	local oneover_rarity = sigfigalt(1/calced_rarity)
    	local perc_rarity = sigfig(100 * calced_rarity, 3)
    	note_item_rare = string.format('%s (1/%s, %s%%)', def_rarity, oneover_rarity, perc_rarity)
    end
    if drop.note then
    	ad_raritynote = string.format(drop.note, note_rare, note_item_rare)
    else
    	ad_raritynote = string.format(_defrarnote, name, note_rare, note_item_rare)
    end
    if ad_raritynote ~= '' then
	    ad_raritynote = frame:callParserFunction{ name = '#tag:ref', args = {
			ad_raritynote, name = 'dlm-r '..drop.name, group = 'd'
		} }
		VariablesLua.vardefine( '_refs_used_', 'true' )
	end
    raritynotes = raritynotes .. ad_raritynote
    
    -- Generate items note
    local itlinks = {}
    for i,v in ipairs(drop.items) do
    	local valt = params.default_to(v.alt,v.name)
    	table.insert(itlinks, string.format('[[%s|%s]]', v.name, string.lower(valt)))
    end
    local ad_namenote = _nmnote .. table.concat(itlinks, ', ') .. '.'
    if ad_namenote ~= _nmnote then
	    ad_namenote = frame:callParserFunction{ name = '#tag:ref', args = {
			ad_namenote, name = 'dlm-n '..drop.name, group = 'd'
		} }
		namenotes = namenotes .. ad_namenote
	    VariablesLua.vardefine( '_refs_used_', 'true' )
    end

	quantity = mw.ustring.lower(quantity)
	local alch = false
	local gemw = false
	
	local valueInfo = {
        alch = {
            has = false,
            value = 0,
            from = nil
        },
        ge = {
            has = false,
            value = 0,
            from = nil
        }
    }
    if drop.value then
    	valueInfo = {
	        alch = {
	            has = true,
	            value = drop.value,
	            from = 'alt'
	        },
	        ge = {
	            has = true,
	            value = drop.value,
	            from = 'alt'
	        }
	    }
	    alch = true
	    gemw = true
    end
    
    -- Check members or F2P
	local members = false
    if params.has_content(memsover) then
        -- overridden
        members = yesno(memsover)
    elseif drop.members ~= nil then
    	members = drop.members
    end

	-- Add to name of item if members item
	if members == true then
		imgmembs = _membs
	else
		imgmembs = ''
	end
	
	-- Image
	local image_n = mw.ustring.gsub(drop.icon, '#.+$', '.png')
	local image
	if image_n:lower() == 'no' or params.is_empty(drop.icon) 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 for the constituent items
	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
	
	-- Level for Fishing, Archaeology, Mining, Woodcutting and Divination
	local level = 0
	if drop.level then
		level = drop.level
	end
	
	-- SMW Drops so they display on pages etc, values
	local totge, totalch, totvals = 0,0,0
	local subrows = {}
	for _,v in ipairs(drop.items) do
		local tmpdrop = smwdrop(v, drop, tempArgs, {
			['monVers'] = monVers,
			['altSource'] = altSource,
			rarity=rarity,
			raritynotes=raritynotes,
			['rarity_value']=rarity_value,
			quantity=quantity,
			memsover=memsover,
			smwjson = smwjson
		})
		if tmpdrop.alch and tmpdrop.alch > 0 then
			totalch = totalch + tmpdrop.alch
			if tmpdrop.ge and tmpdrop.ge > 0 then
				totge = totge + tmpdrop.ge
			else
				totge = totge + tmpdrop.alch
			end
			totvals = totvals + 1
		end
		local tmpline = tostring(tmpdrop.line)
		table.insert(subrows, tmpline)
	end
	if totalch > 0 then
		local alchval = math.floor(totalch / totvals)
		local geval = math.floor(totge / totvals)
    	valueInfo = {
	        alch = {
	            has = true,
	            value = alchval,
	            from = 'alt'
	        },
	        ge = {
	            has = true,
	            value = geval,
	            from = 'alt'
	        }
	    }
	    alch = true
	    gemw = true
    end
    
    -- Rarity pref
    local rarePref = ''
	if args.approx and yesno(args.approx) then
		rarePref = '<span title="Approximate">~</span> '
	end
    
    -- Class for row
    local rowClass = 'dlm-drop dlm-'..string.gsub(drop.name, '%A', '_')
	
	-- Table display row, no smw
	local dlsmwjson = false
	local ret = ''
	local ret =  dropsline._main{ name,
			altname,namenotes,
			quantity,quantitynotes,rarePref,
			rarity,rarity_value,raritynotes,
			image,members,
			valueInfo,gemw,alch,alt,
			isCoins,
			tempArgs,monVers,
			cleanedName,dropVers,level,rowClass,
			smwname,dlsmwjson }

	-- categories for mainspace
	local cats = ''
	if ns == '' then
		if drop.cats then
			cats = cats .. drop.cats
		end
		if quantity:find('Unknown') then
			ret = ret .. '[[Category:Needs drop quantity added]]'
		end
	end
	
	subrows = table.concat(subrows, '\n')
	return ret..subrows..cats
end

function p.list(frame)
	local args = frame:getParent().args
	
	local dlines = {'{{DropsTableHead}}'}
	local grouptbl = mw.html.create( 'table' )
	grouptbl:addClass('wikitable')
		:tag('tr')
			:tag('th'):wikitext('Name'):done()
			:tag('th'):wikitext('Aliases'):done()
		:done()
	
	for i,v in pairs(multidata.noaliases) do
		local v_alias = ''
		for j,k in ipairs(v.alias) do
			v_alias = v_alias .. '<code>'..k..'</code> '
		end
		grouptbl:tag('tr')
			:tag('td')
				:tag('code'):wikitext(i):done()
			:done()
			:tag('td')
				:wikitext(v_alias)
			:done()
		
		local dline = string.format('{{DropsLineMulti|Name=%s|Rarity=1/100|Quantity=1}}', i)
		table.insert(dlines, dline)
	end
	table.insert(dlines, '{{DropsTableBottom}}')
	local dlinestr = frame:preprocess( table.concat(dlines, '') ) 
	
	local ret = mw.html.create( 'div' )
	ret:wikitext('The following is a list of available drop groups:')
		:newline()
		:node( grouptbl )
		:newline()
		:wikitext('Resulting dropslines:')
		:newline()
		:wikitext( dlinestr )
	
	return ret
end

-- Function to generate smw data per dropped item
function smwdrop(ditem, group, tempArgs, parsed)
	-- Params and defaults
	local name,namenotes,
		quantity,quantitynotes,
		raritynotes,memsover = params.defaults{
					{ditem.name,'Item'},
					{ditem.namenotes,''},
					{ditem.quantity, '1'},
					{ditem.quantitynotes,''},
					{ditem.raritynotes,''},
					{ditem.members,''},
				}
	local altname = params.default_to(ditem.alt,name)
	local rarity = ditem.rarity or group.rarity or 'auto'
	local gemwname = params.default_to(ditem.gemwname,name)
	local _smwname = params.default_to(ditem.smwname,gemwname)
	local isCoins = false
	-- Inherited
	local altSource = parsed.altSource
	local monVers = parsed.monVers
	
	-- 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
	local numitems = 0
	for i,v in ipairs(group.items) do
		numitems = numitems + 1
	end
	local baserarity = false
	if string.lower(rarity) == 'base' then
		baserarity = true
	end
	if rarities[rarity:lower()] then
		rarity = params.ucflc(rarity)
	elseif string.lower(rarity) == 'auto' then
		if rarities[parsed.rarity:lower()] then
			rarity = params.ucflc(parsed.rarity)
		else
			rarity_value = 1 / numitems
			rarity = '1/'..numitems
		end
	elseif baserarity then
		rarity = parsed.rarity
		rarity_value = parsed.rarity_value
	else
		rarity_value = expr(rarity)
	end
	if parsed.rarity_value and rarity_value and baserarity == false then
		local parent = tonumber(parsed.rarity_value)
		if parent then
			rarity_value = rarity_value * parent
			local fract = mw.text.split(rarity,'%s*/%s*')
			local denom
			if fract[2] then
	    		denom = fract[2] * (1 / parent)
	    	else
	    		denom = (1 / parent)
	    	end
	    	rarity = fract[1]..'/'..denom
		end
	end
	raritynotes = raritynotes..parsed.raritynotes
	
	-- Calculate quantities
	quantity = mw.ustring.lower(quantity)
	if parsed.quantity == 'unknown' then
		quantity = 'unknown'
	elseif parsed.quantity == 'varies' then
		quantity = 'varies'
	elseif parsed.quantity ~= '1' then
		 quantity = calcqty(quantity, parsed.quantity)
	end
	
	local gemw = yesno(ditem.gemw or group.gemw or 'yes', false)
	local alch = yesno(ditem.alch or group.alch or 'yes', false)

	-- Test for existance of alch value
    local valueInfo = {
        alch = {
            has = false,
            value = 0,
            from = nil
        },
        ge = {
            has = false,
            value = 0,
            from = nil
        }
    }
    
    -- Bulk sources
    local geprice_frombulk = geprices_data[gemwname]
    if not (type(geprice_frombulk) == 'number' and geprice_frombulk > 0) then
    	geprice_frombulk = nil
    end
	
	if alch then
		local alch_frombulk = highalch_data[gemwname]
		if not (type(alch_frombulk) == 'number' and alch_frombulk > -1) then
			alch_frombulk = nil
		end
    	if alch_frombulk ~= nil then
    		valueInfo.alch = {
    			has = true,
    			value = alch_frombulk,
    			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 ditem.AltValue then
                --AltValue set, no alch from GE or SMW, use alt as regular alch
                valueInfo.alch = {
                    has = true,
                    value = tonumber(ditem.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 ditem.AltValue then
            --Altvalue set, no alch value
            valueInfo.alch = {
                has = true,
                value = tonumber(ditem.AltValue),
                from = 'alt'
            }
        end
    end
    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 ditem.AltValue then
        valueInfo.ge = {
            has = true,
            value = tonumber(ditem.AltValue),
            from = 'alt'
        }
    elseif alch then
        valueInfo.ge = {
            has = true,
            value = valueInfo.alch.value,
            from = 'alch'
        }
    end
    
	-- Check members or F2P
	local members = false
    if params.has_content(memsover) then
        -- overridden
        members = yesno(memsover)
    elseif params.has_content(parsed.memsover) then
        -- overridden by parent
        members = yesno(parsed.memsover)
    elseif group.members ~= nil then
    	members = group.members
    else
        local smwret = getSMWInfo(smwname)
        if smwret and smwret.members ~= nil then
            members = smwret.members
        end
    end
	
	-- Add to name of item if members item
	if members == true then
		imgmembs = _membs
	else
		imgmembs = ''
	end
    
    local image,image_n
	if ditem.image then
		image_n = ditem.image
	else
		image_n = name .. '.png'
		image_n = mw.ustring.gsub(image_n, '#.+$', '.png')
	end
	if image_n:lower() == 'no' or params.is_empty(image_n) 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
	
	-- Level for Fishing, Archaeology, Mining, Woodcutting and Divination
	local level = 0
	if ditem.Level and tonumber(ditem.Level, 10) then
		level = tonumber(ditem.Level, 10)
	elseif group.Level and tonumber(group.Level, 10) then
		level = tonumber(group.Level, 10)
	end
	
	-- Class for row
	local rowClass = 'dlm-sub-drop table-na expand-child dlm-'..string.gsub(group.name, '%A', '_')
	
	local tmpline = dropsline._main{ name,
			altname,namenotes,
			quantity,quantitynotes,rarePref,
			rarity,rarity_value,raritynotes,
			image,members,
			valueInfo,gemw,alch,alt,
			isCoins,
			tempArgs,monVers,
			cleanedName,dropVers,level,rowClass,
			smwname,parsed.smwjson }
	-- mw.log(tmpline)
	return { alch = valueInfo.alch.value, ge = valueInfo.ge.value, line = tmpline }
end

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+0.5)
	else
		return sigfig(n, 3)
	end
end

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

function calcqty(quantity, parent)
	-- 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
	
	-- Get parent value
	-- en dashes are the proper dash for number ranges
	-- replace all hyphens and em dashes with en
	-- strip *all* whitespace
	parent = mw.ustring.gsub(parent,'[-—]','–')
		:gsub('%s','')
	
	-- check if noted then remove text
	local noted = false
	if mw.ustring.find(parent,'%(noted%)') then
		noted = true
		parent = mw.ustring.gsub(parent,'%(noted%)','$n')
	end
	
	-- get parent amounts
	local par_vals = mw.text.split(quantity,'[,;]')
	local par_low = 2147483648
	local par_high = 0
	local multipliers = {}
	for _, v in ipairs(par_vals) do
		if mw.ustring.find(v,'–') then
			local splitvals = mw.text.split(v,'–')
			-- 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 < par_low then
				low = a
			end
			if b > par_high then
				high = b
			end
		else
			local num = tonumber(v)
			if num then
				table.insert(multipliers, num)
			end
		end
	end
	local range = false
	if par_low < 2147483648 and par_high > 0 then
		range = true
	end
	
	-- en dashes are the proper dash for number ranges
	-- replace all hyphens and em dashes with en
	-- strip *all* whitespace
	quantity = mw.ustring.gsub(quantity,'[-—]','–')
		:gsub('%s','')
	-- split list into table
	local vals = mw.text.split(quantity,'[,;]')
	local rettbl = {}
	for i, v in ipairs(vals) do
		local inote = mw.ustring.find(v,'%(noted%)')
		local clean = mw.ustring.gsub(v,'%(noted%)','')
		
		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 range then
				local reta = par_low * a
				local retb = par_high * b
				table.insert(rettbl, reta..'-'..retb)
			end
			for j,k in ipairs(multipliers) do
				local reta = a * k
				local retb = b * k
				table.insert(rettbl, reta..'-'..retb)
			end
		else
			local a = tonumber(clean)
			if range then
				local reta = par_low * a
				local retb = par_high * a
				table.insert(rettbl, reta..'-'..retb)
			end
			for j,k in ipairs(multipliers) do
				local reta = a * k
				table.insert(rettbl, reta)
			end
		end
	end
	
	-- Make string again
	ret = table.concat(rettbl,'; ')
	-- If no numbers are found in the string, return unknown
	if not ret:find('%d') then
		return 'Unknown'
	end

	return ret
end

return p