FANDOM


--[[ <nowiki>
Various functions for activities that have time based rotations
--]]
local p = {}
 
local purge = require('Module:Purge')._purge
 
local seconds_in_day = 24 * 60 * 60
local seconds_in_minute = 60
 
local _on_ = 'status-active'
local _off_ = 'status-inactive'
 
local yesno = {
	[true] = _on_,
	[false] = _off_
}
 
local unit_seconds_from_name = {
	minute = seconds_in_minute,
	day = seconds_in_day
}
 
local lang = mw.language.new('en')
--[[
Adds a table row with a purge button to the table passed
Also gives the table an anchor id so that clicking purge will return readers to the table
--]]
function p.add_purge(builder,cols)
	local page_title = mw.title.getCurrentTitle().fullText
	local url = tostring(mw.uri.fullUrl(page_title..'#reload','action=purge'))
	builder:attr('id','reload')
	builder:tag('tr')
			:tag('td')
				:attr({ colspan = cols or 1 })
				:wikitext(purge('reload'))
			:done()
		:done()
end
 
function p.add_purge2(table, td)
	local page_title = mw.title.getCurrentTitle().fullText
	local url = tostring(mw.uri.fullUrl(page_title..'#reload','action=purge'))
	table:attr('id','reload')
	td:wikitext('&nbsp;'..purge('reload'))
	:done()
end
 
--[[
Returns the plural of the word
--]]
function p.plural(word, n, plural)
	if n == 1 then
		return word
	else
		return plural or (word .. 's')
	end
end
 
--[[
 
--]]
function p.on_off(on_time, total_time, offset, unit_seconds)
	local units_after_utc = math.floor(os.time() / unit_seconds)
	local units_into_start = (units_after_utc + offset) % total_time
 
	local on = units_into_start < on_time
 
	local units_until_change
	if on then
		units_until_change = on_time - units_into_start
	else
		units_until_change = total_time - units_into_start
	end
 
	return on, units_until_change
end
 
--[[
Returns a number that can be used to identify the rotation based on:
* The number of days per rotation
* The number of rotations available
* The offset of days such that Tuesday 1 January 1970 + offset would be the starting day for rotation 1 (this should be between 0 and 6)
 
Also returns a second value that determines how many days until the next rotation
--]]
function p.rotation_days(interval, rotation_count, offset)
	local days_after_utc = math.floor(os.time() / seconds_in_day)
	local days_into_period = (days_after_utc + offset) % (interval * rotation_count)
 
	local rotation = math.floor(days_into_period / interval) + 1
	local days_until_next_rotation = interval - days_into_period % interval
 
	return rotation, days_until_next_rotation
end
 
--[[
Returns a number that can be used to identify the rotation based on:
* The number of minutes per rotation
* The number of rotations available
* The offset of minutes such that Tuesday 1 January 1970 + offset would be the starting time for rotation 1
 
Also returns a second value that determines how many minutes until the next rotation
--]]
function p.rotation_minutes(interval, rotation_count, offset)
	local minutes_after_utc = math.floor(os.time() / seconds_in_minute)
	local minutes_into_period = (minutes_after_utc + offset) % (interval * rotation_count)
 
	local rotation = math.floor(minutes_into_period / interval) + 1
	local minutes_until_next_rotation = interval - minutes_into_period % interval
 
	return rotation, minutes_until_next_rotation
end
 
--[[
 
--]]
function p.simple_on_off(on_time, total_time, offset, unit_name)
	local unit_seconds = unit_seconds_from_name[unit_name]
 
	local on, change_time = p.on_off(on_time, total_time, offset, unit_seconds)
 
	local ret_table = mw.html.create('table')
				:addClass('wikitable')
				:css({ ['text-align'] = 'center',
					float = 'right' })
				:tag('tr')
					:tag('td')
						:wikitext('Time until ' .. (on and 'end' or 'start') .. ': ' .. change_time .. ' ' .. p.plural(unit_name, change_time))
						:addClass(yesno[on])
					:done()
				:done()
 
	p.add_purge(ret_table)
	return ret_table
end
 
local function date_of(i_rot, interval, curr_rot, next_in, total_rots)
	local s = os.time() + seconds_in_day * (next_in + interval * ((i_rot - curr_rot - 1) % total_rots) )
	if i_rot==curr_rot then
		return 'Now!'
	else
		return lang:formatDate('j M', '@' .. s, nil)
	end
end
 
--[[
 
--]]
function p.simple_table(rotation_names, interval, offset, dated)
	local rotation, next = p.rotation_days(interval, #rotation_names, offset)
	local align = 'center'
 
	if dated then
		align = 'left'
		pad = '0.5em'
	end
 
	local ret_table = mw.html.create('table')
				:addClass('wikitable')
				:css({ ['text-align'] = align,
					margin = '3px',
					float = 'right' })
				:tag('caption')
					:wikitext('Current rotation')
				:done()
	local td
	for i, v in ipairs(rotation_names) do
		td = ret_table:tag('tr'):tag('td')
		td			:addClass(yesno[i==rotation])
					:wikitext(v)
					:done()
				:done()
			:done()
		if dated then
			td:css('padding-left', '0.5em')
				:tag('span')
				:css({ ['float'] = 'right',
					['text-align'] = 'right',
					['font-size'] = '80%',
					['margin-left'] = '5px' })
				:wikitext(date_of(i, interval, rotation, next, #rotation_names))
		end
	end
	td = ret_table:tag('tr'):tag('td')
	td			:css('text-align', 'center')
				:wikitext("'''Next: "..next..' '..p.plural('day', next).."'''")
			:done()
		:done()
	--if dated then
		p.add_purge2(ret_table, td)
	--else
	--	p.add_purge(ret_table)
	--end
	return ret_table
end
 
--[[
 
--]]
function p.simple_table_minutes(rotation_names, interval, offset)
	local rotation, next = p.rotation_minutes(interval, #rotation_names, offset)
	local align = 'center'
 
	local ret_table = mw.html.create('table')
				:addClass('wikitable')
				:css({ ['text-align'] = align,
					margin = '3px',
					float = 'right' })
				:tag('caption')
					:wikitext('Current rotation')
				:done()
	local td
	for i, v in ipairs(rotation_names) do
		td = ret_table:tag('tr'):tag('td')
		td			:addClass(yesno[i==rotation])
					:wikitext(v)
					:done()
				:done()
			:done()
	end
	td = ret_table:tag('tr'):tag('td')
	td			:css('text-align', 'center')
				:wikitext("'''Next: "..next..' '..p.plural('minute', next).."'''")
			:done()
		:done()
	p.add_purge2(ret_table, td)
	return ret_table
end
 
--[=======================================================[
--						ON/OFF
--]=======================================================]
 
--[==========[
--	Ravens
--]==========]
function p.raven()
	local on, change_time = p.on_off(1, 13, 7, seconds_in_day)
 
	local text
	if on then
		text = '<b>There is currently a raven spawned.</b>'
	else
		local last = 13 - change_time
 
		local date_format = "%e %B %Y"
		local last_date = os.date(date_format, os.time() - last * seconds_in_day)
		local next_date = os.date(date_format, os.time() + change_time * seconds_in_day)
 
		text = 'The last raven spawned <b>' .. last .. ' ' .. p.plural('day', last) .. ' ago on ' .. last_date .. '</b>. The next raven will spawn on <b>' .. next_date .. ' in ' .. change_time .. ' ' .. p.plural('day', change_time) .. '</b>. '
	end
 
	return text .. purge()
end
 
--[==========[
--	Castaways
--]==========]
function p.castaways(frame)
    local args = frame:getParent().args
 
    local bottle = args.bottle
    if (bottle == nil) then
        bottle = 1
    end
 
    local offset = 7
 
    local hidewarning = args.hidewarning
    if (hidewarning == nil) then
        hidewarning = 'no'
    end
 
    local starttext = args.starttext
    if (starttext == nil) then
        starttext = 'This message in a bottle'
    end
 
    if (bottle == "3") then
        offset = 6
    end
    if (bottle == "5") then
        offset = 5
    end
    if (bottle == "6") then
        offset = 3
    end
    if (bottle == "7") then
        offset = 8
    end
    if (bottle == "9") then
        offset = 9
    end
    if (bottle == "10") then
        offset = 5
    end
 
	local on, change_time = p.on_off(1, bottle, offset, seconds_in_day)
 
	local text
	if on then
		text = starttext..' is currently <span style="font-weight:bold;">visible</span>. '
	else
		local last = bottle - change_time
 
		local date_format = "%e %B"
		local last_date = os.date(date_format, os.time() - last * seconds_in_day)
		local next_date = os.date(date_format, os.time() + change_time * seconds_in_day)
 
		text = starttext..' was last visible <span style="font-weight:bold;">' .. last .. ' ' .. p.plural('day', last) .. ' ago</span> on ' .. last_date .. '. It will be visible again in <span style="font-weight:bold;">' .. change_time .. ' ' .. p.plural('day', change_time) .. '</span> on ' .. next_date .. '. '
	end
 
    text = text ..  purge()
 
    if (hidewarning ~= 'yes') then
        text = text .. ' Once a message in a bottle is found and when a player has walked near its spawn location when it was visible it will always be visible, unless the message in the bottle or its treasure map is in the player\'s bank or inventory.' 
    end
 
	return text
end
 
--[=============[
--	Sinkholes
--]=============]
function p.sinkholes()
	return p.simple_on_off(15, 60, 30, 'minute')
end
 
--[================[
--	Guthixian Cache
--]================]
function p.guthix_cache()
	local on, change_time = p.on_off(10, 60, 0, seconds_in_minute)	--For the Hourly event
	local on2, change_time2 = p.on_off(10, 3*60, 0, seconds_in_minute)	--For the Guthixian Boost
 
	--Copied from p.simple_on_off; modified
	local ret_table = mw.html.create('table')
				:addClass('wikitable')
				:css('float','right')
				:tag('tr')
				    :tag('th')
				        :wikitext('Time until ' .. (on and 'end' or 'start'))
				        :css('text-align','right')
				    :done()
					:tag('td')
						:wikitext(change_time .. ' ' .. p.plural('minute', change_time))
						:addClass(yesno[on])
					:done()
				:done()
				:tag('tr')
				    :tag('th')
				        :wikitext('Time until boosts ' .. (on2 and 'end' or 'start'))
				        :css('text-align','right')
				    :done()
					:tag('td')
						:wikitext(change_time2 .. ' ' .. p.plural('minute', change_time2))
						:addClass(yesno[on2])
					:done()
				:done()
 
	p.add_purge(ret_table,2)
	return ret_table
end
 
--[==================[
--	Big chinchompa
--]==================]
function p.big_chinchompa()
	return p.simple_on_off(20, 60, 30, 'minute')
end
 
--[================[
--	Supply run
--]================]
function p.supply_run()
	return p.simple_on_off(25, 12 * 60, 0, 'minute')
end
 
--[=======================================================[
--						  Cycles
--]=======================================================]
 
--[=======================[
--	Minigame spotlight
--]=======================]
 
--modified version of p.simple_table
local function spotlight_table(rotation_names, interval, offset)
	local rotation, next = p.rotation_days(interval, #rotation_names, offset)
	local align = 'left'
	local pad = '0.5em'
 
	local ret_table = mw.html.create('table')
				:addClass('wikitable')
				:css({ ['text-align'] = align,
					margin = '3px',
					float = 'right' })
				:tag('caption')
					:wikitext('Current rotation')
				:done()
	local td
	local starti = 0
	for i,v in ipairs(rotation_names) do
		if date_of(i, interval, rotation, next, #rotation_names) == 'Now!' then
			starti = i-1
			break
		end
	end
 
	local j = 0
	local i, v
	while j < #rotation_names do
		i = ((starti + j) % #rotation_names)+1
		v = '[['..rotation_names[i]..']]'
		td = ret_table:tag('tr'):tag('td')
		td			:addClass(yesno[i==rotation])
					:wikitext(v)
					:done()
				:done()
			:done()
		td:css('padding-left', '0.5em')
			:tag('span')
			:css({ ['float'] = 'right',
				['text-align'] = 'right',
				['font-size'] = '80%',
				['margin-left'] = '5px' })
			:wikitext(date_of(i, interval, rotation, next, #rotation_names))
		j = j + 1
	end
	td = ret_table:tag('tr'):tag('td')
	td		  :css('text-align', 'center')
				:wikitext("'''Next: "..next..' '..p.plural('day', next).."'''")
			:done()
		:done()
		p.add_purge2(ret_table, td)
	return ret_table
end
 
local function get_spotlight_list()
	return {
		'Pest Control',
		'Soul Wars',
		'Fist of Guthix',
		'Barbarian Assault',
		'Conquest',
		'Fishing Trawler',
		'The Great Orb Project',
		'Flash Powder Factory',
		'Castle Wars',
		'Stealing Creation',
		'Cabbage Facepunch Bonanza',
		'Heist',
		'Mobilising Armies',
		'Barbarian Assault',
		'Conquest',
		'Fist of Guthix',
		'Castle Wars',
		'Pest Control',
		'Soul Wars',
		'Fishing Trawler',
		'The Great Orb Project',
		'Flash Powder Factory',
		'Stealing Creation',
		'Cabbage Facepunch Bonanza',
		'Heist',
		'Trouble Brewing',
		'Castle Wars'
	}
end
 
function p.spotlight()
	return spotlight_table(get_spotlight_list(), 3, -49) -- -49 to force same order as news post
end
 
local function to_row(val)
	return '|-\n!Next spotlight\n|'..val..'&nbsp;'..purge() --use wikicode until infoboxes are modulised
end
 
--find the date of the next spotlighted minigame
--returns '' if minigame is not found in the list
--returns a row with the date of the next spotlight, or 'Now!'
function p.next_spotlight(frame)
	local interval, offset = 3, -49
	local rotations = get_spotlight_list()
	local name = frame:getParent().args[1]
	local rotation, next_in = p.rotation_days(interval, #rotations, offset)
	local pos = {}
	local found = false
 
	for i,v in ipairs(rotations) do
		if name == v then
			pos[i] = '0'
			found = true
		end
	end
	if not found then
		return ''
	end
 
	for i,v in pairs(pos) do
		if i == rotation then
			return to_row("'''Now!'''")
		end
		pos[i] = os.time() + seconds_in_day * (next_in + interval * ((tonumber(i) - rotation - 1) % #rotations) )
	end
 
	local next_rot = os.time() + seconds_in_day*365
	for i,v in pairs(pos) do
		if next_rot > v then
			next_rot = v
		end
	end
 
	return to_row(lang:formatDate('j F', '@' .. next_rot, nil))
end
 
--[============[
--	Vorago
--]============]
function p.vorago()
	local rotations = {
		'Ceiling collapse',
		'Scopulus',
		'Vitalis',
		'Green bomb',
		'TeamSplit',
		'The end',
	}
 
	return p.simple_table(rotations, 7, -6, false) -- -6 to force "the end" to be last
end
--[==========[
--	Rots
--]==========]
-- Array borrowed from http://www.pso-clan.com/rotations.js
function p.rots()
	local b = {
			A = 'Ahrim',
			D = 'Dharok',
			G = 'Guthan',
			K = 'Karil',
			T = 'Torag',
			V = 'Verac'
	}
	local rotations = {
		{{b.D,b.T,b.V},{b.K,b.A,b.G}},
		{{b.K,b.T,b.G},{b.A,b.D,b.V}},
		{{b.K,b.G,b.V},{b.A,b.T,b.D}},
		{{b.G,b.T,b.V},{b.K,b.A,b.D}},
		{{b.K,b.T,b.V},{b.A,b.G,b.D}},
		{{b.A,b.G,b.D},{b.K,b.T,b.V}},
		{{b.K,b.A,b.D},{b.G,b.T,b.V}},
		{{b.A,b.T,b.D},{b.K,b.G,b.V}},
		{{b.A,b.D,b.V},{b.K,b.T,b.G}},
		{{b.K,b.A,b.G},{b.T,b.D,b.V}},
		{{b.A,b.T,b.G},{b.K,b.D,b.V}},
		{{b.A,b.G,b.V},{b.K,b.T,b.D}},
		{{b.K,b.A,b.T},{b.G,b.D,b.V}},
		{{b.K,b.A,b.V},{b.D,b.T,b.G}},
		{{b.A,b.T,b.V},{b.K,b.D,b.G}},
		{{b.K,b.D,b.G},{b.A,b.T,b.V}},
		{{b.D,b.T,b.G},{b.K,b.A,b.V}},
		{{b.G,b.D,b.V},{b.K,b.A,b.T}},
		{{b.K,b.T,b.D},{b.A,b.G,b.V}},
		{{b.K,b.D,b.V},{b.A,b.T,b.G}}
	}
	local days_after_utc = math.floor(os.time() / seconds_in_day)
	local rotation = (days_after_utc % 20) + 1
	rotation = rotations[rotation]
	local today = os.date("%e %B %Y")
	local ret = mw.html.create('table')
				:addClass('wikitable')
				:css({ ['text-align'] = 'center',
					float = 'right' })
				:tag('caption')
					:wikitext(today)
				:done()
				:tag('tr')
					:tag('th')
						:wikitext('West')
					:done()
					:tag('th')
						:attr('rowspan','4')
					:done()
					:tag('th')
						:wikitext('East')
					:done()
				:done()
	local s1,s2 = unpack(rotation)
	for i=1,3 do
		ret:tag('tr')
			:tag('td')
				:wikitext(s1[i])
			:done()
			:tag('td')
				:wikitext(s2[i])
			:done()
 
	end
	p.add_purge(ret,3)
	return ret
end
 
--[==========[
--	TH D&D
--]==========]
function p.th_dnd()
	local rotations = {
		'\[[Evil Tree]]',
		'\[[Shooting Star]]',
		'\[[Penguin Hide and Seek]]',
		'\[[Circus]]'
	}
 
	return p.simple_table(rotations, 7, 1, false)
end
 
--[========================[
--	Circus city template
--]========================]
function p.circus(frame)
	local args = frame:getParent().args
	local disp = mw.text.trim( string.lower(args[1] or '') )
	if disp == 'image' then
		return p.circus_city_image()
	elseif disp == 'city' then
		return p.circus_city_name()
	else
		return p.circus_city_table()
	end
end
 
--[======================[
--	Circus city (both)
--	Simple return vals
--]======================]
function p.circus_city()
	local rotations = {
		{ name = '[[Tree Gnome Stronghold]] - south of the entrance; near the gate',
			image = 'Circus (Tree Gnome Stronghold) location.png',
			short = 'Tree Gnome'
		},
		{ name = '[[Seers\' Village]]',
			image = 'Circus (Seers\' Village) location.png',
			short = 'Seers\' Village'
		},
		{ name = '[[Catherby]]',
			image = 'Circus (Catherby) location.png',
			short = 'Catherby'
		},
		{ name = '[[Taverley]]',
			image = 'Circus (Taverley) location.png',
			short = 'Taverley'
		},
		{ name = '[[Edgeville]]',
			image = 'Circus (Edgeville) location.png',
			short = 'Edgeville'
		},
		{ name = '[[Falador]]',
			image = 'Circus (Falador) location.png',
			short = 'Falador'
		},
		{ name = '[[Rimmington]]',
			image = 'Circus (Rimmington) location.png',
			short = 'Rimmington'
		},
		{ name = '[[Draynor Village]]',
			image = 'Circus (Draynor Village) location.png',
			short = 'Draynor'
		},
		{ name = '[[Al Kharid]]',
			image = 'Circus (Al Kharid) location.png',
			short = 'Al Kharid'
		},
		{ name = '[[Lumbridge]]',
			image = 'Circus (Lumbridge) location.png',
			short = 'Lumbridge'
		},
		{ name = '[[Varrock]] - south-east of Lumber Yard',
			image = 'Circus (Lumber Yard) location.png',
			short = 'Lumber Yard'
		},
		{ name = '[[Varrock]] - north of [[Gertrude]]\'s house',
			image = 'Circus (Cooks\' Guild) location.png',
			short = 'Gertrude\'s'
		}
	}
	local rotation,next = p.rotation_days(7,#rotations,1)
	return rotations,rotation,next
end
 
--[======================[
--	Circus city (both)
--	Pretty table
--]======================]
function p.circus_city_table()
	local rotations,rot,next = p.circus_city()
	local img = rotations[rot].image
	local loc = rotations[rot].name
	local ret = mw.html.create('table')
			:addClass('wikitable')
			:css('text-align','center')
			:tag('tr')
				:tag('th')
					:wikitext('Current location:')
				:done()
				:tag('td')
					:attr('rowspan','13')
					:wikitext('[[File:'..img..'|250px]]')
				:done()
			:done()
	for i, v in ipairs(rotations) do
		ret:tag('tr')
			:tag('td')
				:addClass(yesno[i==rot])
				:wikitext(v.short)
			:done()
		:done()
	end
	ret	:tag('tr')
			:tag('td')
				:attr('colspan','2')
				:wikitext(loc)
			:done()
		:done()
	local td = ret:tag('tr'):tag('td')
	td			:attr('colspan','2')
					:wikitext("'''Days until next: "..next.."'''")
				:done()
			:done()
		:done()
	p.add_purge2(ret, td)
	return ret
end
 
--[====================[
--	Circus city name
--]====================]
function p.circus_city_name()
	local rotations,rot = p.circus_city()
	return rotations[rot].name
end
 
--[=====================[
--	Circus city image
--]=====================]
function p.circus_city_image()
	local rotations,rot = p.circus_city()
	return rotations[rot].image
end
 
--[===========[
--	Araxxor
--]===========]
function p.araxxor()
	local rax_rotations = {
		'Minions',
		'Acid',
		'Darkness'
	}
 
	local rotation, days_to_next = p.rotation_days(4, #rax_rotations, 3)
 
	function is_open(path)
		if path == rotation then
			return false
		else
			return true
		end
	end
 
	function add_body(path, tb)
		local open = is_open(path)
		local td = tb:tag('td')
		if open then
			td:wikitext('Open'):addClass(_on_)
		else
			td:wikitext('Closed'):addClass(_off_)
		end
	end
 
	local t = mw.html.create('table'):addClass('wikitable'):css('text-align','center')
	local th = t:tag('tr')
	local tb = t:tag('tr')
	for i=1,3 do
		th:tag('th'):wikitext('Path ' .. i .. ' (' .. rax_rotations[i] .. ')')
		add_body(i, tb)
	end
 
	local td = t:tag('th'):attr('colspan', '3'):wikitext('Days until next rotation: ' .. days_to_next)
 
	local cocoon
	if is_open(1) and is_open(2) then
		cocoon = 'I died covered in acid and spiders.'
	elseif is_open(1) and is_open(3) then
		cocoon = 'I died in the dark, covered in spiders.'
	elseif is_open(2) and is_open(3) then
		cocoon = 'I died in a dark acid pool.'
	end
	t:tag('tr'):tag('td'):attr('colspan','3'):css('font-style','italic'):wikitext(cocoon)
	p.add_purge2(t,td)
	return t
end
 
--[============[
--	Icxan
--]============]
function p.Icxan()
	local rotations = {
	    'Grotworm Lair (Port Sarim)',
		'Wizards\' Tower',
		'Nardah',
		'Slayer Tower (Canifis)',
		'Sawmill (Varrock)',
		'Draynor Manor',
		'White Wolf Mountain',
		'Skavid caves',
		'Oo\'glog',
		'Falador',
	}
 
	return p.simple_table_minutes(rotations, 60, 180) -- -120 in order to fix the time offst.
end
 
return p

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.