Module:Lab Rotation
From IdleOn MMO Wiki
Documentation for this module may be created at Module:Lab Rotation/doc
require('strict')
local Random = require('Module:Random')
local Data = require('Module:Lab Rotation/Data')
local p = {}
--- The number of seconds in a week.
local SECONDS_PER_WEEK = 604800
--- The format to display dates in.
local DATE_FORMAT = '%x'
local CHIP_PREFIX = 'Lab - '
local JEWEL_PREFIX = 'Console Jewel '
--- Gets the current week
--- @return integer
local function getCurrentWeek()
return math.floor(os.time() / SECONDS_PER_WEEK)
end
--- Checks whether the table contains the item.
--- @generic T
--- @param tbl T[] The table
--- @param item T The item to search for
--- @return boolean
local function contains(tbl, item)
item = item:lower()
for _, e in ipairs(tbl) do
if item == e:lower() then
return true
end
end
return false
end
--- Checks whether the item is a chip.
--- @param item string The item to check
--- @return boolean
local function isChip(item)
return contains(Data.chips, item)
end
--- Checks whether the item is a jewel.
--- @param item string The item to check
--- @return boolean
local function isJewel(item)
return contains(Data.jewels, item)
end
--- Formats a week into its date string.
--- @param week integer
--- @return string|osdate
local function formatDate(week)
return os.date(DATE_FORMAT, week * SECONDS_PER_WEEK)
end
--- Generates a random number.
--- @param seed integer The seed
--- @param upper_bound integer The upper bound
--- @return integer
local function generateRandomNumber(seed, upper_bound)
local random = math.floor(Random:new(seed):rand() * 1000)
return upper_bound and (random % upper_bound) or random
end
--- Gets the item that appears in the specified slot during the specified week.
--- @param week integer The week
--- @param slot integer The item slot
function p._getItem(week, slot)
local choices = (slot == 3) and Data.jewels or Data.chips
local numChoices = (slot == 1) and #choices - 10 or #choices
local seed = week + ((slot - 1) * 500)
local item = generateRandomNumber(seed, numChoices)
local prevItem = generateRandomNumber(seed - 1, numChoices)
if prevItem == item then
local nextItem = generateRandomNumber(seed + 1, numChoices)
while item == prevItem or item == nextItem do
seed = seed + 765
item = generateRandomNumber(seed, numChoices)
end
end
-- Lua indices start at 1
item = item + 1
local ret = {}
-- Generate alternative items for Jade Emporium-exclusive jewels.
--
-- Note: The game currently has a bug which causes chips to be rerolled
-- as well as jewels if the bonus isn't unlocked. Once the bug is fixed,
-- remove the commented from the line below and delete this note.
if item >= 19 and item <= 21 --[[and slot == 2]] then
table.insert(ret, 1, choices[item - 10])
end
table.insert(ret, choices[item])
return ret
end
--- Template entry point for querying what items are currently in the shop.
--- @param frame table
--- @return string
function p.what(frame)
return p._what(frame.args)
end
--- Module entry point for querying what items are currently in the shop.
--- @param args { slot: integer, week?: integer, offset?: integer }
--- @return string
function p._what(args)
local offset = tonumber(args.offset) or 0
local week = tonumber(args.week) or getCurrentWeek()
local slot = tonumber(args.slot) or 1
local prefix = (slot == 3) and JEWEL_PREFIX or CHIP_PREFIX
assert(slot ~= nil, '"slot" is undefined')
local ret = mw.html.create('div')
local items = p._getItem(week + offset, slot)
for _, item in ipairs(items) do
local content = '[[File:' .. prefix .. item .. '.png|32px]] ' .. item
ret:tag('span'):addClass('lab-rotation-item'):wikitext(content)
end
return tostring(ret)
end
--- Template entry point for querying when an item will be available next.
--- @param frame table
--- @return string|osdate
function p.when(frame)
return p._when(frame.args)
end
--- Module entry point for querying when an item will be available next.
--- @param args { [1]: string }
--- @return string|osdate
function p._when(args)
local item = args[1]:lower()
local slots = isChip(item) and { 1, 2 } or isJewel(item) and { 3 } or nil
assert(slots ~= nil, 'Invalid item name: ' .. item)
local week = getCurrentWeek()
while true do
for _, slot in ipairs(slots) do
local items = p._getItem(week, slot)
if contains(items, item) then
return formatDate(week)
end
end
week = week + 1
end
end
--- Module entry point for querying when an item will be available next.
--- @param frame table The template frame
--- @return string|osdate
function p.date(frame)
return p._date(frame.args)
end
--- Template entry point for querying when an item will be available next.
--- @param args { week?: integer, offset?: integer }
--- @return string|osdate
function p._date(args)
local week = tonumber(args.week) or getCurrentWeek()
local offset = tonumber(args.offset) or 0
return formatDate(week + offset)
end
return p