Module:Minion

-- local p = {}

local getArgs = require('Module:Arguments').getArgs local slot = require( Module:Inventory slot ).slot local craftingTable = require( Module:UI ).craftingTable local formatResList = require('Module:List')._resourceList local string = require(Module:String) local _error = string._error local table = require(Module:Table) local colorText = require('Module:Color')._colorTemplates local yesno = require(Module:Yesno) local bazaar = require(Module:Bazaar) local formatCoins = require('Module:Currency')._coins local processResource = require('Module:Item')._resourceDisplay local processItem = require('Module:Item')._itemDisplay

local minionData = mw.loadData('Module:Minion/Data')

local TIERS = { "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII" }

local function _actionsPerMinute(minion, tier) -- TBA*2 = SECONDS_PER_FULL_ACTIONS : Each action either picks up or places, so 2 actions are needed for every collection. -- 60/SECONDS_PER_FULL_ACTIONS = ACTIONS_PER_MINION : This gives us a number that can be used as a multiplier to find all other action/time related questions. return 60/(minionData[minion].stats[tier].tba*2) end

local lang = mw.getContentLanguage local title = mw.title.getCurrentTitle.text

-- utility: peakStat local function peakStat(name) if not minionData[name] then error('Invalid minion: '..name) end highestTier = table.length(minionData[name].stats) for x = #TIERS, 1, -1 do		if minionData[name].stats[x].crafting then highestCraftable = x		end if highestCraftable then break end end return highestTier, highestCraftable end

p.peakStat = peakStat

-- Template:GetMinionData -- -- Returns a specified peice of data from the /Data module based on inputs

function p.getMinionData(frame) local args = getArgs(frame) local minion = args[1] or args["minion"] or args["m"] local tier = args["tier"] or args["t"] local data = args[2] or args["data"] or args["d"] local item = args[3] or args["i"] or args["item"] if not minion then return _error('Inputs expected, got none') end if not data then return _error('Invalid Argument to #2: paramater "data" is required.') end if not tier and minion:match('(.+) ([%dIVXivx]+)') then minion, tier = minion:match('(.+) ([%dIVXivx]+)') else tier = tier minion = minion end return p._getMinionData(minion, data, tier, item) end

function p._getMinionData(minionName, data, tier, item) local minionData = minionData[minionName] local aliases = { ['s']='storage'; ['str']='storage'; ['store']='storage'; ['storage']='storage'; ['tba']='time between actions'; ['time b a']='time between actions'; ['time betw a']='time between actions'; ['time between a']='time between actions'; ['time b act']='time between actions'; ['time b actions']='time between actions'; ['time betw act']='time between actions'; ['time between act']='time between actions'; ['time between actions']='time between actions'; ['time between action']='time between actions'; ['ci']='crafting item'; ['c item']='crafting item'; ['cra i']='crafting item'; ['craft i']='crafting item'; ['cra item']='crafting item'; ['craft item']='crafting item'; ['crafting item']='crafting item'; ['cn']='crafting number'; ['cra n']='crafting number'; ['craft n']='crafting number'; ['crafting n']='crafting number'; ['cra n']='crafting number'; ['cra num']='crafting number'; ['cra number']='crafting number'; ['c n']='crafting number'; ['c num']='crafting number'; ['c number']='crafting number'; ['craft num']='crafting number'; ['crafting num']='crafting number'; ['crafting number']='crafting number'; ['desc']='description'; ['d']='description'; ['descr']='description'; ['descrip']='description'; ['description']='description'; ['ai']='average item'; ['avg i']='average item'; ['avrg i']='average item'; ['averag i']='average item'; ['average i']='average item'; ['avg item']='average item'; ['avrg item']='average item'; ['averag item']='average item'; ['average item']='average item'; ['avg i']='average item'; ['a i']='average item'; ['a item']='average item'; ['an']='average number'; ['avg n']='average number'; ['avrg n']='average number'; ['averag n']='average number'; ['average n']='average number'; ['avg num']='average number'; ['avrg num']='average number'; ['averag num']='average number'; ['average num']='average number'; ['avg number']='average number'; ['avrg number']='average number'; ['averag number']='average number'; ['average number']='average number'; }	local data = aliases[data:lower] local tier = tonumber(string._toArabic(tier or 0)) local item = tonumber(item) if data == "storage" then ret = minionData.stats[tier].storage elseif data == "time between actions" then ret = minionData.stats[tier].tba elseif data == "crafting item" then ret = minionData.stats[tier].crafting.item elseif data == "crafting number" then ret = minionData.stats[tier].crafting.num elseif data == "description" then ret = minionData.description elseif data == "average item" then ret = minionData.items[item].item elseif data == "average number" then ret = minionData.items[item].avg else ret = _error("Invalid Argument to \"data\": unknown data type (help)") end return ret end

- -- Template:Days using minions -- -- Makes a table with how long it would take a single minion to output a certain amount of items based on it's tier - function p.minionPerDayTable( frame ) --local parameters = frame.args local args = getArgs(frame) local minion = args["minion"] or args[1] or title local amount = args["amount"] or args[2] local item = args["item"] or (amount == args[2] and args[3] or args[2]) local split = yesno(args["split"], true) local noscroll = yesno(args["noscroll"], false) return p._minionPerDayTable( minion, amount, item, split, noscroll ) end function p._minionPerDayTable( minion, amount, item, split, noscroll ) local HALF_ROW = 6 local actionsPerDay local days local itemIndex = 1 if item then for key,value in ipairs(minionData[minion].items) do			if value['item'] == item then itemIndex = key end end end local table = mw.html.create('table'):addClass('wikitable'):css({['text-align']="center", ['table-layout']="fixed"}) if noscroll and not split then table:css({['min-width']="100%"}) end table:tag('caption'):wikitext(string.format( 'Days to acquire %s using %s %s Minion (by tier, no fuel) If the player has multiple minions, divide the number of days by how many minions will be used. ',		processResource(amount..' '..minionData[minion].items[itemIndex].item, true), (minion:lower:match('^([aeiouy])') and 'an' or 'a'), minion )	)	local row, row_data, row2, row_data2 = table:tag("tr"), table:tag("tr"), table:tag("tr"), table:tag("tr") for i, tier in ipairs(TIERS) do		if minionData[minion].stats[i] then if split and i > HALF_ROW then row2:tag("th"):wikitext(tier):done else if noscroll and not split then row:tag("th"):wikitext(tier):done else row:tag("th"):wikitext(tier):css({["min-width"]="3.8em"}):done end end else if split then row2:tag("td"):wikitext(string.blankCell):attr({rowspan=2}) end end end for i, tier in ipairs(TIERS) do		if minionData[minion].stats[i] then actionsPerDay = _actionsPerMinute(minion, i)*60*24; days = amount/actionsPerDay/minionData[minion].items[itemIndex].avg; -- Show appropriate number of decimals (smaller the number = more) if days < 1 then days = math.floor(days*100)/100 elseif days < 10 then days = math.floor(days*10)/10 else days = math.floor(days) end if split and i > HALF_ROW then row_data2:tag("td"):wikitext( days ):done else row_data:tag("td"):wikitext( days ):done end end end row:done row2:done row_data:done row_data2:done table:done scrollable = " "..tostring(table).." " return noscroll and tostring(table) or scrollable end

- -- Template:Days Using Minions Combined -- -- Displays Days Using Minions with tabbers -

function p.minionPerDayTableCombined( frame ) --local parameters = frame.args local args = getArgs(frame) local lang = mw.language.getContentLanguage local split = yesno(args["split"], true) local noscroll = yesno(args["noscroll"], false) -- first combine both methods of entering data into one list local resources = {} for i = 1,16,1 do	   if i==1 and string.find(args[1], "\*")==1 then local splitwikilist = mw.text.split(string.sub(args[1], 2), "%s*\*%s*"); for i, li in pairs(splitwikilist) do	          resources[#resources+1] = li	        end else resources[#resources+1] = args[i] end end local items = {} for i, item in pairs(resources) do       local numOrNil = tonumber(lang:parseFormattedNumber(item)) if numOrNil then items[#items+1] = numOrNil else local num, name = string.match(item, "([%d,%.]+)x? %[?%[?([%l%s%u%,%'%-%d_]*)%|?.*%]?%]?") if not num then -- also test for format name, num = string.match(item, "([%l%s%u%,%'%-_]*),([%d]+)") if not num then return _error('Invalid item format: '..item) end end items[#items+1] = {name, tonumber(lang:parseFormattedNumber(num))} end end return p._minionPerDayTableCombined(items, split, noscroll) end

function p._minionPerDayTableCombined(entries, split, noscroll) local result = " " -- TODO: Someone please make this section work automatically and not need adding for each new minion ~Harteiga local differentList = {"seeds", "red mushroom", "brown mushroom", "raw beef", "beef", "leather", "raw porkchop", "porkchop", "raw chicken", "feather", "mutton", "wool", "raw rabbit", "rabbit foot", "rabbit's foot", "rabbit hide", "iron ingot", "gold ingot", "lapis lazuli", "nether quartz", "glowstone dust", "flint", "rotten flesh", "poisonous potato", "string", "spider eye", "gunpowder", "ender pearl", "ghast tear", "slimeball", "blaze rod", "magma cream", "oak wood", "spruce wood", "birch wood", "dark oak wood", "acacia wood", "jungle wood", "fish", "raw fish", "salmon", "raw salmon", "pufferfish", "clownfish", "prismarine crystals", "prismarine shard", "sponge", "dandelion", "poppy", "snowball"} local different = {"wheat", "mushroom", "mushroom", "cow", "cow", "cow", "pig", "pig", "chicken", "chicken", "sheep", "sheep", "rabbit", "rabbit", "rabbit", "rabbit", "iron", "gold", "lapis", "quartz", "glowstone", "gravel", "zombie", "zombie", "spider", "spider", "creeper", "enderman", "ghast", "slime", "blaze", "magma cube", "oak", "spruce", "birch", "dark oak", "acacia", "jungle", "fishing", "fishing", "fishing", "fishing", "fishing", "fishing", "fishing", "fishing", "fishing", "flower", "flower", "snow"} local extraList = {"rotten flesh", "diamond", "spider eye", "iron ingot", "obsidian", "quartz", "nether quartz", "spider eye", "string"} local extra = {"revenant", "revenant", "tarantula", "tarantula", "voidling", "voidling", "voidling", "cave spider", "cave spider"} for i, entry in pairs(entries) do		local identical = true ---|Sample=" .. p._minionPerDayTable (different[j], entry[2], entry[1], split, noscroll)				identical = false				break			end		end		-- if identical then result = result .. "|-|Sample=" .. p._minionPerDayTable (entry[1], entry[2], entry[1], split, noscroll) end for j, item in pairs(extraList) do			if entry[1]:lower == item then result = result .. p._minionPerDayTable (extra[j], entry[2], entry[1], split, noscroll) end end end result = result .. " "

local frame = mw.getCurrentFrame return noscroll and frame:preprocess(result) or scrollable end

- -- Template:Minion ideal layout -- -- Makes a table of to form the in-game ideal layout for a minion - function p.minionIdealLayoutTable( frame ) --local parameters = frame.args local args = getArgs(frame) local minion = args["minion"] or args[1] or title local ideal = args["ideal"] local border = args["border"] local size = 5; local slots = {} local abc = { "A", "B", "C", "D", "E", "F", "G", "H", "I" } for y = 1,size,1 do		slots[y] = {} for x = 1,size,1 do		  slots[y][x] = args[abc[x]..y]		end end return p._minionIdealLayoutTable( minion, size, ideal, border, slots ) end function p._minionIdealLayoutTable( minion, size, ideal, border, customSlots ) if border then size = size + 2 end local table = mw.html.create('table'):addClass('mcui mcui-Crafting_Table pixel-image'):css({['margin']='0 auto', ['display']='table'}) for y = 1,size,1 do		local row = table:tag("tr"):addClass('mcui-row') for x = 1,size,1 do			local img if border and (y==1 or y==size or x==1 or x==size) then img = border elseif x==math.ceil(size/2) and y==math.ceil(size/2) then img = "["..minion.."]"..minion.." I"				if customSlots[y][x] then img = img..";"..customSlots[y][x] end else img = border and (customSlots[y-1][x-1] or ideal) or (customSlots[y][x] or ideal) end row:tag("td"):wikitext( slot{ img } ):done end row:done end table:done return tostring(table) end

- -- Template:Minion UI -- -- Makes a table of to form the in-game UI of a minion - function p.minionUI( frame ) --local parameters = frame.args local args = getArgs(frame) local minion = args["minion"] or args[1] or title return p._minionUI( minion ) end

local NPCtext = { ["Bulvar"] = {name="&5Bulvar", loc="&2Dwarven Mines"}, ["Terry"] = {name="&5Terry", loc="&bTrappers Den"}, }

function p._minionUI( minion ) local name, tier = minion:match('(.-)%s?[Mm]?[Ii]?[Nn]?[Ii]?[Oo]?[Nn]?%s([%divxIVX]+)') if tier then tier = tonumber(tier) and tonumber(tier) or string._toArabic(tier) else name = minion:gsub('%s*[Mm][Ii][Nn][Ii][Oo][Nn]', '') tier = 1 end if not minionData[name] then error("Minion name "..name.." is not valid") end if not minionData[name].stats[tier] then error(name.."Minion has no such tier (Tier "..tier..")") end local highestTier, highestCraftable = peakStat(name) local storageTier = {} for slotno = 1,15,1 do -- Minions have 15 total slots for y = 1,10,1 do -- All slots unlocked at tier 10 local avail = minionData[name].stats[y].storage / 64 storageTier[slotno] = y			if avail >= slotno then break end end end local function available( num ) if tier >= storageTier[num] then return slot{ '' } else return slot{ 'White Stained Glass Pane', link='none', title='&eStorage unlocked at tier '..string._toRoman(storageTier[num]), text='none' } end end local title = name..' Minion '..string._toRoman(tier) local description = minionData[name].description or 'No Description' local upgradeMessage = '' local upgradeMessageView = '' local upgradeMessageUpgrade = '' local blank = slot{ 'Black Stained Glass Pane', link='none', title='none', text='none' } if tier == highestTier then upgradeMessage = '&aHighest tier has been reached!' upgradeMessageView = 'The highest tier of this minion/has been reached//'..upgradeMessage upgradeMessageUpgrade = "&cThis minion has reached the/maximum tier." elseif tier == highestCraftable then local tradenpc = minionData[name].stats[tier+1].tradeNpc upgradeMessage = '&aHighest craftable tier has been/reached!' upgradeMessageView = 'The highest craftable tier of/this minion has been reached/Talk to '..NPCtext[tradenpc] and NPCtext[tradenpc].name or tradenpc..' &rin the/'..NPCtext[tradenpc] and NPCtext[tradenpc].loc or '(unknown)'..' &rto unlock the/next tier!//'..upgradeMessage upgradeMessageUpgrade = "&cThis minion has reached the/maximum tier." elseif minionData[name].stats[tier + 1].storage > minionData[name].stats[tier].storage then upgradeMessage = '&7Time Between Actions: &e'..minionData[name].stats[tier].tba..'s/&7Max Storage: &8'..minionData[name].stats[tier].storage..' ➜ &a'..minionData[name].stats[tier + 1].storage upgradeMessageView = '&7View the items required to/upgrade this minion to the/next tier.//'..upgradeMessage..'//&eClick to view!' upgradeMessageUpgrade = upgradeMessage..'//&eClick to upgrade!' else upgradeMessage = '&7Time Between Actions: &8'..minionData[name].stats[tier].tba..'s ➜ &a'..minionData[name].stats[tier + 1].tba..'s/&7Max Storage: &e'..minionData[name].stats[tier].storage upgradeMessageView = '&7View the items required to/upgrade this minion to the/next tier.//'..upgradeMessage..'//&eClick to view!' upgradeMessageUpgrade = upgradeMessage..'//&eClick to upgrade!' end local table = mw.html.create('table'):addClass('mcui mcui-Crafting_Table pixel-image'):css({['margin']='0 auto', ['display']='table'}) table:tag("tr"):attr({style="position:absolute;top:2px;"}):tag("td"):attr({colspan=9,style="font-family:minecraft;color:black;font-size:16px;"}):wikitext( title ):done:done table:tag("tr"):addClass('mcui-row'):attr({style="margin-top: 24px;"}) :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( slot{ 'Redstone Torch', link='none', title='&aIdeal Layout', text='&7View the most effecient spot for/this minion to be placed in.' } ):done :tag("td"):wikitext( slot{ title, title='&9'..title, text='&7'..description..'//&7Time Between Action: &a'..minionData[name].stats[tier].tba..'s/&7Max Storage: &e'..minionData[name].stats[tier].storage..'/&7Resources Generated: &b0' } ):done :tag("td"):wikitext( slot{ 'Gold Ingot', link='none', title='&aNext Tier', text=upgradeMessageView } ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :done table:tag("tr"):addClass('mcui-row') :tag("td"):wikitext( blank ):done :tag("td"):wikitext( slot{ 'Lime Stained Glass Pane', link='none', title='&aMinion Skin Slot', text='&7You can insert a Minion Skin/here to change the appearance of/your minion.' } ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :done table:tag("tr"):addClass('mcui-row') :tag("td"):wikitext( blank ):done :tag("td"):wikitext( slot{ 'Orange Stained Glass Pane', link='none', title='&aFuel', text='&7Increase the speed of your/minion by adding minion fuel/items here.//&cNote: &7You can\'t take/fuel back out after you/place it here!' } ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( available( 1 ) ):done :tag("td"):wikitext( available( 2 ) ):done :tag("td"):wikitext( available( 3 ) ):done :tag("td"):wikitext( available( 4 ) ):done :tag("td"):wikitext( available( 5 ) ):done :tag("td"):wikitext( blank ):done :done table:tag("tr"):addClass('mcui-row') :tag("td"):wikitext( blank ):done :tag("td"):wikitext( slot{ 'Blue Stained Glass Pane', link='none', title='&aAutomated Shipping', text='Add a &bBudget Hopper&7,/&bEnchanted Hopper&7 or a/&bPerfect Hopper&7 here to make/your minion automatically sell/generated items after its/inventory is full.' } ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( available( 6 ) ):done :tag("td"):wikitext( available( 7 ) ):done :tag("td"):wikitext( available( 8 ) ):done :tag("td"):wikitext( available( 9 ) ):done :tag("td"):wikitext( available( 10 ) ):done :tag("td"):wikitext( blank ):done :done table:tag("tr"):addClass('mcui-row') :tag("td"):wikitext( blank ):done :tag("td"):wikitext( slot{ 'Yellow Stained Glass Pane', link='none', title='&aUpgrade Slot', text='&7You can improve your minion by/adding a minion upgrade item/here.' } ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( available( 11 ) ):done :tag("td"):wikitext( available( 12 ) ):done :tag("td"):wikitext( available( 13 ) ):done :tag("td"):wikitext( available( 14 ) ):done :tag("td"):wikitext( available( 15 ) ):done :tag("td"):wikitext( blank ):done :done table:tag("tr"):addClass('mcui-row') :tag("td"):wikitext( blank ):done :tag("td"):wikitext( slot{ 'Yellow Stained Glass Pane', link='none', title='&aUpgrade Slot', text='&7You can improve your minion by/adding a minion upgrade item/here.' } ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( slot{ 'Chest', link='none', title='&aCollect All', text='&eClick to Collect all items!' } ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( slot{ 'Diamond', link='none', title='&aQuick-Upgrade Minion', text='&7Click here to upgrade your/minion to the next tier.//'..upgradeMessageUpgrade } ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( blank ):done :tag("td"):wikitext( slot{ 'Bedrock', link='none', title='&aPickup Minion', text='&eClick to pickup!' } ):done :done table:done return tostring(table) end

- -- Template:Minion recipe gallery -- -- Makes a table of to form the in-game ideal layout for a minion - function p.minionRecipeGallery( frame ) --local parameters = frame.args local args = getArgs(frame) local minion = args["minion"] or args[1] if not minion then minion = title end return p._minionRecipeGallery( minion ) end

function p._minionRecipeGallery( minionName ) local minion = minionData[minionName] -- Get a list of the desired tiers local tiers = {} local start = minion.stats[1].crafting.info and 2 or 1 local _, nCraftable = peakStat(minionName) for i = start, nCraftable do tiers[#tiers+1] = i end -- Generate crafting tables local tables = {} for _,tier in ipairs(tiers) do		local crafting = minion.stats[tier].crafting local recipeBase = crafting.item..","..crafting.num local recipeB3 = crafting.B3 or crafting.item..","..crafting.num local recipeMinion = crafting.base or minionName.." Minion "..TIERS[tier-1] local recipeOutput = minionName.." Minion "..TIERS[tier] tables[#tables+1] = craftingTable{ A1 = recipeBase, B1 = recipeBase,  C1 = recipeBase, A2 = recipeBase, B2 = recipeMinion, C2 = recipeBase, A3 = recipeBase, B3 = recipeB3,	 C3 = recipeBase, Output = recipeOutput }	end -- Display all crafting tables in a grid local grid = mw.html.create('table') local row for i, ct in pairs(tables) do		if (i-1)%3 == 0 then if row then row:done end row = grid:tag("tr") end row:tag("td"):wikitext(ct):done end row:done grid:done return tostring(grid) end

- -- Template:Minion animated crafting table -- -- Make an animated of all potential minion crafting recipes - function p.minionAnimatedCraftingTable( frame ) --local parameters = frame.args local args = getArgs(frame) local minion = args["minion"] or args[1] or title local item = args["item"] local from = args["from"] local to = args["to"] return p._minionAnimatedCraftingTable( minion, item, from, to ) end function p._minionAnimatedCraftingTable( minionName, item, from, to ) local minion = minionData[minionName] local highestTier, highestCraftable = peakStat(minionName) -- Get a list of the desired tiers local tiers = {} if item then for i=1,highestCraftable,1 do			local crafting = minion.stats[i].crafting if crafting.item == item and not crafting.info then tiers[#tiers+1] = i			end end --ignore .info elseif from and to then if not tonumber(from) or not tonumber(to) then error("Range is not correct") end from, to = tonumber(from), tonumber(to) if not minion.stats[from] or not minion.stats[to] then error("Range is not correct") end if from > to then from, to = to, from end for i=from,to,1 do tiers[#tiers+1] = i end else local start = minion.stats[1].crafting.info and 2 or 1 for i=start,highestCraftable,1 do tiers[#tiers+1] = i end end -- Use tier list to generate the animated crafting table local recipeBase = {} local recipeB3 = {} local recipeMinion = {} local recipeOutput = {} for key,tier in pairs(tiers) do		local crafting = minion.stats[tier].crafting recipeBase[#recipeBase+1] = crafting.item..","..crafting.num recipeB3[#recipeB3+1] = crafting.B3 or crafting.item..","..crafting.num recipeMinion[#recipeMinion+1] = crafting.base or minionName.." Minion "..TIERS[tier-1] recipeOutput[#recipeOutput+1] = minionName.." Minion "..TIERS[tier] -- special case for mushroom, since all elements must have 2 states, since the "Mushroom" item cycles between 2 states if minionName == "Mushroom" then recipeMinion[#recipeBase] = recipeMinion[#recipeBase]..";"..recipeMinion[#recipeBase] recipeOutput[#recipeBase] = recipeOutput[#recipeBase]..";"..recipeOutput[#recipeBase] end end -- now return the crafting table recipeBase = table.concat(recipeBase,";") recipeB3 = table.concat(recipeB3,";") recipeMinion = table.concat(recipeMinion,";") recipeOutput = table.concat(recipeOutput,";") return craftingTable{ A1 = recipeBase, B1 = recipeBase, C1 = recipeBase, A2 = recipeBase, B2 = recipeMinion, C2 = recipeBase, A3 = recipeBase, B3 = recipeB3, C3 = recipeBase, Output = recipeOutput } end

- -- Template:Minion stats table -- -- Takes all the data from /Data and displays it nicely in a table on the minion page. - function p.minionStatsTable( frame ) --local parameters = frame.args local args = getArgs(frame) local minion = args["minion"] or args[1] or title return frame:preprocess(p._minionStatsTable( minion )) end

function p._minionStatsTable( minionName ) local minion = minionData[minionName] -- B3 has non-standard info for slayer minions local B3Exists = minion.stats[2].crafting.B3	local showBazaarCost = minionName:lower ~= "flower" local totalsBazaarCost = 0 -- We want the cost of cumulative enchanted version local wikitable = mw.html.create('table'):addClass('wikitable') -- Header row local row = wikitable:tag("tr") :tag("th"):attr({ rowspan=2 }):wikitext("Tier"):done :tag("th"):attr({ rowspan=2 }):wikitext("Info"):done :tag("th"):wikitext("Upgrade Cost"):done if showBazaarCost then row:tag("th"):wikitext("Bazaar Upgrade Cost"):done end row:tag("th"):attr({ colspan=B3Exists and 3 or 2 }):wikitext("Recipe"):done -- Header row (second row) row = wikitable:tag("tr") row:tag("th"):css({ ["background-color"]="#29181a" }):wikitext("Total Cost") if showBazaarCost then row:tag("th"):wikitext("Bazaar Cumulative Cost") end row:tag("th"):wikitext(" Base ") if B3Exists then row:tag("th"):wikitext(" Item (x7) ") row:tag("th"):wikitext(" Extra ") else row:tag("th"):wikitext(" Item (x8) ") end -- Display non-header rows local baseCraftingItem = minion.stats[2].crafting.item local items = minion.items local background = { -- {rowBG CSS Table, rowBG2 CSS Table} {{}, { background="rgba(255, 255, 255, 0.3)" }}, -- phase 1 {{ background="rgba(110, 17, 17, 0.15)" }, { background="rgba(255, 255, 255, 0.15)" }}, -- phase 2 {{ background="rgba(110, 17, 17, 0.5)" },{ background="rgba(90, 75, 75, 0.34)" }}, -- etc.		{{ background="rgba(110, 17, 17, 0.9)" },{ background="rgba(90, 75, 75, 0.03)" }}, }	local function getInfo(i) local stats = minion.stats[i] local crafting = stats.crafting local mainItem = crafting and crafting.item or stats.trade[1].item return stats, crafting, mainItem end for i = 1,#TIERS,1 do		if not minion.stats[i] then break end local stats, crafting, mainItem = getInfo(i) local multi8 = crafting and crafting.B3 and 7 or 8 local actionsPerDay = _actionsPerMinute(minionName, i)*60*24 local unique = {} for j = i,1,-1 do			local stats, crafting, mainItem = getInfo(j) unique[mainItem] = true end -- Row color for recipes using enchanted items local rowBG = background[table.length(unique)][1] local rowBG2 = background[table.length(unique)][2] local cellTopBrdr = { ["border-top-width"]="2px" } --		-- Calculate info for below --		--Calculate Item Costs local tierCosts = {} if crafting then tierCosts[#tierCosts+1] = (crafting.num*multi8).." "..crafting.item.."" if crafting.base then tierCosts[#tierCosts+1] = "1x "..crafting.base.."" end if crafting.B3 then tierCosts[#tierCosts+1] = "1x "..crafting.B3.."" end elseif stats.trade then for _,itemData in ipairs(stats.trade) do				if itemData.item:lower == "coin" then tierCosts[#tierCosts+1] = itemData.num.." coins" elseif itemData.item:lower == "pelt" then tierCosts[#tierCosts+1] = itemData.num.." pelts" else tierCosts[#tierCosts+1] = itemData.num.."x "..itemData.item.."" end end end -- Find totals for items needed for all tiers up to this one local totals = {} local totalsObj = _getStatTableCumulativeTotal(minionName, i)		for key,value in pairs(totalsObj) do totals[#totals+1] = string._formatNum(math.ceil(value)).." "..key end --Calculate Bazaar Costs local bazaarCost = 0 if showBazaarCost then -- Since crafting and trade data have different formats, list format is needed local items = {} if crafting then items[#items+1] = { item=crafting.item, num=crafting.num*multi8 } elseif stats.trade then items = stats.trade end for _,itemData in ipairs(items) do				local craftNum, craftItem = itemData.num, itemData.item -- Hardcoded workaround for Silver Fang not being purchasable on Bazaar if craftItem == "Silver Fang" then craftNum = craftNum*25 craftItem = "Enchanted Ghast Tear" elseif craftItem == "Melon (block)" then craftNum = craftNum*9 craftItem = "Melon" end -- Handle special items if craftItem:lower == "coin" then -- just increment cost by coin amount bazaarCost = bazaarCost + craftNum elseif craftItem:lower == "pelt" then -- skip else local calculatedPrice = bazaar._calcMaterialBuyPrices({ { craftItem, tonumber(craftNum) } }) if type(calculatedPrice) ~= "number" then return calculatedPrice end bazaarCost = bazaarCost + calculatedPrice end end --Totals totalsBazaarCost = totalsBazaarCost + bazaarCost end --		-- First row for this tier --		local row = wikitable:tag("tr"):attr('id', TIERS[i]):css(rowBG) -- Tier + Icon

row:tag('th'):attr({ rowspan=2 }):css(cellTopBrdr):wikitext(TIERS[i]..' '..slot{ minionName..' Minion '..TIERS[i] }):done -- Info row:tag("td") :attr({ rowspan=2 }) :css(cellTopBrdr) :wikitext("Cooldown: "..colorText("Green", stats.tba.."s")) :wikitext(" Storage: "..colorText("Yellow", stats.storage)) :done -- Cost row:tag("td"):css(cellTopBrdr):wikitext(""):done -- Bazaar Cost if showBazaarCost then row:tag("td"):css(cellTopBrdr):wikitext("") end -- Recipe if stats.trade then -- If bought via trade, list data local slots,list = { slot{ minionName.." Minion "..TIERS[i-1] } },{} for _,trd in ipairs(stats.trade) do				if trd.item:lower == "coin" then list[#list+1] = "" elseif trd.item:lower == "pelt" then list[#list+1] = "Pelts" else slots[#slots+1] = slot{ trd.item..","..trd.num } end end row:tag("td"):attr({ rowspan=2, colspan=B3Exists and 3 or 2 }):css(cellTopBrdr) :wikitext("Merchant: "..stats.tradeNpc.."") :tag("div"):wikitext(table.concat(slots, "")):done :tag("ul"):addClass('lowmargin'):wikitext(""..table.concat(list, "")..""):done elseif crafting.info then -- If there is no crafting recipe, show the details on why row:tag("td"):attr({ rowspan=2, colspan=B3Exists and 3 or 2 }):css(cellTopBrdr):wikitext(crafting.info):done else -- Recipe exists row:tag("td"):attr({ rowspan=2 }):css(cellTopBrdr):wikitext(crafting.base and slot{ crafting.base } or slot{ minionName.." Minion "..TIERS[i-1] }):done local itemColspan = B3Exists and not crafting.B3 and 2 or 1 row:tag("td"):attr({ rowspan=2, colspan=itemColspan }):css(cellTopBrdr):wikitext(slot{ crafting.item..","..crafting.num }):wikitext(itemColspan==2 and " x8" or ""):done if crafting.B3 then row:tag("td"):attr({ rowspan=2 }):css(cellTopBrdr):wikitext(slot{ crafting.B3 }):done end end --		-- Second row for this tier --		row = wikitable:tag("tr") -- Total cost row:tag("td"):css(rowBG2):wikitext("") -- Bazaar Total Cost if showBazaarCost then row:tag("td"):css(rowBG):wikitext("") end end return tostring(wikitable) end

-- Find the total number of the base minion items -- returns { {item,num} } function _getStatTableBaseCost(minionName, item, num) local minion = minionData[minionName] local recipes = minion.recipes if not recipes[item] then return { {item, num} } end local items = {} for key,value in ipairs(recipes[item]) do		local subitems = _getStatTableBaseCost(minionName, value[1], value[2]*num) for key2,value2 in ipairs(subitems) do			items[#items+1]=value2 end end return items end

-- Find total of base minion items for all tiers up to the one passed in function _getStatTableCumulativeTotal(minionName, tier) local minion = minionData[minionName] local totals = {} for i = 1,tier,1 do		local stats = minion.stats[i] local items = {} if stats.crafting then local crafting = stats.crafting local multi8 = crafting.B3 and 7 or 8 items = { { item=crafting.item, num=crafting.num*multi8} } elseif stats.trade then items = stats.trade end local cost = {} for _,itemData in ipairs(items) do			cost = table.merge(cost, _getStatTableBaseCost(minionName, itemData.item, itemData.num)) end for key,value in ipairs(cost) do			local item = value[1] totals[item] = (totals[item] or 0) + value[2] end end return totals end

- -- Template:MinionProfitTable -- -- Shows a table with resources collected by the minion alongside their bazaar sell prices - function p.minionProfitTable(frame) local args = getArgs(frame) local minion = args[1] or args["minion"] or title if not minion then minion = mw.title.getCurrentTitle.text end minion = minion:gsub('%s*[Mm]inion', '') return frame:preprocess(p._minionProfitTable(minion)) end

function p._minionProfitTable(minion, isOffline) local produce = minionData[minion].items local tba = minionData[minion].stats local items = {} --get drops produced by the minion for i=1,25,1 do --25 because some minions like flower minion have very large amount of different drops if produce[i] and produce[i].condition == nil then items[i] = produce[i] elseif produce[i] and produce[i].condition == "Auto Smelter" and produce[i].item ~= "Cactus Green" then items[i] = produce[i] table.remove(items, i-1) elseif produce[i] and produce[i].condition == "Flint Shovel" then items[i] = produce[i] table.remove(items, i-1) end end --compose the header of the table local wikitable = mw.html.create('table'):addClass('wikitable') wikitable:tag('caption'):wikitext("Listed below are the profits of this minion when its items are sold to Bazaar. No fuel and no diamond spreading are used.", ' ', 'Values are shown when the player is online.') wikitable:tag('tr') :tag('th'):wikitext('Tier'):attr({ rowspan=2 }):done :tag('th'):wikitext('Harvests per minute '):attr({ rowspan=2 }):done :tag('th'):wikitext('Per hour'):attr ({ colspan=2 }):done :tag('th'):wikitext('Per day'):attr ({ colspan=2 }):done :done wikitable:tag('tr') :tag('th'):wikitext('Items'):done :tag('th'):wikitext('Bazaar Profit'):done :tag('th'):wikitext('Items'):done :tag('th'):wikitext('Bazaar Profit'):done :done -- add minion produce and profits to the table for i=1,#TIERS,1 do 		if not minionData[minion].stats[i] then return tostring(wikitable) end local tba = minionData[minion].stats[i].tba --list items not possible to sell at bazaar local non_bazaar_items = { ["Poisonous Potato"] = 10, ['White Wool'] = 2, ['Clownfish'] = 20, }		--create the string displayed in the "produce" column (compatible with ) local resourceStr = {} --get first item resourceStr[1] = "*"..math.floor(p._producePerMinute(items[1].avg, tba)*60).." "..items[1].item --get rest of items, if applicable for j=2,#items,1 do resourceStr[#resourceStr+1] = "\n*"..math.floor(p._producePerMinute(items[j].avg, tba)*60).." "..items[j].item end resourceStr = table.concat(resourceStr) --create string for profit calculation (compatible woth ) local bazaarStr = {} --get first item bazaarStr[1] = "*"..(p._producePerMinute(items[1].avg, tba)*60).." "..items[1].item --get rest of items, if applicable for j=2,#items,1 do			if non_bazaar_items[items[j].item] then bazaarStr[#bazaarStr+1] = "\n*"..(p._producePerMinute(items[j].avg, tba)*60)*non_bazaar_items[items[j].item] else bazaarStr[#bazaarStr+1] = "\n*"..(p._producePerMinute(items[j].avg, tba)*60).." "..items[j].item end end bazaarStr = table.concat(bazaarStr) --same thing as above but for the 24 hour section local resourceStr_24 = {} resourceStr_24[1] = "*"..math.floor(p._producePerMinute(items[1].avg, tba)*60*24).." "..items[1].item for j=2,#items,1 do resourceStr_24[#resourceStr_24+1] = "\n*"..math.floor(p._producePerMinute(items[j].avg, tba)*60*24).." "..items[j].item end resourceStr_24 = table.concat(resourceStr_24) local bazaarStr_24 = {} bazaarStr_24[1] = "*"..(p._producePerMinute(items[1].avg, tba)*60*24).." "..items[1].item for j=2,#items,1 do			if non_bazaar_items[items[j].item] then bazaarStr_24[#bazaarStr_24+1] = "\n*"..(p._producePerMinute(items[j].avg, tba)*60*24)*non_bazaar_items[items[j].item] else bazaarStr_24[#bazaarStr_24+1] = "\n*"..(p._producePerMinute(items[j].avg, tba)*60*24).." "..items[j].item end end bazaarStr_24 = table.concat(bazaarStr_24) --get harvests per minute local hpm if minion == "Pumpkin" or minion == "Melon" or minion == "Fishing" then hpm = string.roundNumber(60/(tba), 2) else hpm = string.roundNumber(60/(tba*2), 2) end --add the table cells and fill them with the data from above wikitable:tag('tr') :tag('th'):wikitext(string._toRoman(i)..' '..slot{ minion..' Minion '..string._toRoman(i) }):done :tag('td'):wikitext(' "):done			:tag('td'):wikitext(""):done			:tag('td'):wikitext(""):done			:tag('td'):wikitext(""):done			:tag('td'):wikitext(""):done		:done	end	return tostring(wikitable) end function p._producePerMinute(amount, tba)	-- ACTIONS_PER_MINUTE = 60/TBA*2	-- PRODUCE_PER_MINUTE = ACTIONS_PER_MINUTE * AVG_AMOUNT	return 60/(tba*2)*amount end

- -- Template:Minion Profit -- -- Shows the average bazaar sell price for a specific minion setup - function p.minionProfit(frame) local args = getArgs(frame) local minion = args[1] or args["minion"] or title local tier = args["tier"] or args["t"] local diam_spread = yesno(args["diamond_spreading"] or args["ds"], false) local fuel = args["fuel"] or args["f"] local crystal = yesno(args["crystal"] or args["c"]) local bonus = args["bonus"] or args["other"] local expander = args["expander"] or args["e"] local web = args["flycatcher"] or args["fly"] or args["web"] local sc3000 = yesno(args["sc3000"], false) local input_time = args["input_time"] or args["time"] return p._minionProfit(minion, tier, diam_spread, fuel, crystal, bonus, expander, web, sc3000, input_time, true) end

function p._minionProfit(minion, tier, diam_spread, fuel, crystal, bonus, expander, web, sc3000, input_time, coin) local minionData = require('Module:Minion/Data') if not minionData[minion] then return _error('Invalid minion: '.. minion) end --remove "Minion" from minion name if minion then minion = minion:gsub("(%w)(%w+)", function(t1, t2) return t1:upper..t2:lower end) minion = minion:gsub("%s+?[Mm]inion", "") end local produce = minionData[minion].items local tba = minionData[minion].stats --set time to 1h if not specified if not input_time then input_time = 1 end --set tier to max tier if not specified. convert tier to arabic local maxTier = table.length(minionData[minion].stats) if tier then if tonumber(tier) then tier = tonumber(tier) if tier > maxTier then return nil end else tier = string._toArabic(tier) end else tier = maxTier end -- get fuel name local aliases = { ["coal"] = "coal", ["block of coal"] = "block of coal", ["enchanted bread"] = "enchanted bread", ["ench bread"] = "enchanted bread", ["enchanted coal"] = "enchanted bread", ["ench coal"] = "enchanted coal", ["enchanted charcoal"] = "enchanted charcoal", ["ench charcoal"] = "enchanted charcoal", ["solar panel"] = "solar panel", ["solar"] = "solar panel", ["enchanted lava bucket"] = "enchanted lava bucket", ["enchanted lava"] = "enchanted lava bucket", ["ench lava"] = "enchanted lava bucket", ["lava"] = "enchanted lava bucket", ["magma bucket"] = "magma bucket", ["magma"] = "magma bucket", ["plasma bucket"] = "plasma bucket", ["plasma"] = "plasma bucket", ["hamster wheel"] = "hamster wheel", ["hw"] = "hamster wheel", ["foul flesh"] = "foul flesh", ["ff"] = "foul flesh", ["tasty cheese"] = "tasty cheese", ["cheese"] = "tasty cheese", ["catalyst"] = "catalyst", ["cat"] = "catalyst", ["c"] = "catalyst", ["hyper catalyst"] = "hyper catalyst", ["hc"] = "hyper catalyst", }	local fuels = { -- note: fuel multipliers should be written as a multiple, in other words, (1+increase%) ["coal"] = 1.05, ["block of coal"] = 1.05, ["enchanted bread"] = 1.05, ["enchanted coal"] = 1.1, ["enchanted charcoal"] = 1.2, ["solar panel"] = 1.25, ["enchanted lava bucket"] = 1.25, ["magma bucket"] = 1.3, ["plasma bucket"] = 1.35, ["hamster wheel"] = 1.5, ["foul flesh"] = 1.9, ["tasty cheese"] = 2, ["catalyst"] = 3, ["hyper catalyst"] = 4, }	-- local fuelTypes = { --	 ["catalyst"] = "catalyst", --	 ["hyper catalyst"] = "catalyst", -- }	if fuel then fuel = aliases[fuel:lower] or error('Invalid fuel "' .. fuel .. '"') end -- local fuelType = fuel and (fuelTypes[fuel] and fuelTypes[fuel] or "normal") or "normal" -- 'fuel' gets the fuel multiplier fuel = fuels[fuel] or 0 --check if some other params exist; if not set to default if not expander then expander = 0 else expander = tonumber(expander) end if not web then web = 0 else web = tonumber(web) end if not bonus then bonus = 0 else bonus = tonumber(bonus) end -- if not crystal then crystal = 0 else crystal = 0.1 end if input_time then input_time = tonumber(input_time) or error('Invalid time "'..input_time..'"') end

--get items produced by minion local items = {} for i=1,25,1 do --25 because some minions like flower minion have very large amount of different drops if produce[i] and produce[i].condition == nil then items[i] = produce[i] elseif produce[i] and produce[i].condition == "Auto Smelter" and produce[i].item ~= "Cactus Green" then items[i] = produce[i] table.remove(items, i-1) elseif produce[i] and produce[i].condition == "Flint Shovel" then items[i] = produce[i] table.remove(items, i-1) end end local function getMultiplier(multiplier, boost) -- note: 'boost' should be passed as a multiple, in other words, (1+increase%) return boost == 0 and multiplier or multiplier * boost end --get minion tba local tba, multiplier = minionData[minion].stats[tier].tba, 1 --calculate new tba multiplier = getMultiplier(multiplier, fuel) for i = 1, expander>2 and 2 or expander<0 and 0 or expander, 1 do		multiplier = getMultiplier(multiplier, 1.1) end for i = 1, web>2 and 2 or web<0 and 0 or web, 1 do		multiplier = getMultiplier(multiplier, 1.1) end if crystal then -- check if minion can use crystal local validMinions = { -- Woodcutting Crystal ["oak"]=true, ["spruce"]=true, ["birch"]=true, ["dark oak"]=true, ["acacia"]=true, ["jungle"]=true, -- Farm Crystal ["wheat"]=true, ["carrot"]=true, ["potato"]=true, ["pumpkin"]=true, ["melon"]=true, ["mushroom"]=true, ["cocoa beans"]=true, ["cactus"]=true, ["sugar cane"]=true, ["nether wart"]=true, -- Mithril Crystal ["coal"]=true, ["iron"]=true, ["gold"]=true, ["diamond"]=true, ["lapis"]=true, ["emerald"]=true, ["redstone"]=true, ["quartz"]=true, ["mithril"]=true, }		if validMinions[minion:lower] then multiplier = getMultiplier(multiplier, 1.1) end end if bonus then multiplier = getMultiplier(multiplier, bonus) end --calculate final produce collected local totalProduce = {} for i,item in pairs(items) do		totalProduce[#totalProduce+1] = { name = item.item, num = p._producePerMinute(item.avg, tba/multiplier)*60*input_time }	end --add diamond spreading diamonds if necessary if diam_spread then totalProduce[#totalProduce+1] = { name = "Diamond", num = p._producePerMinute(1, tba/multiplier)*60*input_time/10 }	end -- If a sc3000 is being used, use enchanted forms for more accurate price if sc3000 then local recipes = minionData[minion].recipes local ingr, numToBeCompacted for i,item in pairs(totalProduce) do			-- Go down recipe list and see if this item can be upgraded for rName,rIngrList in pairs(recipes) do				-- If recipe has more than one ingredient sc3000 doesn't work on it					-- Some items (such as Silver Fang) are ignored due to not having Bazaar support if #rIngrList == 1 and (rName ~= "Silver Fang" and rName ~= "Melon (block)") then ingr = rIngrList[1] if ingr[1] == item.name and item.num >= ingr[2] then -- integer division, since we only want a whole number of craftable amount numToBeCompacted = math.floor(item.num / ingr[2]) -- if item can be compacted then add new item to list and subtract count away from original recipe ingredient local n = #totalProduce+1 totalProduce[n] = { name=rName, num=numToBeCompacted } item.num = item.num % ingr[2] -- update list with the remainder item = totalProduce[n] end end end end end --get merchant sell pricess for non-bazaar items local non_bazaar_items = { ["Poisonous Potato"] = 10, ['White Wool'] = 2, ['Clownfish'] = 20, }	if minion == "Flower" then non_bazaar_items[""] = 1 end --make a string that can be used in 	local bazaarValues = {} for i,item in pairs(totalProduce) do		if non_bazaar_items[item.name] then -- If not in the bazaar calculate the shop sell price bazaarValues[#bazaarValues+1] = item.num*non_bazaar_items[item.name] else bazaarValues[#bazaarValues+1] = {item.name, item.num} end end --finish return bazaar._calcMaterialBuyPrices(bazaarValues, "buy", coin); end

- -- invoked directly -- -- more or less p.minionProfit, just in table for each minion - function p.minionsPageProfitTable(frame) local args = getArgs(frame) local diam_spread = yesno(args["diamond_spreading"] or args["ds"], false) local fuel = args["fuel"] or args["f"] local crystal = yesno(args["crystal"] or args["c"]) local bonus = args["bonus"] or args["other"] local expander = args["expander"] or args["e"] local web = args["flycatcher"] or args["fly"] or args["web"] -- These two have defaults different from normal profit code due to minion page local sc3000 = yesno(args["sc3000"], true) local input_time = args["input_time"] or args["time"] or 24 return p._minionsPageProfitTable(diam_spread, fuel, crystal, bonus, expander, web, sc3000, input_time) end

function p._minionsPageProfitTable(diam_spread, fuel, crystal, bonus, expander, web, sc3000, input_time) -- Needed due to calculator if fuel == "[none]" or fuel == "none" then fuel = nil end local wikitable = mw.html.create('table'):addClass('wikitable sortable searchable') wikitable:tag("caption"):wikitext("Profit per "..input_time.." hours") wikitable:tag("tr") :tag("th"):wikitext("Minion"):done :tag("th"):attr( 'data-sort-type', 'number' ):wikitext("Tier 1"):done :tag("th"):attr( 'data-sort-type', 'number' ):wikitext("Tier 3"):done :tag("th"):attr( 'data-sort-type', 'number' ):wikitext("Tier 5"):done :tag("th"):attr( 'data-sort-type', 'number' ):wikitext("Tier 7"):done :tag("th"):attr( 'data-sort-type', 'number' ):wikitext("Tier 9"):done :tag("th"):attr( 'data-sort-type', 'number' ):wikitext("Tier 11"):done :tag("th"):attr( 'data-sort-type', 'number' ):wikitext("Tier "..#TIERS):done local function getPrice(minion, tier) return p._minionProfit(minion, tier, diam_spread, fuel, crystal, bonus, expander, web, sc3000, input_time, false) end -- Store as a list first so we can sort it	local rowData = {} for minion,_ in pairs(minionData) do		local minionText = minion if minion=="Chicken" then minionText = minionText..'ⓘ' end if minion=="Gravel" then minionText = minionText..'ⓘ' end rowData[#rowData+1] = { minionText, getPrice(minion, 1), getPrice(minion, 3), getPrice(minion, 5), getPrice(minion, 7), getPrice(minion, 9), getPrice(minion, 11), getPrice(minion, #TIERS) } end -- Now sort minion from best to worst in terms of profit table.sort(rowData, function(a,b) 		if type(a[6]) == 'string' or type(b[6]) == 'string' then			return false		else			return a[6] > b[6]		end	end) local function makeCoin(num) if type(num) == 'string' then return string.blankCell end return tostring(formatCoins(math.ceil(num))) end -- now add rows to the table for _,data in ipairs(rowData) do		wikitable:tag("tr") :tag("td"):wikitext(data[1]):done :tag("td"):wikitext(makeCoin(data[2])):done :tag("td"):wikitext(makeCoin(data[3])):done :tag("td"):wikitext(makeCoin(data[4])):done :tag("td"):wikitext(makeCoin(data[5])):done :tag("td"):wikitext(makeCoin(data[6])):done :tag("td"):wikitext(makeCoin(data[7])):done :tag("td"):wikitext(data[8] and makeCoin(data[8]) or string.blankCell):done end return tostring(wikitable) end

- -- Template: Minion Drops Table -- -- Returns a table with minion drops - function p.minionDropsTable(frame) local args = getArgs(frame) local minion = args.minion or args.m or args[1] or title return p._minionDropsTable(minion) end

function p._minionDropsTable(minion) local notBazaarItems = { ['Cactus Green'] = 1, ['White Wool'] = 2, ['Egg'] = 3, ['Poisonous Potato'] = 10, ['Iron Ore'] = 3, ['Gold Ore'] = 3 }	minion = minion:lower minion = minion:match('(.+) minion') or minion local needsToBeDividedByTwo = minion == "fishing" and true or false local dropData = minionData[string._ucfirst(minion)].items local hasNotBazaarItem, hasCondition = {false, false} for _,v in pairs(dropData) do		if notBazaarItems[v.item] then hasNotBazaarItem = true end if v.condition			then hasCondition = true	 end end local wikitable = mw.html.create('table'):addClass('wikitable') local row = wikitable:tag('tr') row:tag('th'):attr({rowspan=2, colspan=2}):wikitext('Items'):done row:tag('th'):attr({rowspan=2}):wikitext('Chance'):done row:tag('th'):attr({rowspan=2}):wikitext('Average per harvest'):done if hasCondition then row:tag('th'):attr({rowspan=2}):wikitext('Condition'):done end row:tag('th'):attr({rowspan=2}):wikitext('Experience per Item'):done row:tag('th'):attr({colspan=2}):wikitext(hasNotBazaarItem and 'Bazaar/Merchant Sell Price' or 'Bazaar Sell Price'):done row:done row = wikitable:tag('tr') row:tag('th'):wikitext('1 Item'):done row:tag('th'):wikitext('Stack'):done row:done for _,v in pairs(dropData) do		row = wikitable:tag('tr') row:tag('td'):wikitext(slot{ v.item }):done row:tag('td'):wikitext(..v.item..):done local chance = '100%' if v.avg < 1 then if needsToBeDividedByTwo then chance = (v.avg/2*100)..'%' else chance = (v.avg*100)..'%' end end row:tag('td'):wikitext(chance):done row:tag('td'):wikitext(v.avg):done if v.condition then row:tag('td'):wikitext(processItem(v.condition)):done elseif hasCondition then row:tag('td'):wikitext(string.blankCell):done end if v.exp then row:tag('td'):wikitext(v.exp):done else row:tag('td'):wikitext('?'):done end if notBazaarItems[v.item] then row:tag('td'):wikitext(tostring(formatCoins(notBazaarItems[v.item]))):done row:tag('td'):wikitext(tostring(formatCoins(notBazaarItems[v.item]*64))):done else row:tag('td'):wikitext(tostring(bazaar._calcMaterialBuyPrices(, 'buy', true))):done row:tag('td'):wikitext(tostring(bazaar._calcMaterialBuyPrices(, 'buy', true))):done end row:done end return tostring(wikitable) end

-- Finish Module -- return p --''