Module:Sandbox/MonkeysHK

local getArgs = require('Module:Arguments').getArgs local mergeArgsSyntax = require('Module:Arguments').mergeArgsSyntax local loader = require('Module:Loader')

local string, table, yesno, ui, uiText, Interface, potion = loader.require('String', 'Table', 'Yesno', 'UI', 'UIText', 'UI/Core', 'Potion') local collectionData, armorSets, potionData = loader.loadData('Collection/Data', 'Armor/Sets', 'Potion/Data')

local aliasesCache = require('Module:Cache').slotAliasesCache local pagetitle = mw.title.getCurrentTitle local p = {}

-- first row positions for Collection UI and Collection (Outer) Rewards UI local firstRowPositions = { --1 { {3, 5} },	--2 { {3, 4}, {3, 6} },	--3 { {3, 4}, {3, 5}, {3, 6} },	--4 { {3, 2}, {3, 4}, {3, 6}, {3, 8} },	--5 { {3, 3}, {3, 4}, {3, 5}, {3, 6}, {3, 7} },	--6 { {3, 2}, {3, 3}, {3, 4}, {3, 6}, {3, 7}, {3, 8} },	--7 { {3, 2}, {3, 3}, {3, 4}, {3, 5}, {3, 6}, {3, 7}, {3, 8} },	--8 { {3, 1}, {3, 2}, {3, 3}, {3, 4}, {3, 6}, {3, 7}, {3, 8}, {3, 9} }, } -- X/Y positions for Collection (Inner) Rewards UI local rewardUIPositions = { -- 1 { {3, 5} },	-- 2 { {3, 4}, {3, 6} },	-- 3 { {3, 3}, {3, 5}, {3, 7} },	-- 4 { {3, 2}, {3, 4}, {3, 6}, {3, 8} },	-- 5 { {2, 2}, {2, 4}, {2, 6}, {2, 8}, {4, 5}, },	-- 6 { {2, 2}, {2, 4}, {2, 6}, {2, 8}, {5, 4}, {5, 6}, },	-- 7 { {2, 2}, {2, 4}, {2, 6}, {2, 8}, {3, 3}, {3, 5}, {3, 7}, },	-- 8 { {2, 2}, {2, 4}, {2, 6}, {2, 8}, {4, 2}, {4, 4}, {4, 6}, {4, 8}, },	-- 9 { {2, 4}, {2, 5}, {2, 6}, {3, 4}, {3, 5}, {3, 6}, {4, 4}, {4, 5}, {4, 6}, },	--10 { {2, 4}, {2, 5}, {2, 6}, {2, 7}, {3, 4}, {3, 5}, {3, 6}, {4, 4}, {4, 5}, {4, 6}, },	--11 { {2, 3}, {2, 4}, {2, 5}, {2, 6}, {2, 7}, {3, 4}, {3, 5}, {3, 6}, {4, 4}, {4, 5}, {4, 6}, }, } -- maximum of three rows of rewards (or 27 rewards) will be processed local function calcXY(total, i, isInner) if isInner then local pos = rewardUIPositions[total][i] return pos[1], pos[2] else local frp = firstRowPositions[total] local x, y		if frp then local pos = frp[i] x, y = pos[1], pos[2] else x = 3 + (math.floor(i / 9) - (i % 9 == 0 and 1 or 0)) y = i % 9 y = y == 0 and 9 or y		end return x, y	end end local numToEng = { 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty', 'twenty-one', 'twenty-two', 'twenty-three', 'twenty-four', 'twenty-five', 'twenty-six', 'twenty-seven' }

local function wrapdiv(s) return (' %s '):format(s) end local function frm(...) return table.concat(table.map({...}, function(s) return string.lower(string.gsub(s, ' ', '-')) end), '-') end local function typeformatter(item, _type, ttcol) local str if _type:match('[Ee]xperience') then str = ('&8+&3%s %s'):format(item, _type) elseif _type:match('[Uu]pgrade') then str = ('&a%s Upgrade'):format(item) elseif _type:match('[Cc]oming [Ss]oon') then str = ('&%s%s &8(&4COMING SOON&8)'):format(ttcol, item) elseif _type:match('[Dd]warven [Ff]orge [Rr]ecipe') then str = ('&%s%s &6Dwarven Forge Recipe'):format(ttcol, item) elseif _type:match('[Nn]one') then str = ('&%s%s'):format(ttcol, item) elseif _type:match('[Rr]ecipe') and item:match('Minion$') then str = ('&%s%s &7Recipes'):format(ttcol, item) else str = ('&%s%s &7%s'):format(ttcol, item, _type) end return str end local function getRarityCol(item) -- For getting special case rarity colors local function specialCaseColors(item) if item:match('[Mm]inion$') then return '9' end if item:match('[Pp]otion$') then return 'e' end -- if item:match('Enchanted Book') then return '9' end if item:match('[Cc]oming [Ss]oon') then return 'c' end if tonumber(item) then return '3' end end local tooltip = aliasesCache:get(item) local tooltipRarity = tooltip and tooltip.title and tooltip.title:match('^&([a-z0-9])') or nil return tooltipRarity or specialCaseColors(item) or 'f' end local function getRewardStr(rewards, req) local rewardStr = ('&7Reward%s:/%s'):format(		#rewards > 1 and 's' or '',		table.concat(table.map(rewards, function(v)			local rname = v[1]			v = type(v) == 'table' and v or {v}			return v.str or ('&#160;&#160;%s'):format( typeformatter(rname, v.type or 'recipe', v.ttcolor or getRarityCol(rname)) )		end), '/')	) return ('&f%s &e0&6\\/&e%s&7//%s'):format(		('-'):rep(20),		string._formatShortNum(string._toNumber(req)),		rewardStr	) end local function toShowRecipe(s) return s:match('[Rr]ecipe') or s:match('[Cc]oming [Ss]oon') end

- -- Template: Collection UI -- -- Main collection UI module, can dive deep into sub-interfaces! -- Generates - function p.collectionUI(frame) local args = getArgs(frame) local res = p._collectionUI(args) return res end local function subexplode(rewardTb) local tb = {} for _, reward in ipairs(rewardTb.reward) do		-- checks all conditions when reward name needs explosion if armorSets[reward[1]] then for _, w in ipairs(armorSets[reward[1]]) do				local t = table.deepCopy(reward) t[1] = w				table.push(tb, t)			end elseif reward.explode then for _, v in ipairs(reward.explode) do				local t = table.deepCopy(reward) if type(v) == 'table' then for _, w in ipairs(table.merge(table.namedKeys(v), {1})) do						t[w] = (v[w] ~= '__NIL__' and v[w] or nil) end else t[1] = v				end table.push(tb, t)			end else table.push(tb, reward) end end rewardTb.reward = tb	return rewardTb end local function explode(collData) for i, rewardTb in ipairs(collData) do		subexplode(rewardTb) end return collData end local function parseTextList(s) local rewards = {} local count = 0 local function g(s) return s and s:gsub('\255', ',') or nil end for i, v in ipairs(string.split((s:gsub('\\,', '\255')), '\n')) do		local _type v = string.methods(v):_trim if v:getValue ~= '' then if v:charAt(1) == '*' then _type = v:charAt(2) == '*' and 'item' or 'amount' end if _type == 'amount' then -- * amount[, goto] local amount, _goto = unpack(string.split(v:sub(2), "%s*,%s*")) count = count + 1 rewards[count] = { required = g(amount), _goto = g(_goto and _goto:gsub('^goto%-', '') or nil), reward = {}, }			elseif _type == 'item' then -- ** reward[, type][, rarity] local item, type, rarity = unpack(string.split(v:sub(3), '%s*,%s*')) item = g(item or v:sub(3)) table.push(rewards[count].reward, {					item,					ttcolor = g((rarity and rarity ~= '') and uiText.getFormatting(rarity) or nil),					type = g((type and type ~= "") and string.ucfirst(type)),				}) end end end return rewards end function p._collectionUI(args) checkType(1, args, { 'table' }) assertTrue(args[1], 'No collection specified', 2) local collection = args[1] and args[1]:gsub('[Ee][Nn][Cc][Hh][Aa]?[Nn]?[Tt]?[Ee]?[Dd]? ?', ):gsub('[Bb][Ll][Oo][Cc][Kk] ?[Oo]?[Ff]? ?', ) local dt = table.deepCopy(collectionData[collection], true) if not dt then error('Collection ' .. collection .. ' not found in data') end -- Prepare the list from input local inputs = args[2] and parseTextList(args[2]) or {} for i = 1, #inputs do		-- process/override args if inputs[i] then dt[i] = dt[i] or {} dt[i].reward = dt[i].reward or {} dt[i].required = inputs[i].required or dt[i].required dt[i]._goto = inputs[i]._goto or dt[i]._goto local dtreward, listreward = dt[i].reward, inputs[i].reward or {} for j = 1, #listreward do				dtreward[j] = dtreward[j] or {} dtreward[j][1] = listreward[j][1] or dtreward[j][1] dtreward[j].ttcolor = listreward[j].ttcolor or dtreward[j].ttcolor dtreward[j].type = listreward[j].type or dtreward[j].type end end end explode(dt) local id = args.id or frm(collection, 'default') -- Now prepare parent UI. local ui = Interface({		collection and collection .. ' Collection' or pagetitle.rootText,		id = id, -- this is 		return_text = args.return_text or args.goback,		return_link = args.return_link,		return_id = args.return_id,		hide = args.hide,	}) ui:setSlot(1, 5, {		args.title or collection,		link = 'none',		title = '&e' .. (args.title or collection) .. ' Collection',		text = ('&7View all your %s Collection/&7progress and rewards!//&7Total Collected: &e0'):format(collection),	}) local rows, total = 1, #dt for i, rewardTb in ipairs(dt) do		if i > 27 then break end -- Processes upwards of 3 rows of rewards -- calculate X, Y		local x, y = calcXY(total, i)		-- advance row if i % 9 == 0 and i > 0 then rows = rows + 1 end local title = ('&%s%s %s'):format(i == 1 and 'e' or 'c', collection, string._toRoman(i)) local text = ('/&7Progress: &e0&6%%/%s//&eClick to view rewards!'):format(			getRewardStr(rewardTb.reward, rewardTb.required)		) ui:setSlot(x, y, {			(i == 1 and 'Yellow' or 'Red') .. ' Stained Glass Pane,' .. i,			link = 'none',			class = Interface.makeGotoClass(rewardTb._goto or frm(collection, numToEng[i])),			title = title,			text = text,		}) end -- Now prepare all children UIs. local uis = {} for i, rewardTb in ipairs(dt) do		-- create UIs local settings = { collection, i,			id = ('%s'):format(frm(collection, numToEng[i])), return_text = ('&7To %s Collection'):format(collection), return_id = id, hide = false, predefined_table = rewardTb, }		if table.some(rewardTb.reward, function(k, v)			return toShowRecipe(v.type)		end) then table.push(uis, p._collectionRewardsUI(settings)) end end return wrapdiv(table.concat( table.merge({ tostring(ui) }, uis) )) end

- -- Template: Collection Rewards UI -- -- Creates a Collection Rewards UI. -- Generates -n - function p.collectionRewardsUI(frame) local args = getArgs(frame) return p._collectionRewardsUI(args) end function p._collectionRewardsUI(args) checkType(1, args, { 'table' }) -- Handling custom inputs from the templates local coll, tier, required, items = args[1], tonumber(args[2]), args[3], args[4] coll = coll and coll:gsub('[Ee][Nn][Cc][Hh][Aa]?[Nn]?[Tt]?[Ee]?[Dd]? ?', ):gsub('[Bb][Ll][Oo][Cc][Kk] ?[Oo]?[Ff]? ?', ) if not items then items, required = required, nil end items = type(items) == 'string' and mergeArgsSyntax({ items }) or items or {} items = table.filter(items, function(v)		local s = string.trim(v)		return s ~= '' and s ~= 'nil'	end) -- Getting data from database and parsing, letting custom input to override stuff. -- Is skipped when predefined_table (the "exploded" rewards table) is passed in. local dt	local collFullname = ('%s %s'):format(coll, string._toRoman(tier or 1)) if args.predefined_table then dt = args.predefined_table else dt = table.deepCopy(collectionData[coll] and collectionData[coll][tier] or {}, true) dt.required = required or dt.required for i = 1, math.max(#dt, #items) do			-- process/override args if items[i] then local item, id, title, text = pipeline(items[i], string.trim, '%s*,%s*', string.unpackedSplit) -- these are custom inputs -- the dt table is info from the database, now we let custom inputs override them dt.reward = dt.reward or {} dt.reward[1] = item or dt.reward[1] dt.reward.id = id or dt.reward.id				dt.reward.title = title or dt.reward.title dt.reward.text = text or dt.reward.text end end subexplode(dt) end -- Now prepare the parent UI. local topText = collFullname .. ' Rewards' local ui = Interface({		topText,		id = args.id, -- this is -n		return_text = args.return_text or args.goback,		return_link = args.return_link,		return_id = args.return_id, -- this is 		hide = args.hide,	}) ui:setSlot(1, 5, {		coll,		link = 'none',		title = ('&%s%s'):format(tier > 1 and 'c' or 'e', collFullname),		text = ('&7View all your %s/&7Collection rewards!//&7Progress: &e0&6%%/%s')			:format(collFullname, getRewardStr(dt.reward, dt.required or 'unknown'))	}) local uis = {} local todo = table.filter(dt.reward, function(v)		return toShowRecipe(v.type)	end) for i, reward in ipairs(todo) do		local rname = reward[1] local isMinion, isPotion = rname:match('Minion$'), rname:match('Potion$') local tooltip = reward.type:match('[Cc]oming [Ss]oon') and aliasesCache:get('Coming Soon') or aliasesCache:get(rname) local itemTitle = reward.title or (tooltip and tooltip.title) local itemText = reward.desc or (tooltip and tooltip.text) if isMinion then -- replace minion tooltip to what's shown in collection itemTitle = itemTitle:gsub(' %S+$', ' Recipes') itemText = itemText:gsub('%s*Minions[%s/&7]*also[%s/&7]*work[%s/&7]*when[%s/&7]*you[%s/&7]*are[%s/&7]*offline!.*$', '') end local x, y = calcXY(#todo, i)		ui:setSlot(x, y, {			rname .. (isMinion and ' I' or ''),			link = 'none',			class = Interface.makeGotoClass(frm(args.id, 'inner')),			title = (itemTitle or 'none'):gsub(',', '\\,'),			text = (itemText and itemText .. (item == 'Coming Soon' and  or '//&eClick to view recipe' .. ((isMinion or isPotion) and 's' or ) .. '!') or 'none'):gsub(',', '\\,'),		}) end -- Now prepare children UIs. for i, reward in ipairs(todo) do		local rname = reward[1] local isMinion, isPotion = rname:match('Minion$'), rname:match('Potion$') local settings = { rname, coll, tier, id = ('%s'):format(frm(args.id, 'inner')), -- this is -n-inner return_id = args.id, -- this is -n hide = false, return_text = '&7 To ' .. topText, }		if isMinion then table.push(uis, p._minionRecipesUI(settings)) elseif isPotion then table.push(uis, p._potionRecipesUI(settings)) end end return tostring(ui) .. table.concat(uis) end

- -- Template: (none) -- -- Creates a Recipe Selector. -- Generates -n-inner only - function p.recipeSelector(frame) local args = getArgs(frame) return p._recipeSelector(args) end function p._recipeSelector(args) checkType(1, args, { 'table' }) -- Handling custom inputs from the templates local items = args[1] topText = args.title and args.title:gsub('[Ee][Nn][Cc][Hh][Aa]?[Nn]?[Tt]?[Ee]?[Dd]? ?', ):gsub('[Bb][Ll][Oo][Cc][Kk] ?[Oo]?[Ff]? ?', ) items = type(items) == 'string' and mergeArgsSyntax({ items }) or items or {} items = table.filter(items, function(v)		local s = string.trim(v)		return s ~= '' and s ~= 'nil'	end) -- Getting data from database and parsing, letting custom input to override stuff. -- Is skipped when predefined_table (the "exploded" rewards table) is passed in. local dt = {} for i = 1, #items do		-- process/override args if items[i] then local item, id, title, text = pipeline(items[i], string.trim, '%s*,%s*', string.unpackedSplit) table.push(dt, {				item,				id = id,				title = title,				text = text,			}) end end local ui = Interface({		topText,		id = args.id,		return_text = args.return_text or args.goback,		return_link = args.return_link,		return_id = args.return_id,		hide = args.hide,	}) for i, reward in ipairs(dt) do		local rname = reward[1] local tooltip = aliasesCache:get(rname) local itemTitle = reward.title or (tooltip and tooltip.title) local itemText = reward.desc or (tooltip and tooltip.text) local x, y = calcXY(#dt, i, true) ui:setSlot(x, y, {			rname,			link = 'none',			class = Interface.makeGotoClass(frm(args.baseId, numToEng[i])),			title = (itemTitle or 'none'):gsub(',', '\\,'),			text = (itemText and itemText .. (item == 'Coming Soon' and '' or '//&eClick to view recipe!') or 'none'):gsub(',', '\\,'),		}) end -- Current implementation: This UI will not make children UIs, -- since Minion Recipes and Potion Recipes UI have varying handlers. return tostring(ui) end

- -- Template: Minion Recipes UI -- -- Creates a minion recipes UI -- Calls on -n-inner and -n-recipe-n - function p.minionRecipesUI(frame) local args = getArgs(frame) return p._minionRecipesUI(args) end function p._minionRecipesUI(args) checkType(1, args, 'table') local minion, col, tier = args.item or args[1], args.collection or args[2], args.reward_tier or args[3] if not tier then tier, col = col, minion end tier = string._toRoman(tier or 1) or 'I'	minion = (minion or pagetitle.rootText):gsub('%s[Mm]inion', '') col = (col or pagetitle.rootText):gsub('%s[Mm]inion', '') local back = ('To %s %s Rewards'):format(col, tier) -- Now prepare the parent UI. local fulllist = mergeArgsSyntax({ pipeline('*%s Minion $n\n', minion, string.format, 11, string._repeat):gsub('(%d+)', function(m) return string._toRoman(m) end) }) local parent = p._recipeSelector{ fulllist, title = minion .. ' Minion Recipes', hide = args.hide, id = args.id, -- this is -n-inner return_id = args.return_id, -- this is -n baseId = frm(args.return_id, 'recipe'), -- we want -n-recipe-n return_text = args.return_text or args.goback or back, }	-- Now prepare children UIs. return parent end

- -- Template: (none) -- -- Creates a potion recipes UI -- Calls on -n-inner and -n-recipe-n - function p.potionRecipesUI(frame) local args = getArgs(frame) return p._potionRecipesUI(args) end function p._potionRecipesUI(args) checkType(1, args, 'table') local pot, col, tier = args.item or args[1], args.collection or args[2], args.reward_tier or args[3] if not tier then tier, col = col, pot end tier = string._toRoman(tier or 1) or 1 pot = (pot or pagetitle.rootText):gsub('%s[Pp]otion', '') col = (col or pagetitle.rootText):gsub('%s[Pp]otion', '') local back = ('To %s %s Rewards'):format(col, tier) -- Now prepare the parent UI. local topText = pot .. ' Potion Recipes' local levels = potion._brewableCount(pot) local fulllist = mergeArgsSyntax({ pipeline('*%s $n Potion\n', pot, string.format, levels, string._repeat):gsub('(%d+)', function(m) return string._toRoman(m) end) }) local parent = p._recipeSelector{ fulllist, title = topText, hide = args.hide, id = args.id, -- this is -n-inner return_id = args.return_id, -- this is -n baseId = frm(args.return_id, 'recipe'), -- we want -n-recipe-n return_text = args.return_text or args.goback or back, }	-- Now prepare children UIs. local uis = {} for i = 1, levels do		table.push(uis, ui.potionUI{			('%s %s'):format(pot, i),			title = ('%s %s Potion Recipe'):format(pot, string._toRoman(i)),			hide = args.hide,			id = frm(args.return_id, 'recipe', numToEng[i]), -- we want -n-recipe-n			return_id = args.id, -- this is -n-inner			return_text = '&7To ' .. topText,		}) end return parent .. table.concat(uis) end

return p