Module:Rotations/Shattered Worlds

From the RuneScape Wiki, the wiki for all things RuneScape
Jump to navigation Jump to search
Module documentation
This documentation is transcluded from Module:Rotations/Shattered Worlds/doc. [edit] [history] [purge]
This module does not have any documentation. Please consider adding documentation at Module:Rotations/Shattered Worlds/doc. [edit]
Module:Rotations/Shattered Worlds's function future is invoked by Template:Shattered Worlds future challenges.
Module:Rotations/Shattered Worlds's function today is invoked by Template:Shattered Worlds current challenges.
Module:Rotations/Shattered Worlds requires Module:Array.
Module:Rotations/Shattered Worlds requires Module:Purge.
Module:Rotations/Shattered Worlds requires Module:Rotations.
Module:Rotations/Shattered Worlds requires Module:Rotations/RsRandom.
Module:Rotations/Shattered Worlds requires Module:Rotations/Shattered Worlds/data.
Function list
L 28 — wednesday
L 32 — getDates
L 37 — p.get
L 42 — getSlots
L 52 — p.api
L 56 — p._api
L 92 — makeFullRow
L 133 — p.today
L 138 — p._today
L 170 — makeShortRow
L 225 — p.future
L 232 — p._future
L 240 — parseOffsetsToString
L 302 — p.listItems
L 356 — p.nextItem
L 368 — p._nextItem
L 442 — check_count

--[=[
-- Rotation of the [[Shattered Worlds]] weekly challenges
--]=]
local p = {}

local rotations = require('Module:Rotations')
local rsrandom = require('Module:Rotations/RsRandom')
local purge = require('Module:Purge')._purge
local removeDuplicates = require('Module:Array').unique
local lang = mw.getContentLanguage()

local SECS_IN_DAY = 60 * 60 * 24

local challenges;
local mutators;
do
	local _data = require( "Module:Rotations/Shattered Worlds/data" );
	challenges = _data.challenges;
	mutators = _data.mutators;
end

local rewards = {
	['Bronze'] = '250,000',
	['Silver'] = '1,000,000',
	['Gold'] = '7,500,000'
}

function wednesday(runedate)
	return math.floor(runedate/7)*7
end

local function getDates( rot_index )
	local stock_date = os.date( "[[%d %B]] [[%Y]]", os.time() + rot_index * SECS_IN_DAY * 7 ):gsub( "%[0", "[" )
	return {day_diff = day_diff, rot_index = rot_index, stock_date = stock_date}
end

function p.get(runedate, k, n)
	local seed = runedate * (2 ^ 32) + (runedate % k)
	return rsrandom.nextInt(seed, n)
end

local function getSlots(offset)
	local runedate = wednesday(rsrandom.runeDate_today() + offset * 7)
	local index = p.get(runedate, 1, 8) + 1
	return {
		bronze = challenges[1][index],
		silver = challenges[2][index],
		gold = challenges[3][index]
	}
end

function p.api(frame)
	local args = frame:getParent().args
	return p._api(args.offset, args.format)
end
function p._api(offset, format)
	offset = tonumber(offset or 0) or 0
	local slots = getSlots(offset)
	local challenge_bronze = slots.bronze
	local challenge_silver = slots.silver
	local challenge_gold = slots.gold
	
	if format == 'simple' then
		return string.format('@%s¦%s¦%s@', challenge_bronze.name, challenge_silver.name, challenge_gold.name)
	end
	if format == 'json' then
		local jsonret = {}
		jsonret['timestamp'] = os.time()
		jsonret['challenges'] = {challenge_bronze, challenge_silver, challenge_gold}
		return mw.text.jsonEncode(jsonret)
	end
	local ret = mw.html.create('div')
	ret	:tag('span')
			:addClass('slotA')
				:tag('span'):addClass('name'):wikitext(a):done()
				:tag('span'):addClass('cost'):wikitext(a_d.cost):done()
				:tag('span'):addClass('use'):wikitext(a_d.use):done()
			:done()
			:addClass('slotB')
				:tag('span'):addClass('name'):wikitext(b):done()
				:tag('span'):addClass('cost'):wikitext(b_d.cost):done()
				:tag('span'):addClass('use'):wikitext(b_d.use):done()
			:done()
			:addClass('slotC')
				:tag('span'):addClass('name'):wikitext(c):done()
				:tag('span'):addClass('cost'):wikitext(c_d.cost):done()
				:tag('span'):addClass('use'):wikitext(c_d.use):done()
			:done()
	return ret	
end

function makeFullRow(rank, challenge)
	local tr = mw.html.create('tr')
	tr	:tag('td')
			:wikitext(rank)
		:done()
		:tag('td')
			:wikitext(challenge.name)
		:done()
		:tag('td')
			:wikitext(challenge.description)
		:done()
	
	if challenge.mutators ~= nil then
		local td = tr:tag('td')
		for n,mutator_id in ipairs(challenge.mutators) do
			local mutator = mutators[mutator_id]
			local mutator_icon = string.format("[[File:%s mutator.png|link=Shattered Worlds#Mutators|27px]] ", mutator.name)
			td	:wikitext(mutator_icon)
			td	:tag('span')
					:addClass('hover-text')
					:attr('title', mutator.description)
					:wikitext(mutator.name)
				:done()
			if n ~= #challenge.mutators then
				td:wikitext(', ')
			end
		end
		td:done()
	else
		tr	:tag('td')
				:addClass('table-na')
				:wikitext('N/A')
			:done()
	end
	
	tr	:tag('td')
			:wikitext(rewards[rank])
		:done()
	return tr
end

function p.today()
	local slots = getSlots(0)
	return p._today(slots.bronze, slots.silver, slots.gold)
end

function p._today(challenge_bronze, challenge_silver, challenge_gold)
	local t = mw.html.create('table')
	t	:addClass('wikitable')
		:tag('tr')
			:tag('th')
				:wikitext('Rank')
			:done()
			:tag('th')
				:wikitext('Challenge')
			:done()
			:tag('th')
				:wikitext('Description')
			:done()
			:tag('th')
				:wikitext('Mutators')
			:done()
			:tag('th')
				:wikitext('[[Shattered anima]] reward')
			:done()
	t	:node(makeFullRow('Bronze', challenge_bronze))
		:node(makeFullRow('Silver', challenge_silver))
		:node(makeFullRow('Gold', challenge_gold))
	local runedate_wednesday = wednesday(rsrandom.runeDate_today())
	local cd = mw.html.create('span')
	cd	:addClass('countdown')
		:attr('data-end', 'stop')
		:tag('span')
			:addClass('countdowndate')
			:wikitext(lang:formatDate("j F Y", "27 February 2002 + " .. (runedate_wednesday + 7) .. " days"))
	return string.format("''These are the challenges for the week of %s ([[Runedate]] %s).'' <sup>%s</sup><br>Valid for %s\n%s", lang:formatDate('[[j F]] [[Y]]', "27 February 2002 + " .. runedate_wednesday .. " days"), runedate_wednesday, tostring(purge()), tostring(cd), tostring(t))
end

function makeShortRow(offset, onlyitem_list)
	local slots = getSlots(offset)
	local challenge_bronze = slots.bronze
	local challenge_silver= slots.silver
	local challenge_gold = slots.gold
	local class_bronze, class_silver, class_gold = nil,nil,nil
	local founditems = {}
	
	if onlyitem_list then
		local foundonlyitem = false
		local highlightclass = 'table-bg-green'
		
		for _,onlyitem in ipairs(onlyitem_list) do
			if onlyitem == challenge_bronze.name then
				class_bronze = highlightclass
				table.insert(founditems, onlyitem)
				foundonlyitem = true
			end
			if onlyitem == challenge_silver.name then
				class_silver = highlightclass
				table.insert(founditems, onlyitem)
				foundonlyitem = true
			end
			if onlyitem == challenge_gold.name then
				class_ = highlightclass
				table.insert(founditems, onlyitem)
				foundonlyitem = true
			end
		end
		
		if not foundonlyitem then
			return {nil, {nil}}
		end
		founditems = removeDuplicates(founditems)
	end
	
	local tr = mw.html.create('tr')
	tr	:tag('td')
			:wikitext(getDates(offset).stock_date)
		:done()
		:tag('td')
			:wikitext(challenge_bronze.name)
			:addClass(class_bronze)
		:done()
		:tag('td')
			:wikitext(challenge_silver.name)
			:addClass(class_silver)
		:done()
		:tag('td')
			:wikitext(challenge_gold.name)
			:addClass(class_gold)
		:done()
	return {tr, founditems}
end

function p.future(frame)
	local args = frame:getParent().args
	local s = args.start or args[1] or 0
	local e = args['end'] or args[2] or 5
	return p._future(s,e)
end

function p._future(startOffset, endOffset)
	-- enforce numbers
	startOffset = tonumber(startOffset or 0) or 0
	endOffset = tonumber(endOffset or 5) or 5
	-- switch around if start/end mixed
	if startOffset > endOffset then
		startOffset, endOffset = endOffset, startOffset
	end
	local function parseOffsetsToString()
		local start_s = math.abs(startOffset) > 1 and "s" or ""
		local end_s = math.abs(endOffset) > 1 and "s" or ""
		if startOffset == endOffset then
			if startOffset > 0 then
				return string.format("%s week%s after this week", startOffset, start_s)
			elseif startOffset < 0 then
				return string.format("%s week%s before this week", startOffset * -1, start_s)
			elseif startOffset == 0 then
				return "this week"
			end
		else
			local s = ""
			if startOffset > 0 then
				s = string.format("%s%s week%s after this week", s, startOffset, start_s)
			elseif startOffset < 0 then
				s = string.format("%s%s week%s before this week", s, startOffset * -1, start_s)
			elseif startOffset == 0 then
				s = string.format("%sthis week", s)
			end
			s = string.format("%s to ", s)
			if endOffset > 0 then
				s = string.format("%s%s week%s after this week", s, endOffset, end_s)
			elseif endOffset < 0 then
				s = string.format("%s%s week%s before this week", s, endOffset * -1, end_s)
			elseif endOffset == 0 then
				s = string.format("%sthis week", s)
			end
			return s
		end
		return "(error: start and/or end not numbers)"
	end

	local t = mw.html.create("table")
	t:addClass("wikitable sticky-header")
		:tag("tr")
		:tag("th")
		:wikitext("Date")
		:done()
		:tag("th")
		:wikitext("Bronze")
		:done()
		:tag("th")
		:wikitext("Silver")
		:done()
		:tag("th")
		:wikitext("Gold")
		:done()
	
	for i=startOffset,endOffset,1 do
		t:node(makeShortRow(i)[1])
	end
	
	return string.format(
		"This table shows challenges for %s (%s)<sup>%s</sup>\n%s",
		parseOffsetsToString(),
		lang:formatDate("[[j F]] [[Y]]", "27 February 2002 + " .. rsrandom.runeDate_today() .. " days"),
		purge(),
		tostring(t)
	)
end

function p.listItems()
	local challengesBronze = { 
		'Novice World Traveller', 'Shocking', 'Novice Adventurer', 'The Undead Rise', 'British Summer Time',
		'Bug Squasher', 'Instability', 'Automata Adjustment'
	}
	local challengesSilver = { 'Arachnophobia', 'Experienced World Traveller', 'Zones', 'Experienced Adventurer',
		'Hella Mental', 'Demon Slayer', 'Starved', 'Shelve the Elves'
	}
	local challengesGold = { 'A Quick Trip to Hell', 'Master World Traveller', 'Stardust Crusade', 'Just Keep Moving',
		'Master Adventurer', 'The Floor is Lava', 'Un-undead The Undead', 'Knightmare'
	}
	
	local t = mw.html.create('table')
	t	:addClass('wikitable align-center-1 align-center-4')
		:tag('tr')
			:tag('th'):attr('colspan', 5):wikitext('Slot 1 (always)'):done()
		:done()
		:tag('tr')
			:tag('th'):attr('colspan', 2):wikitext('Item'):done()
			:tag('th'):wikitext('Cost'):done()
			:tag('th'):wikitext('Quantity'):done()
			:tag('th'):wikitext('Use'):done()
		:done()
		:node(makeFullRow('Uncharted island map (Deep Sea Fishing)', challenges['Uncharted island map (Deep Sea Fishing)']))
		:tag('tr')
			:tag('th'):attr('colspan', 5):wikitext('Slots 2 and 3'):done()
		:done()
		:tag('tr')
			:tag('th'):attr('colspan', 2):wikitext('Item'):done()
			:tag('th'):wikitext('Cost'):done()
			:tag('th'):wikitext('Quantity'):done()
			:tag('th'):wikitext('Use'):done()
		:done()
		
	for i,v in ipairs(challengesBronze) do
		t:node(makeFullRow(v, challenges[v]))
	end
		
	t	:tag('tr')
			:tag('th'):attr('colspan', 5):wikitext('Slot 4'):done()
		:done()
		:tag('tr')
			:tag('th'):attr('colspan', 2):wikitext('Item'):done()
			:tag('th'):wikitext('Cost'):done()
			:tag('th'):wikitext('Quantity'):done()
			:tag('th'):wikitext('Use'):done()
		:done()
	
	for i,v in ipairs(challengesSilver) do
		t:node(makeFullRow(v, challenges[v]))
	end
	return t
end

function p.nextItem(frame)
	local args = frame:getParent().args
	local item = args.item
	if not item then
		item = mw.title.getCurrentTitle().text
	end
	local item_list = mw.text.split(item,",%s*")
	local boolconverter = {["true"]=true,["false"]=false}
	local individual = boolconverter[args.individual]
	return p._nextItem(item_list, tonumber(args.number), tonumber(args.limit), individual)
end

function p._nextItem(item_list, n, dayslimit, individual)
	local list = {
		-- Bronze challenges
		['Novice World Traveller'] = 'a',
		['Shocking'] = 'a',
		['Novice Adventurer'] = 'a',
		['The Undead Rise'] = 'a',
		['British Summer Time'] = 'a',
		['Bug Squasher'] = 'a',
		['Instability'] = 'a',
		['Automata Adjustment'] = 'a',
	
		-- Silver challenges
		['Arachnophobia'] = 'b',
		['Experienced World Traveller'] = 'b',
		['Zones'] = 'b',
		['Experienced Adventurer'] = 'b',
		['Hella Mental'] = 'b',
		['Demon Slayer'] = 'b',
		['Starved'] = 'b',
		['Shelve the Elves'] = 'b',
	
		-- Gold challenges
		['A Quick Trip to Hell'] = 'c',
		['Master World Traveller'] = 'c',
		['Stardust Crusade'] = 'c',
		['Just Keep Moving'] = 'c',
		['Master Adventurer'] = 'c',
		['The Floor is Lava'] = 'c',
		['Un-undead The Undead'] = 'c',
		['Knightmare'] = 'c'
	}
	
	if not n then
		n = 5
	end
	if not dayslimit then
		dayslimit = 200
	end
	if not individual or #item_list == 1 then
		individual = false
	end
	
	local unrecognised_item = ""
	for _, item in ipairs(item_list) do
		if not list[item] then
			unrecognised_item = unrecognised_item .. "Unrecognised item ''"..item.."''. Please enter the item name exactly.<br>"
		end
	end
	if unrecognised_item ~= "" then
		return unrecognised_item:sub(1, -5)
	end
	
	local t = mw.html.create('table')
	t	:addClass('wikitable sticky-header')
		:tag('tr')
			:tag('th')
				:wikitext('Date')
			:done()
			:tag('th')
				:wikitext('[[Runedate]]')
			:done()
			:tag('th')
				:wikitext('Slot A')
			:done()
			:tag('th')
				:wikitext('Slot B')
			:done()
			:tag('th')
				:wikitext('Slot C')
			:done()
	
	local d = 0
	
	function check_count(count_all_table, n)
		count_reached = true
		for _,v in pairs(count_all_table) do
			if v < n then
				count_reached = false
			end
		end
		return count_reached
	end
	
	local finalcount = math.huge
	
	if not individual then
		local count = 0
		while count < n and d < dayslimit do
			local r = makeShortRow(d, item_list)[1]
			if r then
				t:node(r)
				count = count + 1
			end
			d = d + 1
		end
		finalcount = count
	else
		local count = {}
		for _,item in ipairs(item_list) do
			count[item] = 0
		end
		
		local count_all = false
		while not count_all and d < dayslimit do
			local r = makeShortRow(d, item_list)
			if r[1] then
				t:node(r[1])
				for _,item in ipairs(r[2]) do
					count[item] = count[item] + 1
				end
			end
			count_all = check_count(count, n)
			d = d + 1
		end
		
		-- local test = ""
		-- for k,v in pairs(count) do
		-- 	test = test .. k .. ": " .. tostring(v) .. "<br>"
		-- end
		-- return test
		for _,v in pairs(count) do
			finalcount = math.min(finalcount, v)
		end
	end
	
	local simpleitem = ""
	for item_count = 1, #item_list do
		local item = item_list[item_count]
		
		local iteminfo = challenges[item]
		
		local andor = "or"
		if individual then
			andor = "and"
		end
		
		if item_count == #item_list then
			if #item_list == 1 then
				simpleitem = simpleitem..", "
			elseif #item_list == 2 then
				simpleitem = simpleitem.." " .. andor .. " "
			else
				simpleitem = simpleitem..", " .. andor .. " "
			end
		else
			simpleitem = simpleitem..", "
		end
		simpleitem = simpleitem.."<i>"..item.."</i>"
	end
	simpleitem = simpleitem:sub(3)
	
	local be_string = "is"
	if individual then
		be_string = "are"
	end
	
	local intro = string.format('This table shows the next %s dates<sup>%s</sup> on which %s %s available.', finalcount, tostring(purge()), simpleitem, be_string)

	return intro .. '\n' .. tostring(t)
end

return p