Module:Exchange
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Exchange/doc
--[[ {{Helper module|name=Exchange |fname1=_price(arg) |ftype1=String |fuse1=Gets the current median price of item named arg |fname2=_value(arg) |ftype2=String |fuse2=Gets the value of item named arg }} --]] -- <pre> -- -- Implements various exchange templates -- See Individual method docs for more details -- -- See also: -- - [[Module:ExchangeData]] -- - [[Module:ExchangeDefault]] -- local p = {} -- only load commonly used modules here local yesno = require( 'Module:Yesno' ) local addcommas = require( 'Module:Addcommas' )._add -- map redirects to their correct pages local geRedirects = { ['1/2 anchovy pizza'] = '½ anchovy pizza', ['1/2 meat pizza'] = '½ meat pizza', ["1/2 p'apple pizza"] = "½ p'apple pizza", ['1/2 plain pizza'] = '½ plain pizza', ['1/3 evil turnip'] = '⅓ evil turnip', ['1/3 blue blubber jellyfish'] = '⅓ blue blubber jellyfish', ['1/3 green blubber jellyfish'] = '⅓ green blubber jellyfish', ['2/3 cake'] = '⅔ cake', ['2/3 chocolate cake'] = '⅔ chocolate cake', ['2/3 evil turnip'] = '⅔ evil turnip', ['2/3 blue blubber jellyfish'] = '⅔ blue blubber jellyfish', ['2/3 green blubber jellyfish'] = '⅔ green blubber jellyfish' } -- -- Makes sure first letter of item is uppercase -- Automatically handles any redirects -- local function checkTitle( item ) -- upper case first letter to make sure we can find a valid item page item = mw.text.split( item, '' ) item[1] = mw.ustring.upper( item[1] ) item = table.concat( item ) -- automatically handle redirects if geRedirects[item] ~= nil then item = geRedirects[item] end return item end -- -- Simple mw.loadData wrapper used to access data located on module subpages -- -- @param item {string} Item to retrieve data for -- @return {table} Table of item data -- local function load( item ) item = checkTitle( item ) local noErr, ret = pcall( mw.loadData, 'Module:Exchange/' .. item ) if noErr then return ret end error( ret ) end -- -- Returns the price of an item -- -- @param item {string} Item to get current price of -- @param multi {number} (optional) Multiplies the output price by the specified number -- @param format {boolean} (optional) Format the result with commas (defaults to false) -- @param round {number} (optional) Round the result to a number of decimal places -- @return {number|string} Price of item. Will return a string if formatted, else a number. -- function p._price( item, multi, format, round ) local price = load( item ).price local multi = type( multi ) == 'number' and multi or 1 local format = type( format ) == 'boolean' and format or false local ret = price * multi -- round the number to X d.p. if round ~= nil then local multi = 10^( round ) ret = math.floor( ret * multi + 0.5 ) / multi end if format then return addcommas( ret ) end return ret end -- -- Returns the limit of an item -- -- @param item {string} Item to get the limit of -- @return {number} Limit of item -- function p._limit( item ) return load( item ).limit end -- -- Returns the value of an item -- -- @param item {string} Item to get the value or -- @return {number} Value of item -- function p._value( item ) return load( item ).value end -- -- Calculates the difference between the current price and the last price of an item -- -- @param item {string} Item to calculate price difference for -- @param format {boolean} `true` if the output is to be formatted with commas -- Defaults to `false` -- @return {number|string} The price difference as a number -- If `format` is set to `true` then this returns a string -- If either of the prices to calculate the diff from are unavailable, this returns `0` (number) -- function p._diff( item, format ) local data = load( item ) local diff = 0 if data.price and data.last then diff = data.price - data.last if format then diff = addcommas( diff ) end end return diff end -- -- {{GEItem}} internal method -- -- @todo merge into p.table -- -- @param item {string} Item to get data for -- @return {string} -- function p._table( item ) -- load data and any required modules local item = checkTitle( item ) local data = load( item ) local timeago = require( 'Module:TimeAgo' )._ago local changeperday = require( 'Module:ChangePerDay' )._change -- set variables here to make the row building easier to follow local div = '<i>Unknown</i>' local limit = data.limit and addcommas( data.limit ) or '<i>Unknown</i>' local members = '<i>Unknown</i>' if data.last then local link = 'http://services.runescape.com/m=itemdb_rs/viewitem.ws?obj=' .. data.itemId local change = math.abs( changeperday( {data.price, data.last, data.date, data.lastDate} ) ) if data.price > data.last then arrow = '[[File:Up.png|link=' .. link .. ']]' elseif data.price < data.last then arrow = '[[File:Down.png|link=' .. link .. ']]' else arrow = '[[File:Unchg.png|link=' .. link .. ']]' end if change >= 0.04 then arrow = arrow .. arrow .. arrow elseif change >= 0.02 then arrow = arrow .. arrow end div = mw.html.create( 'div' ) :css( 'white-space', 'nowrap' ) :wikitext( arrow ) div = tostring( div ) end if data.members == true then members = '[[File:P2P icon.png|30px|link=Members]]' elseif data.members == false then members = '[[File:F2P icon.png|30px|link=Free-to-play]]' end -- build table row local tr = mw.html.create( 'tr' ) :tag( 'td' ) :wikitext( '[[File:' .. item .. '.png|' .. item .. ']]' ) :done() :tag( 'td' ) :css( { ['width'] = '15%', ['text-align'] = 'left' } ) :wikitext( '[[' .. item .. ']]' ) :done() :tag( 'td' ) :wikitext( addcommas( data.price ) ) :done() :tag( 'td' ) :wikitext( div ) :done() if data.alchable == nil or yesno( data.alchable ) then local low, high = '<i>Unknown</i>', '<i>Unknown</i>' if data.value then low = addcommas( math.floor( data.value * 0.4 ) ) high = addcommas( math.floor( data.value * 0.6 ) ) end tr :tag( 'td' ) :wikitext( low ) :done() :tag( 'td' ) :wikitext( high ) :done() else tr :tag( 'td' ) :attr( 'colspan', '2' ) :wikitext( '<i>Cannot be alchemised</i>' ) :done() end tr :tag( 'td' ) :wikitext( limit ) :done() :tag( 'td' ) :wikitext( members ) :done() :tag( 'td' ) :css( 'white-space', 'nowrap' ) :wikitext( '[[Exchange:' .. item .. '|view]]' ) :done() :tag( 'td' ) :css( 'font-size', '85%' ) :wikitext( timeago{data.date} ) :done() return tostring( tr ) end -- -- {{GEExists}} -- function p.exists( frame ) local args = frame:getParent().args local item = checkTitle( args[1] or '' ) local noErr, data = pcall( mw.loadData, 'Module:Exchange/' .. item ) if noErr then return '1' end return '0' end -- -- GEExists for modules -- function p._exists( arg ) local item = checkTitle( arg or '' ) local noErr, data = pcall( mw.loadData, 'Module:Exchange/' .. item ) if noErr then return true end return false end -- -- Internal method for p.highAlchTable, p.lowAlchTable and p.genStoreTable -- -- @param item {string} The name of the item -- @param data {table} The item's ge data -- @param alch {number} The item's alch/sell value -- @param min {number} (optional) Sets the cap for amount of items that can be converted to gp per hour -- @param natPrice {number} (optional) Sets the price of a Nature rune (set to `0` by `p.genStoreTable`) -- local function alchTable( item, data, alch, min, natPrice ) local timeago = require( 'Module:TimeAgo' )._ago local round = require( 'Module:Number' )._round -- gen store doesn't need a nat price as it's not used -- therefore we'd set it to 0 local natPrice = natPrice or load( 'Nature rune' ).price local profit = alch - data.price - natPrice local image = '[[File:' .. item .. '.png|' .. item .. ']]' local itemStr = '[[' .. item .. ']]' local priceStr = addcommas( data.price ) local alchStr = addcommas( alch ) local profitStr = addcommas( profit ) local roi = tostring( round( ( profit / ( data.price + natPrice ) * 100 ), 1 ) ) .. '%' local limit = data.limit and addcommas( data.limit ) or '<i>Unknown</i>' local maxProfit = '<i>Unknown</i>' local members = '<i>Unknown</i>' local members_sortkey = 2 local details = '[[Exchange:' .. item .. '|view]]' local lastUpdated = timeago{data.date} if data.limit then -- cap at 4800, the maximum number of alchs that can be cast every 4 hours -- varies for general store rows min = min or 4800 min = ( data.limit > min ) and min or data.limit maxProfit = addcommas( min * profit ) end mw.log( maxProfit ) if data.members == true then members = '[[File:P2P icon.png|30px|link=Members]]' members_sortkey = 1 elseif data.members == false then members = '[[File:F2P icon.png|30px|link=Free-to-play]]' members_sortkey = 0 end local tr = mw.html.create( 'tr' ) :tag( 'td' ) :wikitext( image ) :done() :tag( 'td' ) :css( { width = '15%', ['text-align'] = 'left' } ) :wikitext( itemStr ) :done() :tag( 'td' ) :wikitext( priceStr ) :done() :tag( 'td' ) :wikitext( alchStr ) :done() :tag( 'td' ) :wikitext( profitStr ) :done() :tag( 'td' ) :wikitext( roi ) :done() :tag( 'td' ) :wikitext( limit ) :done() :tag( 'td' ) :wikitext( maxProfit ) :done() :tag( 'td' ) :wikitext( members ) :attr('data-sort-value', members_sortkey) :done() :tag( 'td' ) :css( 'white-space', 'nowrap' ) :wikitext( details ) :done() :tag( 'td' ) :css( 'font-size', '85%' ) :wikitext( lastUpdated ) :done() return tostring( tr ) end -- -- {{HighAlchTableRow}} -- -- @example {{HighAlchTableRow|<item>}} -- function p.highAlchTable( frame ) local args = frame:getParent().args local item = checkTitle( args[1] ) local data = load( item ) local alch = math.floor( data.value * 0.6 ) return alchTable( item, data, alch ) end -- -- {{LowAlchTableRow}} -- -- @example {{LowAlchTableRow|<item>}} -- function p.lowAlchTable( frame ) local args = frame:getParent().args local item = checkTitle( args[1] ) local data = load( item ) local alch = math.floor( data.value * 0.4 ) return alchTable( item, data, alch ) end -- -- {{GenStoreTableRow}} -- -- @example {{GenStoreTableRow|<item>}} -- function p.genStoreTable( frame ) local args = frame:getParent().args local item = checkTitle( args[1] ) local data = load( item ) local alch = math.floor( data.value * 0.3 ) return alchTable( item, data, alch, 50000, 0 ) end -- -- {{GEP}} -- {{GEPrice}} -- -- @example {{GEPrice|<item>|<format>|<multi>}} -- @example {{GEPrice|<item>|<multi>}} -- @example {{GEP|<item>|<multi>}} -- function p.price( frame ) -- usage: {{foo|item|format|multi}} or {{foo|item|multi}} local args = frame.args local pargs = frame:getParent().args local item = pargs[1] local expr = mw.ext.ParserFunctions.expr local round = tonumber( pargs.round ) if item then item = mw.text.trim( item ) else error( '"item" argument not specified', 0 ) end -- default to formatted for backwards compatibility with old GE templates local format = true local multi = 1 -- format is set with #invoke -- so set it first to allow it to be overridden by template args if args.format ~= nil then format = yesno( args.format ) end if tonumber( pargs[2] ) ~= nil then multi = tonumber( pargs[2] ) -- indicated someone is trying to pass an equation as a mulitplier -- known use cases are fractions, but pass it to #expr to make sure it's handled correctly elseif pargs[2] ~= nil and mw.ustring.find( pargs[2], '[/*+-]' ) then multi = tonumber( expr( pargs[2] ) ) -- uses elseif to prevent something like {{GEP|Foo|1}} -- causing a formatted output, as 1 casts to true when passed to yesno elseif type( yesno( pargs[2] ) ) == 'boolean' then format = yesno( pargs[2] ) if tonumber( pargs[3] ) ~= nil then multi = tonumber( pargs[3] ) end end return p._price( item, multi, format, round ) end -- -- {{GEItem}} -- -- @example {{GEItem|<item>}} -- function p.table( frame ) local args = frame:getParent().args local item = args[1] if item then item = mw.text.trim( item ) else error( '"item" argument not specified', 0 ) end return p._table( item ) end -- -- experimental limit method for [[Grand Exchange/Buying Limits]] -- function p.gemwlimit( frame ) local item = frame:getParent().args[1] local data = mw.loadData( 'Module:Exchange/' .. item ) return data.limit end -- -- {{ExchangeItem}} -- {{GEDiff}} -- {{GELimit}} -- {{GEValue}} -- {{GEId}} -- -- @example {{ExchangeItem|<item>}} -- @example {{GEDiff|<item>}} -- @example {{GELimit|<item>}} -- @example {{GEValue|<item>}} -- @example {{GEId|<item>}} -- function p.view( frame ) local fargs = frame.args local pargs = frame:getParent().args local item = pargs[1] or fargs.item local view = fargs.view or '' local loadView = {limit=true, value=true, itemId=true, members=true, category=true, examine=true, alchable=true} if item then item = mw.text.trim( item ) else error( '"item" argument not specified', 0 ) end view = mw.ustring.lower( view ) if view == 'itemid' then view = 'itemId' end if view == 'diff' then return p._diff( item ) elseif loadView[view] then return load( item )[view] else local default = require( 'Module:ExchangeDefault' ) -- handle redirects and casing of item before passing it on item = checkTitle( item ) return default.main( item ) end end return p