Module:Sandbox/User:F0r F0x Sake/calctest
Documentation for this module may be created at Module:Sandbox/User:F0r F0x Sake/calctest/doc
--- this is a comment - from a -- to the end of a line
-- whatever is in a comment does nothing
--[=[
this is a block comment, from --[(any number of =)[ to ](the same number of =)]
you can use 0 but that isn't encouraged as it can be confused with wikilinks
the -- when closing isn't required but it does make it line up nicely
--]=]
-- <nowiki>
-- a commented nowiki does nothing to the code but ensures there is no false whatlinkshere
--[======[
STEP 1
declare a main variable and define it as an empty table
see also - step 3 at the end
--]======]
-- conventionally this is named p
local p = {}
--[==[
helper functions
these are up here as they generally need to be reasonably high up
--]==]
-- helper function that just gets GE prices
-- load in GE prices
-- loadData works slightly differently to require - if the relevant conditions are fulfilled, loadData should be used instead of require
local geps = mw.loadData('Module:GEPrices/data')
-- fetch the GE price out of the table or return 0 if it doesn't exist
function gep(item)
return geps[item] or 0
end
local curr = require('Module:Currency')._amount
-- a helper function to make coins images
function coins(x)
return curr(string.format('%.2f',x), 'coins')
end
-- a helper function to make coins images
local yesno = require('Module:Yesno')
-- not sure if yesno goes here in this format. Check here first if any issues arise.
local scrollInfo = {
[true] = .9,
[false] = 1
}
local maskInfo = {
[true] = .05,
[false] = 0
}
local portableInfo = {
[true] = .05,
[false] = 0
}
local broochInfo = {
[true] = .05,
[false] = 0
}
local amuletInfo = {
[true] = .05,
[false] = 0
}
local factoryInfo = {
[true] = .1,
[false] = 0
}
local desertInfo = {
[true] = .2,
[false] = 0
}
local morytaniaInfo = {
[true] = .2,
[false] = 0
}
local varanusaurInfo = {
[true] = .1,
[false] = 0
}
--[======[
STEP 2
assign functions into p
--]======]
-- `main` is a common name for the function that wikitext will call
-- the only passed input for the function called by wikitext is a Frame, often just called frame
function p.main(frame)
-- it is common to strip out the arguments from the frame, then pass those into another function
-- this is because we can then test the module in the console or without a tegamplate
-- getParent() will error if there is no parent, which is the case in the console or without a template
return p._main(frame:getParent().args)
end
-- this function is also part of p but can be called with just the arguments, i.e. in the console
function p._main(args)
-- local is the keyword to declare a variable in the current scope
local scroll, portable, brooch, factory, desert, morytania, varanusaur, mask, amulet = args.Scroll_Input, args.Portable_Input, args.Brooch_Input, args.Factory_Input, args.Desert_Input, args.Morytania_Input, args.Varanusaur_Input, args.Mask_Input, args.Amulet_Input
--declares and assigns the variables
scroll = yesno(scroll or '', false)
mask = yesno(mask or '', false)
portable = yesno(portable or '', false)
brooch = yesno(brooch or '', false)
amulet = yesno(amulet or '', false)
factory = yesno(factory or '', false)
desert = yesno(desert or '', false)
morytania = yesno(morytania or '', false)
varanusaur = yesno(varanusaur or '', false)
--uses yesno because these are checkboxes.
--building table here
local ret = makeTable(scroll, portable, brooch, factory, desert, morytania, varanusaur, mask, amulet)
-- make sure to return the relevant thing at the end
return ret
end
--[=[
data tables
--]=]
-- before moving on to the function, we're going to setup some data tables
-- again, this is usually at the top, between step 1 and 2
-- but for demonstration they're here
-- these data tables are for mapping from the values passed from the frame into the actual values we care about
-- this is the main data table containing all the information about the herbs
-- the outer table is using another alternative notation, providing no keys at all
-- this makes it behave like a standard array in other langs - the keys will begin at 1 (the number 1 not the string '1' - these are different keys!) and increment by 1 for each un-keyed item in it
local potionInfo = {
{
name = 'Attack potion', -- potion name
level = 1, -- herblore level level
experience = 25, --xp
herb = 'Guam', -- herb name
primary = '', --fill if potion uses a non-standard primary
secondary = 'Eye of newt', -- secondary ingredient
},
{
name = 'Ranging potion', -- potion name
level = 3, -- herblore level level
experience = 30, --xp
herb = 'Guam', -- herb name
primary = '', --fill if potion uses a non-standard primary
secondary = 'Redberries', -- secondary ingredient
},
-- fill in with more potions
}
function makeTable(scroll, mask, portable, brooch, amulet, factory, desert, morytania, varanusaur)
-- declare t as a mw.html object
-- mw.html is used to make html tag creation generally easier
local t = mw.html.create('table')
-- this is a method of the mw.html object
-- this is actually syntactic sugar for mw.html.addClass(t, 'wikitable'), but using that is dumb if you don't have to
t:addClass('wikitable')
-- add more classes
-- I like to have numbers aligned right except for levels, which are aligned center
-- since only 3 columns aren't aligned right, use style to align right then override with classes
t:css('text-align', 'right')
t:addClass('align-left-1 align-center-2 align-center-3 ')
-- you can chain this syntax, if the object returns a value that is an object (which all of mw.html does)
-- whitespace doesn't matter so it is good to indent to make the structure clear
t:tag('tr') -- create a table row (tr)
:tag('th') -- create a table header cell (th)
:wikitext('Icon') -- add some content to the th
:done()
:tag('th')
:wikitext('Potion')
:done()
:tag('th')
:wikitext('[[File:Herblore.png|20px|frameless|link=Herblore]] level')
:done()
:tag('th')
:wikitext('XP')
:done()
:tag('th')
:wikitext(' ')
:addClass('unsortable')
:attr('rowspan', #potionInfo+1) -- make this as long as the potionInfo table, plus 1 for the header
:done()
:tag('th')
:wikitext('Primary')
:done()
:tag('th')
:wikitext('Primary cost')
:done()
:tag('th')
:wikitext(' ')
:addClass('unsortable')
:attr('rowspan', #potionInfo+1) -- make this as long as the potionInfo table, plus 1 for the header
:done()
:tag('th')
:wikitext('Secondary')
:done()
:tag('th')
:wikitext('Secondary cost')
:done()
:tag('th')
:wikitext(' ')
:addClass('unsortable')
:attr('rowspan', #potionInfo+1) -- make this as long as the potionInfo table, plus 1 for the header
:done()
:tag('th')
-- we're not going to put the references here for now, since they are complicated
-- if you are interested I can add them later
:wikitext('Profit')
:done()
:tag('th')
:wikitext('GP/XP')
:done()
:done()
-- header is now done!
-- we can call :done() or :addDone() here but it isn't necessary since we are now done chaining, starting a new statement wil work just fine
-- now we're going to iterate the entire herbs table
-- but before that, there are a few things we'll be using over the entire iteration, so setup a few variables
-- get the compost number from the info, default to none if an invalid value was provided
-- for the calculator you shouldn't ever have an invalid value, but it is good practice to check anyway (unless you intentionally want it to script error)
local maskVal = maskInfo[mask] or 1
local factoryVal = factoryInfo[factory] or 1
local portableVal = portableInfo[portable] or 1
local amuletVal = amuletInfo[amulet] or 1
local varanusaurVal = varanusaurInfo[varanusaur] or 1
local desertVal = desertInfo[desert] or 1
local morytaniaVal = morytaniaInfo[morytania] or 1
local scrollVal = scrollInfo[scroll] or 1
local broochVal = broochInfo[brooch] or 1
-- since these are always just added together anyway
local doubleMultiplier = 1 + broochVal + maskVal + portableVal or 1
local doseMultiplier = 1 + factoryVal + amuletVal or 1
local standardMultiplier = doubleMultiplier * doseMultiplier or 1
local poisondoseMultiplier = 1 + factoryVal + amuletVal + varanusaurVal or 1
local poisonMultiplier = poisondoseMultiplier * doubleMultiplier or 1
local morytaniadoseMultiplier = 1 + factoryVal + amuletVal + morytaniaVal or 1
local desertdoseMultiplier = 1 + factoryVal + amuletVal + desertVal or 1
local morytaniaMultiplier = morytaniadoseMultiplier * doubleMultiplier or 1
local desertMultiplier = desertdoseMultiplier * doubleMultiplier or 1
-- ok now we can begin the iteration
-- this uses a common form of the for statement
-- you can read this as "declare the variables i and v; for every item in the herbsInfo table in order, assign the key to i and the value to v, then execute the code inside"
-- if that doesn't make sense, don't worry - if you've never encountered loops before, they can be a brainbender
for i,v in ipairs(potionInfo) do
-- create a tr inside the return table, and store it
local tr = t:tag('tr')
-- there's a number of things happening right here
-- 1) get the herbname from the current herb table we're looking at, and use that if it exists
-- 2) if herbname doesn't exist it will be nil and thus we trigger the other side of the or
-- 3) we take the string 'Grimy ' (with a space) and concatenate it (the .. operator) to
-- 4) the name from the current herb table, lowercased - if we know that the variable is a string, we can do the : syntactic sugar like on mw.html objects (this is equivalent to string.lower(v.name))
local potion = v.name
-- we'll use these a few times, so lets set up some variables for prices
-- seed price: 2 steps of overrides - an explicit seedprice
-- if not set, find the gep of the seed name
-- if not set, find the gep of the herb name + seed
local herbprice = v.herbprice or gep('Clean '..v.herb)
local primaryprice = v.primaryprice or gep(v.primary)
-- can add fixed primary price if that applies to a given potion.
local potionprice = v.potionprice or gep(v.potion)
local secondaryprice = v.secondaryprice or gep(v.secondary)
-- calculate the various values
-- just define these important variables here, fill in later - these go into the table
local scrollsecondaryprice, totalprice, moddedvalue, profit, gpxp
scrollsecondaryprice = secondaryprice * scrollVal
totalprice = herbprice + scrollsecondaryprice
moddedvalue = potionprice * standardMultiplier
-- need to add here the conditions for the desert/mory/poison stuff.
profit = moddedvalue - totalprice
gpxp = profit / v.experience
tr :tag('td') -- td tag is a normal cell
:wikitext(string.format('[[File:%s_(3).png|link=%s]]', potion, potion)) -- file cell
-- this uses string.format, which is a very useful function to prevent repeatedly concatenating strings (which can be a slow operation)
-- essentially, each time you see %s in the string, that will be replaced by the corresponding value from the rest of the arguments (in this case we have 2 %s's and they're replaced by the value of gherb both times)
-- string.format has a lot more complexities but that is a task for the full documentation to explain
:done()
:tag('td')
:wikitext('[['..potion..']]') -- could use format but for 2 concats/1 subst, probably doesn't matter enough
:done()
-- nothing special about these
:tag('td')
:wikitext(v.level)
:done()
:tag('td')
:wikitext(v.experience)
:done()
:tag('td')
:wikitext('[[File:Herblore.png|20px|frameless|link=Herblore]] level')
:done()
:tag('td')
:wikitext(herbprice)
-- pass this in to the coins helper function defined below, so that we get the value formatted with the coins image
:done()
:tag('td')
:wikitext('[[File:Herblore.png|20px|frameless|link=Herblore]] level')
:done()
:tag('td')
:wikitext(secondaryprice)
:done()
:tag('td')
:wikitext(profit)
:done()
:tag('td')
:wikitext(gpxp)
:done()
end
-- NB ipairs is used to loop array-tables where the keys are integers from 1 to n with no gaps
-- to loop a map-table, use pairs
-- be aware that ipairs guarantees the order is preserved, while pairs does not guarantee an order
-- make sure to return the table we created
return t
-- we can call tostring on t if we wanted to, but this will be done automatically when going back from lua to wikitext
end
--[======[
STEP 3
return the main variable
--]======]
return p
-- in modern mediawiki, nowiki needs to be closed to be effective
-- </nowiki>