Module:LoRUtility
Jump to navigation
Jump to search
Documentation for this module may be created at Module:LoRUtility/doc
-- <pre>
local lib = require('Module:Feature')
local MAX_KNOWN_VERSION = 5
local p = {}
local VarInt = {}
local BitOperator = {}
local Utility = {}
local REGIONS = {
[1] = {code = "DE", name = "Demacia"},
[2] = {code = "FR", name = "Freljord"},
[3] = {code = "IO", name = "Ionia"},
[4] = {code = "NX", name = "Noxus"},
[5] = {code = "PZ", name = "Piltover and Zaun"},
[6] = {code = "SI", name = "Shadow Isles"},
[7] = {code = "BW", name = "Bilgewater"},
[8] = {code = "SH", name = "Shurima"},
[10] = {code = "MT", name = "Targon"},
[11] = {code = "BC", name = "Bandle City"},
[13] = {code = "RU", name = "Runeterra"}
}
--To test debug this line mw.executeFunction(p.test)
function p.test(frame)
output = p.adventureRegions{"RUIODE--"}
mw.log(output)
end
function base32_decode(str)
local base32Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
local binary =
str:gsub(
".",
function(char)
if char == "=" then
return ""
end
local pos = string.find(base32Alphabet, char)
pos = pos - 1
return string.format("%05u", Utility.dec2bin(pos))
end
)
local bytes = Utility.str_split(binary, 8)
local result = {}
for _, byte in pairs(bytes) do
table.insert(result, tonumber(byte, 2))
end
return result
end
function p.deckDataFromCode(frame)
local args = lib.frameArguments(frame)
local lorData = mw.loadData("Module:LoRData/data")
local bytes = base32_decode(args['code'])
local firstByte = bytes[1]
local deckList = {}
table.remove(bytes, 1)
local version = BitOperator.AND(tonumber(firstByte), 0xF)
if version > MAX_KNOWN_VERSION then
return "The provided code requires a higher version of this library; please update."
end
local totalShards = 0
local regions = {}
local followerCount = 0
local spellCount = 0
local landmarkCount = 0
local equipmentCount = 0
local rarities = {}
local champions = {}
local shardValues = {
["None"] = 0,
["Common"] = 100,
["Rare"] = 300,
["Epic"] = 1200,
["Champion"] = 3000,
}
local topChampion = nil
local topFollower = nil
local runeterranChampions = {}
local allCards = {}
function handleCardData(cardData, count, cardCode, regionName)
local cardType = cardData.type
if cardType == "Unit" then
if cardData.supertype ~= nil and cardData.supertype == "Champion" then
cardType = "Champion"
if regionName == "Runeterra" then
table.insert(runeterranChampions,cardData.name)
end
else
cardType = "Follower"
end
end
table.insert(allCards,{code=cardCode, count=count, regionName=regionName})
regions[regionName] = (regions[regionName] or 0) + count
if args['detail'] then
totalShards = totalShards + shardValues[cardData.rarity] * count
if cardData.rarity ~= 'None' then
if cardData.rarity == 'Common' then
rarities[1] = (rarities[1] or 0) + count
elseif cardData.rarity == 'Rare' then
rarities[2] = (rarities[2] or 0) + count
elseif cardData.rarity == 'Epic' then
rarities[3] = (rarities[3] or 0) + count
else
rarities[4] = (rarities[4] or 0) + count
end
end
if cardType == "Champion" or cardType =='Follower' then
if cardType == "Champion" then
table.insert(champions, {
code = cardCode,
count = count
})
if topChampion == nil or cardData.cost > topChampion.cost then
topChampion = {
cost = cardData.cost,
code = cardCode
}
end
else
if topFollower == nil or cardData.cost > topFollower.cost then
topFollower = {
cost = cardData.cost,
code = cardCode
}
end
followerCount = followerCount + count
end
elseif cardType == "Spell" then
spellCount = spellCount + count
elseif cardType == "Landmark" then
landmarkCount = landmarkCount + count
elseif cardType == "Equipment" then
equipmentCount = equipmentCount + count
end
else
table.insert(
deckList,
{
code = cardCode,
name = cardData.name,
count = count,
region = regionName,
type = cardType
}
)
end
end
for i = 3, 1, -1 do
local numGroupOfs = tonumber(VarInt.Pop(bytes)) or 0
for j = 1, numGroupOfs, 1 do
local numOfsInThisGroup = tonumber(VarInt.Pop(bytes)) or 0
local set = VarInt.Pop(bytes)
local region = VarInt.Pop(bytes)
for k = 1, numOfsInThisGroup, 1 do
local card = VarInt.Pop(bytes)
if card ~= nil then
local setString = Utility.padLeft(tostring(set), 2, "0")
local regionData = REGIONS[region + 1]
local cardString = Utility.padLeft(tostring(card), 3, "0")
local cardCode = setString .. regionData.code .. cardString
if (not lorData[cardCode]) then
return 'Card Code "' .. cardCode .. '" does not exist'
end
handleCardData(lorData[cardCode], i, cardCode, regionData.name)
end
end
end
end
--For cards with over 3 copies
while true do
local count = tonumber(VarInt.Pop(bytes)) or nil
local set = VarInt.Pop(bytes)
local region = VarInt.Pop(bytes)
local card = VarInt.Pop(bytes)
if not count or not set or not region or not card then
break --end of stream
end
local setString = Utility.padLeft(tostring(set), 2, "0")
local regionData = REGIONS[region + 1]
local cardString = Utility.padLeft(tostring(card), 3, "0")
local cardCode = setString .. regionData.code .. cardString
if (not lorData[cardCode]) then
return 'Card Code "' .. cardCode .. '" does not exist'
end
handleCardData(lorData[cardCode], count, cardCode, regionData.name)
end
while #bytes > 0 do
local fourPlusCount = VarInt.Pop(bytes)
local fourPlusSet = VarInt.Pop(bytes)
local fourPlusFaction = VarInt.Pop(bytes)
local fourPlusNumber = VarInt.Pop(bytes)
if fourPlusFaction == nil then
return 'Invalid Deck Code'
end
local fourPlusSetString = Utility.padLeft(tostring(fourPlusSet), 2, "0")
local fourPlusregionData = REGIONS[fourPlusFaction + 1]
local fourPlusNumberString = Utility.padLeft(tostring(fourPlusNumber), 3, "0")
local cardCode = fourPlusSetString .. fourPlusregionData.code .. fourPlusNumberString
if (not lorData[cardCode]) then
return 'Card Code "' .. cardCode .. '" does not exist'
end
handleCardData(lorData[cardCode], fourPlusCount, cardCode, regionData.name)
end
local multiregion = {}
if args['detail'] then
for _, champion in ipairs(runeterranChampions) do
for _, data in ipairs(allCards) do
if p.isFromRuneterran{data.code,champion} then
regions["Runeterra"] = (regions["Runeterra"] or 0) + data.count
regions[data.regionName] = (regions[data.regionName] or 0) - data.count
data.regionName = "Runeterra"
end
end
end
for _, data in ipairs(allCards) do
if data.regionName ~= "Runeterra" and lorData[data.code].categoryRefs and lorData[data.code].regions then
for _,ref in ipairs(lorData[data.code].categoryRefs) do
if ref == "Multi-region" then
regions[data.regionName] = (regions[data.regionName] or 0) - data.count
local region1 = data.regionName
local region2 = nil
for _,region in ipairs(lorData[data.code].regions) do
if region1 ~= region then
region2 = region
break
end
end
table.insert(multiregion,{
count=data.count,
region1=region1,
region2=region2
})
break
end
end
end
end
for _,data in pairs(multiregion) do
if not data.region2 or (regions[data.region1] and regions[data.region1] > 0) then
regions[data.region1] = (regions[data.region1] or 0) + data.count
elseif regions[data.region2] and regions[data.region2] > 0 then
regions[data.region2] = (regions[data.region2] or 0) + data.count
else
regions[data.region1] = (regions[data.region1] or 0) + data.count
end
end
for region, count in pairs(regions) do
if count <= 0 then
regions[region] = nil
end
end
return {
shards = totalShards,
topUnitCode = (topChampion or topFollower).code,
regions = regions,
champions = champions,
followerCount = followerCount,
spellCount = spellCount,
landmarkCount = landmarkCount,
equipmentCount = equipmentCount,
rarities = rarities
}
else
for _, champion in ipairs(runeterranChampions) do
for _, data in ipairs(deckList) do
if p.isFromRuneterran{data.code,champion} then
data.region = "Runeterra"
end
end
end
for _, data in ipairs(allCards) do
if lorData[data.code].categoryRefs and lorData[data.code].regions then
for _,ref in ipairs(lorData[data.code].categoryRefs) do
if ref == "Multi-region" then
regions[data.regionName] = (regions[data.regionName] or 0) - data.count
break
end
end
end
end
for _, data in ipairs(deckList) do
if data.region ~= "Runeterra" and lorData[data.code].categoryRefs and lorData[data.code].regions then
for _,ref in ipairs(lorData[data.code].categoryRefs) do
if ref == "Multi-region" then
local region1 = data.region
local region2 = nil
for _,region in ipairs(lorData[data.code].regions) do
if region1 ~= region then
region2 = region
break
end
end
if not region2 or (regions[region1] and regions[region1] > 0) then
data.region = region1
elseif regions[region2] and regions[region2] > 0 then
data.region = region2
else
data.region = region1
end
break
end
end
end
end
return deckList
end
end
function p.isFromRuneterran(frame)
local args = lib.frameArguments(frame)
local lorData = require("Module:LoRData/data")
local cardCode = args['code'] or args[1] or nil
local region = args['region'] or args['champion'] or args[2] or nil
if not cardCode or not region or not lorData[cardCode] then
return false
end
local card = lorData[cardCode]
if card.supertype == "Champion" then
return false
end
if region == "Aatrox" and card.subtype then
for _, subtype in ipairs(card.subtype) do
if subtype == "Darkin" then
return true
end
end
elseif region == "Bard" then
local CHIME_CARDS = { ["06BC011"] = true, ["06BC026"] = true, ["06BC031"] = true,
["06BC032"] = true, ["06BC044"] = true, ["06MT029"] = true, ["06MT047"] = true,
["08BC005"] = true, ["08SI021"] = true
}
if CHIME_CARDS[cardCode] then return true end
elseif region == "Elder Dragon" then
if card.cost >= 6 then return true end
elseif region == "Evelynn" and card.categoryRefs then
for _, ref in ipairs(card.categoryRefs) do
if ref == "Husk-generating" then
return true
end
end
elseif region == "Jax" and card.subtype then
for _, subtype in ipairs(card.subtype) do
if subtype == "Weaponmaster" then
return true
end
end
elseif region == "Jhin" and card.keywordRefs then
for _, ref in ipairs(card.keywordRefs) do
if ref == "Skill-generating" then
return true
end
end
elseif (region == "Kayn" or region == "Varus") and card.subtype then
for _, subtype in ipairs(card.subtype) do
if subtype == "Cultist" then
return true
end
end
elseif region == "Neeko" and card.subtype then
local SUBTYPES = { ["Bird"] = true, ["Cat"] = true, ["Dog"] = true,
["Elnuk"] = true, ["Fae"] = true, ["Reptile"] = true, ["Spider"] = true
}
for _, subtype in ipairs(card.subtype) do
if SUBTYPES[subtype] then
return true
end
end
elseif region == "Ryze" and card.type == "Spell" and card.keywords then
for _, keyword in ipairs(card.keywords) do
if keyword == "Burst" or keyword == "Focus" then
return true
end
end
elseif region == "The Poro King" then
local PORO_CARDS = { ["01FR016"] = true, ["01FR025"] = true, ["02FR003"] = true,
["03FR018"] = true, ["03PZ018"] = true, ["06BC043"] = true, ["09FR009"] = true,
["99FR001"] = true, ["99FR002"] = true, ["99PZ118"] = true
}
if PORO_CARDS[cardCode] then return true end
if card.subtype then
for _, subtype in ipairs(card.subtype) do
if subtype == "Poro" then
return true
end
end
end
end
return false
end
function p.adventureRegions(frame)
local args = lib.frameArguments(frame)
local str = args[1] or nil
local size = args["size"] or args[2] or "20px"
local sep = args["sep"] or " "
if not str then return nil end -- Exit if no input
local result = {}
local REGION_CODES = {
DE = "Demacia",
FR = "Freljord",
IO = "Ionia",
NX = "Noxus",
PZ = "Piltover and Zaun",
SI = "Shadow Isles",
BW = "Bilgewater",
SH = "Shurima",
MT = "Targon",
BC = "Bandle City",
RU = "Runeterra"
}
for i = 1, #str, 2 do
local pair = str:sub(i, i + 1):upper()
mw.log(pair)
local region = REGION_CODES[pair] or "Neutral"
table.insert(result, "[[File: " .. region .. " LoR Region.png|" .. size .. "|link=]]")
end
return table.concat(result, sep)
end
function p.adventureRewards(frame)
local args = lib.frameArguments(frame)
local step = args["step"] or "5"
step = tonumber(step)
if not step then
return "Step Error."
end
local base_start = "<center><table style=\"border-collapse: collapse;\"><tr>"
local base_end = "</tr></table></center>"
local r = ""
local count = 0
for i, a in ipairs(args) do
count = i
end
local rows = math.ceil(count/step)
local index_lastrow = ((rows-1)*step)+1
for i, a in ipairs(args) do
if i % step == 1 then
if i ~= 1 then
r = r .. base_end
end
r = r .. base_start
if i < index_lastrow then
r = r .. "<td style=\"text-align: center; padding: 5px; border-right: 1px solid #3e4452; border-left: 1px solid #3e4452; border-bottom: 1px solid #3e4452;\"> "
else
r = r .. "<td style=\"text-align: center; padding: 5px; border-right: 1px solid #3e4452; border-left: 1px solid #3e4452;\"> "
end
else
if i < index_lastrow then
r = r .. "<td style=\"text-align: center; padding: 5px; border-right: 1px solid #3e4452; border-bottom: 1px solid #3e4452;\"> "
else
r = r .. "<td style=\"text-align: center; padding: 5px; border-right: 1px solid #3e4452;\"> "
end
end
r = r .. args[i] .. "</td>"
end
r = r .. base_end
return frame:preprocess(r)
end
function p.headerDeck(frame)
local args = lib.frameArguments(frame)
local builder = require("Module:SimpleHTMLBuilder")
args['name'] = args['name'] or 'Untitled Deck'
if args['code'] == nil then
return 'Empty Code'
end
local result = {}
local deckDetail = p.deckDataFromCode{code = args['code'], detail = true}
if type(deckDetail) ~= 'table' then
return deckDetail
end
local totalShards = deckDetail['shards'] or nil
local topUnitCode = deckDetail['topUnitCode'] or nil
local regions = deckDetail['regions'] or nil
local champions = deckDetail['champions'] or nil
local followerCount = deckDetail['followerCount'] or nil
local spellCount = deckDetail['spellCount'] or nil
local landmarkCount = deckDetail['landmarkCount'] or nil
local equipmentCount = deckDetail['equipmentCount'] or nil
local rarities = deckDetail['rarities'] or nil
local template = builder.create('div')
:css('position', 'relative')
:css('font-family', 'BeaufortLoL')
:tag('div')
:css('opacity', '0.5')
:wikitext('[[File:' .. topUnitCode .. '-full.png|700px|link=]]')
:done()
local deckHeader = builder.create('div')
:addClass('deck-header')
:css('position', 'absolute')
:css('top', '10px')
:css('left', '10px')
:tag('span')
:css('font-weight', 'bold')
:css('color', '#FFE3B0')
:css('font-size', '30px')
:css('margin-left', '10px')
:css('margin-right', '10px')
:wikitext(args['name'])
:done()
for regionName, regionCount in pairs(regions) do
deckHeader
:tag('span')
:addClass('inline-image')
:wikitext('[[File:' .. regionName .. ' LoR Region.png|60px|link=LoR:' .. regionName .. ']]')
:done()
end
deckHeader
:tag('span')
:css('display', 'inline-block')
:css('position', 'relative')
:css('color', '#FFE3B0')
:css('font-size', '16px')
:wikitext('[[File:Shard icon.png|40px|link=LoR:Shard]]' .. Utility.comma_value(totalShards))
:done()
deckHeader:done()
local deckInfo = builder.create('div')
:addClass('deck-info')
:css('position', 'absolute')
:css('top', '65px')
:css('left', '20px')
:css('font-weight', 'bold')
:css('color', '#FFE3B0')
if args['description'] then
deckInfo
:tag('span')
:css('display', 'inline-block')
:css('max-width', '660px')
:css('font-size', '16px')
:wikitext(args['description'])
:done()
end
deckInfo:done()
template
:node(deckHeader)
:node(deckInfo)
:tag('div')
:addClass('deck-info')
:css('position', 'absolute')
:css('top', '240px')
:css('left', '15px')
:css('font-size', '20px')
:css('color', '#FFE3B0')
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Follower card.png|25px|link=LoR:Follower]]' .. followerCount .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Spell card.png|25px|link=LoR:Spell]]' .. spellCount .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Keyword Landmark.png|25px|link=LoR:Landmark]]' .. landmarkCount .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Keyword Equipment.png|25px|link=LoR:Equipment]]' .. equipmentCount .. ' ')
:done()
:tag('br')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:LoR Common icon.png|25px|link=LoR:Card_types#By_rarity]]' .. (rarities[1] or 0) .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:LoR Rare icon.png|25px|link=LoR:Card_types#By_rarity]]' .. (rarities[2] or 0) .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:LoR Epic icon.png|25px|link=LoR:Card_types#By_rarity]]' .. (rarities[3] or 0) .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:LoR Champion icon.png|25px|link=LoR:Card_types#By_rarity]]' .. (rarities[4] or 0) .. ' ')
:done()
:done()
if #champions then
local deckChampions = builder.create('div')
:addClass('deck-champions')
:css('position', 'absolute')
:css('bottom', '20px')
:css('left', '200px')
:css('font-size', '20px')
:css('color', '#FFE3B0')
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Champion card.png|25px|link=LoR:Champion]]')
:done()
for _, deckData in pairs(champions) do
deckChampions
:tag('span')
:addClass('inline-image')
:wikitext('[[File:' .. deckData.code .. '.png|60px|link=LoR:'.. deckData.code .. ']]')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('x' .. deckData.count .. ' ')
:done()
end
deckChampions:
done()
template
:node(deckChampions)
end
template:allDone()
return tostring(template)
end
function p.deckDecoder(frame)
local args = lib.frameArguments(frame)
if args['code'] == nil then
return 'Empty Code'
end
local groupBy = args['groupBy'] or 'none'
local groupByValues = {'region', 'type', 'none'}
if Utility.table_contains(groupByValues, groupBy) == false then
return 'Only support Group By "region", "type" or none'
end
local header = args['header'] or true
if header == 'false' then
header = false
end
local result = {}
local deckList = p.deckDataFromCode{code = args['code']}
if type(deckList) ~= 'table' then
return deckList
end
for _, deckData in pairs(deckList) do
local groupByKey = ''
if groupBy == 'region' then
groupByKey = deckData.region
elseif groupBy == 'type' then
groupByKey = deckData.type .. 's'
else
groupByKey = 'List'
end
if result[groupByKey] == nil then
result[groupByKey] = {}
end
table.insert(
result[groupByKey],
{
code = deckData.code,
count = deckData.count,
name = deckData.name,
type = deckData.type
}
)
end
local sortedKeys = {}
for k in pairs(result) do
table.insert(sortedKeys, k)
end
table.sort(sortedKeys)
local template = ""
if header then
template = template .. p.headerDeck{code = args['code'], name = args['name'], description = args['description']}
end
local newline = "\n"
template = template .. "<tabber>" .. newline
template = template .. "Hide=" .. newline .. '|-|' .. newline
for i, groupByKey in ipairs(sortedKeys) do
if i ~= 1 then
template = template .. "|-|" .. newline
end
template = template .. groupByKey .. "=" .. newline .. "'''Code:''' " .. args['code'] .. newline
local regionData = result[groupByKey]
table.sort(
regionData,
function(a, b)
if (a.type == b.type) then
return a.name < b.name
end
return a.type < b.type
end
)
local type = ""
for _, cardData in ipairs(regionData) do
if cardData.type ~= type then
if (type ~= "") then
template = template .. "}}" .. newline
end
if groupBy == 'region' or groupBy == 'none' then
template = template .. ";" .. cardData.type .. "s" .. newline
end
template = template .. "{{column|3|" .. newline
type = cardData.type
end
local cardCodeTemplate = "* {{LoR|" .. cardData.name
if cardData.type ~= "Champion" then
cardCodeTemplate = cardCodeTemplate .. "|code=" .. cardData.code
end
cardCodeTemplate = cardCodeTemplate .. "}}"
if cardData.count > 1 then
cardCodeTemplate = cardCodeTemplate .. " (x" .. cardData.count .. ")"
end
template = template .. cardCodeTemplate .. newline
end
template = template .. "}}" .. newline
end
template = template .. "</tabber>"
return frame:preprocess(template)
end
function p.pocHeaderDeck(frame)
local args = lib.frameArguments(frame)
local builder = require("Module:SimpleHTMLBuilder")
local adventureData = require("Module:LoRPoCData/adventure")
local lorData = require("Module:LoRData/data")
args['name'] = args['name'] or args['foe'] or 'Untitled Deck'
local cmp = args['campaign'] or "World Adventures"
if args['adventure'] == nil or not adventureData[cmp][args['adventure']] then
return 'Adventure missing or invalid'
end
local adventure = adventureData[cmp][args['adventure']]
if args['foe'] == nil or not adventure[args['foe']] then
return 'Foe missing or invalid'
end
local foe = adventure[args['foe']]
local stars = adventure.stars or false
local adventureType = adventure["type"] or false
args['code'] = args['code'] or foe.deck or nil
if args['code'] == nil then
return 'Empty Code'
end
local hasPower = foe.power or false
local hasPowerArgs = args['powername'] or args['powerdesc'] or false
local powername = nil
local powerdesc = nil
local powericon = nil
local powerrarity = nil
if hasPower then
powername = foe.power.name or 'Power Name'
powerdesc = foe.power.desc or 'Power Description.'
powericon = foe.power.icon or nil
powerrarity = foe.power.rarity or 'Common'
end
if hasPowerArgs then
powername = args['powername'] or powername or 'Power Name'
powerdesc = args['powerdesc'] or powerdesc or 'Power Description.'
powericon = args['powericon'] or powericon or nil
powerrarity = args['powerrarity'] or powerrarity or 'Common'
hasPower = true
end
local deckDetail = p.deckDataFromCode{code = args['code'], detail = true}
if type(deckDetail) ~= 'table' then
return deckDetail
end
local deckSimple = p.deckDataFromCode{code = args['code'], detail = false}
if type(deckSimple) ~= 'table' then
return deckSimple
end
local topUnitCode = args['topUnitCode'] or foe.code or deckDetail['topUnitCode'] or nil
local regions = deckDetail['regions'] or nil
local champions = deckDetail['champions'] or nil
local followerCount = deckDetail['followerCount'] or nil
local spellCount = deckDetail['spellCount'] or nil
local landmarkCount = deckDetail['landmarkCount'] or nil
local equipmentCount = deckDetail['equipmentCount'] or nil
local MAX_MOBILE_ITEMS = 3
local MAX_ITEMS = 7
--Extract items on cards
local items = foe.items or nil
--Fuse champions with items
local championsItem = {}
if items then
for code, data in pairs(items) do
if lorData[code] and lorData[code]["supertype"] == "Champion" then
table.insert(championsItem,{
code=code,
count=0,
items=data
})
end
end
end
for _, deckData in pairs(champions) do
local found = false
for _, data in pairs(championsItem) do
if data.code == deckData.code then
found = true
data.count=deckData.count
break
end
end
if not found then
table.insert(championsItem, {
code=deckData.code,
count=deckData.count,
items=nil
})
end
end
table.sort(championsItem, function(a, b)
if (lorData[a.code].name == args['foe']) ~= (lorData[b.code].name == args['foe']) then -- 1. Priority if "name" is the foe
return lorData[a.code].name == args['foe']
end
local aitems = {}
local bitems = {}
if a.items then aitems = a.items end
if b.items then bitems = b.items end
if #aitems ~= #bitems then -- 2. Sort by the number of items (higher count first)
return #aitems > #bitems
end
if lorData[a.code].cost ~= lorData[b.code].cost then -- 3. Sort by "cost" (lowest to highest)
return lorData[a.code].cost < lorData[b.code].cost
end
return lorData[a.code].name < lorData[b.code].name -- 4. Sort alphabetically by "name"
end)
--detailed cards with items list
local detailedItems = {}
if items then
for code, data in pairs(items) do
table.insert(detailedItems, {
code=code,
name=lorData[code].name,
cost=lorData[code].cost,
items=data
})
end
end
table.sort(detailedItems, function(a, b)
if (a.name == args['foe']) ~= (b.name == args['foe']) then -- 1. Priority if "name" is the foe
return a.name == args['foe']
end
if #a.items ~= #b.items then -- 2. Sort by the number of items (higher count first)
return #a.items > #b.items
end
if a.cost ~= b.cost then -- 3. Sort by "cost" (lowest to highest)
return a.cost < b.cost
end
return a.name < b.name -- 4. Sort alphabetically by "name"
end)
local template = builder.create('div')
:css('display', 'inline-block')
:css('position', 'relative')
:css('font-family', 'BeaufortLoL')
:tag('div')
:css('opacity', '0.5')
:wikitext('[[File:' .. topUnitCode .. '-full.png|800px|link=]]')
:done()
local deckHeader = builder.create('div')
:addClass('deck-header')
:css('position', 'absolute')
:css('max-width', '650px')
:css('top', '10px')
:css('left', '10px')
:tag('span')
:css('font-weight', 'bold')
:css('color', '#FFE3B0')
:css('font-size', '30px')
:css('margin-left', '10px')
:css('margin-right', '10px')
:wikitext(args['name'])
:done()
local regionSize = 60
local count = 0
for _ in pairs(regions) do
count = count + 1
if count > 9 then
regionSize = 50
break
end
end
for regionName, regionCount in pairs(regions) do
deckHeader
:tag('span')
:addClass('inline-image')
:wikitext('[[File:' .. regionName .. ' LoR Region.png|' .. tostring(regionSize) .. 'px|link=LoR:' .. regionName .. ']]')
:done()
end
if stars then
local starHeader= builder.create('div')
:addClass('deck-header')
:css('position', 'absolute')
:css('top', '10px')
:css('left', '730px')
:tag('span')
:css('font-weight', 'bold')
:css('color', 'white')
:css('font-size', '18px')
:tag('span')
:addClass('PoC-Star-Difficulty')
:css('white-space', 'nowrap')
:tag('span')
:css('position', 'relative')
:css('text-indent', '0')
:css('top', '-3px')
:wikitext('[[File:PoC Star icon.png|60px|link=]]')
:tag('span')
:css('position', 'absolute')
:css('left', '17px')
:css('top', '30%')
:css('user-select', 'none')
:css('line-height', '1.5')
:css('text-align', 'center')
:css('width', '25px')
:css('height', '25px')
:css('transform', 'translateY(-25%)')
:css('text-shadow', '0.5px 0.5px 1px #000, 0.5px 0.5px 1px #000, -0.5px -0.5px 1px #000, -0.5px -0.5px 1px #000, 0.5px -0.5px 1px #000, 0.5px -0.5px 1px #000, -0.5px 0.5px 1px #000, -0.5px 0.5px 1px #000, 0.5px 0.5px 1px #000, 0.5px 0.5px 1px #000;')
:wikitext(stars)
:done()
:done()
:done()
:done()
template
:node(starHeader)
end
if adventureType then
local leftOffset = '660px'
if not stars then leftOffset = '730px' end
local typeImage = ''
if adventureData.Icons[adventureType] then
typeImage = '[[File:' .. adventureData.Icons[adventureType] .. '|60x60px|link=LoR:World Adventures]]'
end
local typeHeader= builder.create('div')
:addClass('deck-header')
:css('position', 'absolute')
:css('top', '8px')
:css('left', leftOffset)
:tag('span')
:wikitext(typeImage)
:done()
template
:node(typeHeader)
end
if #championsItem > 0 then
local deckChampions = builder.create('div')
:addClass('deck-champions')
:css('position', 'absolute')
:css('top', '70px')
:css('left', '20px')
:css('font-size', '20px')
:css('color', '#FFE3B0')
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Champion card.png|25px|link=LoR:Champion]]')
:done()
local n = 0
for _, deckData in pairs(championsItem) do
if (n == MAX_MOBILE_ITEMS and args['mobile']) or (n == MAX_ITEMS) then
deckChampions
:tag('span')
:css('font-weight', 'bold')
:css('font-size', '30px')
:addClass('inline-image')
:wikitext(frame:preprocess(' ...'))
:done()
break
end
if deckData.items then
local s = '{{PoCCardItems|size=70|' .. deckData.code
for _, item in pairs(deckData.items) do
s = s .. '|' .. item
end
s = s .. '}}'
deckChampions
:tag('span')
:addClass('inline-image')
:wikitext(frame:preprocess(s))
:done()
:tag('span')
:addClass('inline-image')
:wikitext(' x' .. deckData.count .. ' ')
:done()
else
deckChampions
:tag('span')
:addClass('inline-image')
:wikitext('[[File:' .. deckData.code .. '.png|70px|link=LoR:'.. deckData.code .. ']]')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('x' .. deckData.count .. ' ')
:done()
end
n = n+1
end
deckChampions:
done()
template
:node(deckChampions)
end
if items then
local deckItems = builder.create('div')
:addClass('deck-champions')
:css('position', 'absolute')
:css('top', '175px')
:css('left', '20px')
:css('font-size', '20px')
:css('color', '#FFE3B0')
:tag('span')
:addClass('inline-image')
:wikitext('[[File:PoC Relic icon.png|25px]]')
:done()
local n = 0
for _, itemList in ipairs(detailedItems) do
if (n == MAX_MOBILE_ITEMS and args['mobile']) or (n == MAX_ITEMS) then
deckItems
:tag('span')
:css('font-weight', 'bold')
:css('font-size', '30px')
:addClass('inline-image')
:wikitext(frame:preprocess(' ...'))
:done()
break
end
local count = 0;
local nonChampion = true
for _, data in pairs(championsItem) do
if data.code == itemList.code then
nonChampion = false
break
end
end
if nonChampion then
for _, data in pairs(deckSimple) do
if data.code == itemList.code then
count = data.count
break
end
end
local s = '{{PoCCardItems|size=70|' .. itemList.code
for _, item in ipairs(itemList.items) do
s = s .. '|' .. item
end
s = s .. '}}'
deckItems
:tag('span')
:addClass('inline-image')
:wikitext(frame:preprocess(s))
:done()
:tag('span')
:addClass('inline-image')
:wikitext(' x' .. count .. ' ')
:done()
n = n+1
end
end
deckItems:
done()
template
:node(deckItems)
end
local powerImage
local powerTitle
local powerInfo
if hasPower then
local image_leftOffset = 230
if args['mobile'] then image_leftOffset = 200 end
powerImage = builder.create('div')
:addClass('deck-info')
:css('position', 'absolute')
:css('top', '300px')
:css('left', tostring(image_leftOffset) .. 'px')
if powericon then
powerImage
:tag('span')
:css('display', 'inline-block')
:wikitext(frame:preprocess('{{PoCPowerIconOverlay|' .. powericon .. '|' .. powerrarity .. '|80}}'))
:done()
end
powerImage
:done()
powerTitle = builder.create('div')
:addClass('deck-info')
:css('position', 'absolute')
:css('top', '300px')
:css('left', tostring(image_leftOffset+100) .. 'px')
:css('color', '#FFE3B0')
:css('font-weight', 'bold')
:tag('span')
:css('display', 'inline-block')
:css('font-size', '16px')
:wikitext(powername)
:done()
powerTitle:done()
powerInfo = builder.create('div')
:addClass('deck-info')
:css('position', 'absolute')
:css('top', '330px')
:css('left', tostring(image_leftOffset+100) .. 'px')
:css('color', '#FFE3B0')
:tag('span')
:css('display', 'inline-block')
:css('max-width', '460px')
:css('font-size', '14px')
:wikitext(frame:preprocess(powerdesc))
:done()
powerInfo:done()
end
local health = foe.health or '?'
local mana = foe.mana or '?'
local hand = foe.hand or '?'
local vicious = foe.vicious or false
if hasPower then
template
:node(deckHeader)
:node(powerImage)
:node(powerTitle)
:node(powerInfo)
else
template
:node(deckHeader)
end
local color = '#FFE3B0'
if vicious then
color = '#FF7474'
if tonumber(health) then health = tostring(tonumber(health) + 10) end
if tonumber(mana) then mana = tostring(tonumber(mana) + 1) end
if tonumber(hand) then hand = tostring(tonumber(hand) + 1) end
end
template
:tag('div')
:addClass('deck-info')
:css('position', 'absolute')
:css('top', '320px')
:css('left', '15px')
:css('font-size', '20px')
:css('color', '#FFE3B0')
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Follower card.png|25px|link=LoR:Follower]]' .. followerCount .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Spell card.png|25px|link=LoR:Spell]]' .. spellCount .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Keyword Landmark.png|25px|link=LoR:Landmark]]' .. landmarkCount .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Keyword Equipment.png|25px|link=LoR:Equipment]]' .. equipmentCount .. ' ')
:done()
:done()
:tag('div')
:addClass('deck-info')
:css('position', 'absolute')
:css('top', '360px')
:css('left', '15px')
:css('font-size', '20px')
:css('color', color)
:tag('span')
:addClass('inline-image')
:wikitext('[[File:PoC Health icon.png|25px|link=LoR:Nexus Health]]' .. health .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:LoR mana icon alt.png|25px|link=LoR:Terminology#Mana]]' .. mana .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Keyword Draw.png|25px|link=]]' .. hand .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext(lib.ternary(vicious,frame:preprocess(' {{PoCPowerIconOverlay|04SI017-full.png|Special|30}}'),''))
:done()
:done()
template:allDone()
return tostring(template)
end
function p.pocDeckDecoder(frame)
local args = lib.frameArguments(frame)
local adventureData = require("Module:LoRPoCData/adventure")
local cmp = args['campaign'] or "World Adventures"
local groupBy = args['groupBy'] or 'none'
local groupByValues = {'region', 'type', 'none'}
if Utility.table_contains(groupByValues, groupBy) == false then
return 'Only support Group By "region", "type" or none'
end
if args['adventure'] == nil or not adventureData[cmp][args['adventure']] then
return 'Adventure missing or invalid'
end
local adventure = adventureData[cmp][args['adventure']]
if args['foe'] == nil or not adventure[args['foe']] then
return 'Foe missing or invalid'
end
local foe = adventure[args['foe']]
local items = foe.items or {}
local deckItems = {}
args['code'] = args['code'] or foe.deck or nil
if args['code'] == nil then
return 'Empty Code'
end
local header = args['header'] or true
if header == 'false' then
header = false
end
local result = {}
local deckList = p.deckDataFromCode{code = args['code']}
if type(deckList) ~= 'table' then
return deckList
end
for _, deckData in pairs(deckList) do
local groupByKey = ''
if groupBy == 'region' then
groupByKey = deckData.region
elseif groupBy == 'type' then
groupByKey = deckData.type .. 's'
else
groupByKey = 'List'
end
if result[groupByKey] == nil then
result[groupByKey] = {}
end
if items[deckData.code] then
table.insert(deckItems,deckData.code)
end
table.insert(
result[groupByKey],
{
code = deckData.code,
count = deckData.count,
name = deckData.name,
type = deckData.type
}
)
end
local sortedKeys = {}
for k in pairs(result) do
table.insert(sortedKeys, k)
end
table.sort(sortedKeys)
local template = ""
if header then
template = template .. p.pocHeaderDeck(frame)
end
local newline = "\n"
template = template .. "<tabber>" .. newline
template = template .. "Hide=" .. newline .. '|-|' .. newline
for i, groupByKey in ipairs(sortedKeys) do
if i ~= 1 then
template = template .. "|-|" .. newline
end
template = template .. groupByKey .. "=" .. newline .. "'''Code:''' <span style='word-wrap: break-word;'>" .. args['code'] .. '</span>' .. newline
local regionData = result[groupByKey]
table.sort(
regionData,
function(a, b)
if (a.type == b.type) then
return a.name < b.name
end
return a.type < b.type
end
)
local type = ""
local isFirst = true
for _, cardData in ipairs(regionData) do
if cardData.type ~= type then
if (type ~= "") then
template = template .. "|}" .. newline
end
template = template .. "{| class=\"wikitable\"" .. newline .. "! colspan=5 style=\"background-color: #374b65; text-align: left;\" |" .. cardData.type .. "s" .. newline .. "|-" .. newline
isFirst = true
type = cardData.type
end
local cardCodeTemplate = ""
if isFirst then
cardCodeTemplate = cardCodeTemplate .. '|-' .. newline
isFirst = false
else
cardCodeTemplate = cardCodeTemplate .. '|' .. newline
isFirst = true
end
cardCodeTemplate = cardCodeTemplate .."| {{LoR|" .. cardData.name
if cardData.type ~= "Champion" then
cardCodeTemplate = cardCodeTemplate .. "|code=" .. cardData.code
end
cardCodeTemplate = cardCodeTemplate .. "}}" .. " (x" .. cardData.count .. ")" .. newline
cardCodeTemplate = cardCodeTemplate .. "| "
if items[cardData.code] then
for _, item in ipairs(items[cardData.code]) do
if item:sub(1, 1) == "I" then
cardCodeTemplate = cardCodeTemplate .. "{{PoC|item|code=" .. item .. "}} "
else
cardCodeTemplate = cardCodeTemplate .. "{{PoC|relic|code=" .. item .. "}} "
end
end
end
template = template .. cardCodeTemplate .. newline
end
template = template .. "|}" .. newline
end
local extraItems = ""
local isFirst = true
for key, itemList in pairs(items) do
if true then
end
if not Utility.table_contains(deckItems,key) then
if isFirst then
extraItems = extraItems .. '|-' .. newline
isFirst = false
else
extraItems = extraItems .. '|' .. newline
isFirst = true
end
extraItems = extraItems .. "| {{LoR|code=" .. key .. "}}" .. newline .. "| "
for _, item in ipairs(itemList) do
if item:sub(1, 1) == "I" then
extraItems = extraItems .. "{{PoC|item|code=" .. item .. "}} "
else
extraItems = extraItems .. "{{PoC|relic|code=" .. item .. "}} "
end
end
extraItems = extraItems .. newline
end
end
if extraItems ~= "" then
if groupBy == 'region' or groupBy == 'type' then
template = template .. "|-|" .. newline .. "Non-deck=" .. newline .. "'''Code:''' " .. args['code'] .. newline
else
-- template = template .. ";Non-deck cards with items" .. newline
end
template = template .. "{| class=\"wikitable\"" .. newline .. "! colspan=5 style=\"background-color: #374b65; text-align: left;\" | Non-deck cards with items" .. newline .. "|-" .. newline .. extraItems .. "|}" .. newline
end
template = template .. "</tabber>"
return frame:preprocess(template)
end
function p.championRoster(frame)
local args = lib.frameArguments(frame)
local size = args["size"] or "120px"
local lorData = require('Module:LoRData/data')
local lorTable = {}
local s = ""
local REGION_CODES = {
DE = "Demacia",
FR = "Freljord",
IO = "Ionia",
NX = "Noxus",
PZ = "Piltover and Zaun",
SI = "Shadow Isles",
BW = "Bilgewater",
SH = "Shurima",
MT = "Targon",
BC = "Bandle City",
RU = "Runeterra"
}
local cardtable = {}
for cardcode in pairs(lorData) do
table.insert(cardtable, cardcode)
end
table.sort(cardtable)
local championtable = {}
for i, cardcode in pairs(cardtable) do
repeat
local t = lorData[cardcode]
if t.rarity ~= 'Champion' then
break
end
t['code'] = cardcode
table.insert(championtable, t)
break
until true
end
-- END
table.sort(championtable, function(a, b) return a['name'] < b['name'] end)
local rosterText = '' -- Temporarily will be class poc-roster
rosterText = rosterText .. '<div class="poc-roster-container" style="display: flex; flex-direction: column; margin: 0 auto; padding: 5px; text-align: center;">'
rosterText = rosterText .. '<div class="poc-roster" style="display: flex; justify-content: space-evenly; flex-wrap: wrap;">'
for _, card in pairs(championtable) do
rosterText = rosterText .. '<div class="poc-roster-champion" style="padding:0.5em;" data-champion="' .. card.name .. '" data-searchname="' .. card.name .. '" data-game="lor"'
if card.regions then
rosterText = rosterText .. ' data-region="' .. table.concat(card.regions, ", ") .. '">'
else
rosterText = rosterText .. ' data-region="' .. REGION_CODES[card.code:sub(3,4):upper()] .. '">'
end
rosterText = rosterText .. '[[File:' .. card.code .. '.png|' .. size .. '|link=LoR:' .. card.name .. ']] <br> [[LoR:' .. card.name .. '|' .. card.name .. ']]</div>'
end
rosterText = rosterText .. '</div>'
rosterText = rosterText .. '</div>'
return rosterText
end
function p.championRosterPoC(frame)
local args = lib.frameArguments(frame)
local size = args["size"] or "120px"
local pocData = require('Module:LoRPoCData/data')
local lorTable = {}
local s = ""
local rosterText = ''
rosterText = rosterText .. '<div class="poc-roster-container" style="display: flex; flex-direction: column; margin: 0 auto; padding: 5px; text-align: center;">'
rosterText = rosterText .. '<div class="poc-roster" style="display: flex; justify-content: space-evenly; flex-wrap: wrap;">'
pocData = pocData["Champion"]
local championtable = {}
for champion, data in pairs(pocData) do
if (data["playable"]) then
table.insert(championtable, data)
end
end
table.sort(championtable, function(a, b) return a.name < b.name end)
for _, data in pairs(championtable) do
rosterText = rosterText .. '<div class="poc-roster-champion" style="padding:0.5em;" data-champion="' .. data.name .. '" data-searchname="' .. data.name .. '" '
rosterText = rosterText .. 'data-game="lor" data-playstyle="' .. (data.playable.style or "no") .. '" data-difficulty="' .. (data.playable.difficulty or "no") .. '" '
rosterText = rosterText .. 'data-region="' .. table.concat(data.playable.region, ", ") .. '" '
if data.playable.constellationRegion then
rosterText = rosterText ..'data-constellation="' .. data.playable.constellationRegion .. ', hasConstellation" '
else
rosterText = rosterText ..'data-constellation="constellation-none" '
end
rosterText = rosterText .. '>[[File:' .. data.id .. '.png|' .. size .. '|link=LoR:' .. data.name .. '/PoC]] <br> [[LoR:' .. data.name .. '/PoC|' .. data.name .. ']]</div>'
end
rosterText = rosterText .. '</div>'
rosterText = rosterText .. '</div>'
return rosterText
end
function p.getPoCChampionGrid(frame)
-- Author: @Bladenite
local args = lib.frameArguments(frame)
local size = args["size"] or "120px"
local pocData = require('Module:LoRPoCData/data')
local gridText = ''
-- Creating outer container
gridText = gridText .. '<div class="poc-champions-flexbox-container" style="display: flex; flex-wrap: wrap; gap: 7.5px; justify-content: space-around;">'
-- Getting playable champions data
pocData = pocData["Champion"]
local championtable = {}
for champion, data in pairs(pocData) do
if (data["playable"]) then
table.insert(championtable, data)
end
end
table.sort(championtable, function(a, b) return a.name < b.name end)
for _, data in pairs(championtable) do
gridText = gridText .. '<div class="poc-champion-container" '
-- Adding data for search parameters
gridText = gridText .. 'data-champion="' .. data.name .. '" data-searchname="' .. data.name .. '" '
gridText = gridText .. 'data-game="lor" data-playstyle="' .. (data.playable.style or "no") .. '" data-difficulty="' .. (data.playable.difficulty or "no") .. '" '
gridText = gridText .. 'data-region="' .. table.concat(data.playable.region, ", ") .. '" '
if data.playable.constellationRegion then
gridText = gridText ..'data-constellation="' .. data.playable.constellationRegion .. ', hasConstellation" '
else
gridText = gridText ..'data-constellation="constellation-none" '
end
gridText = gridText .. '>' --closing div
-- Building champion board
gridText = gridText .. '<div class="poc-champion-circle"> [[File:' .. data.id .. '-circle.png|75px|link=LoR:' .. data.name .. '/PoC]]</div>'
gridText = gridText .. '<div class="poc-champion-name"> [[LoR:' .. data.name .. '/PoC|' .. data.name .. ']]</div>'
gridText = gridText .. '<div class="poc-champion-region">'
if #data.playable.region == 1 then
gridText = gridText .. '[[File:' .. data.playable.region[1] .. ' LoR Region.png|35px|link=]]</div>'
elseif #data.playable.region == 2 then
gridText = gridText .. '[[File:' .. data.playable.region[1] .. ' LoR Region.png|35px|link=]] '
gridText = gridText .. '[[File:' .. data.playable.region[2] .. ' LoR Region.png|35px|link=]]</div>'
end
if data.playable.style then
gridText = gridText .. '<div class="poc-champion-info"><span class="poc-' .. data.playable.style:lower() .. '">[[File:' .. data.playable.style .. ' LoR icon.png|20px|link=]] ' .. data.playable.style .. '</span> • '
end
if data.playable.difficulty then
gridText = gridText .. '<span class="poc-' .. data.playable.difficulty:lower() .. '"> ' .. data.playable.difficulty .. '</span></div>'
end
gridText = gridText .. '<div class="poc-champion-power" style="display: inline-flex; flex-wrap: wrap; justify-content: center;'
if data.name ~= 'Aurelion Sol' then
gridText = gridText .. 'width: 120px;' -- Controls the number of powers in one line (120px -> 3 powers per line)
end
gridText = gridText .. '>'
-- Ordering the powers correctly
for i, power in ipairs(data.playable.power) do
if power.code then
gridText = gridText .. '<span class="skin-icon" style="white-space: nowrap; order: ' .. tostring(i) .. ';"'
gridText = gridText .. ' data-game="lor" data-skin="PoC" data-champion="' .. power.code .. '"><span style="position:relative;text-indent:0;">'
gridText = gridText .. '[[File:' .. power.code .. '.png|30px|link=LoR:' .. data.name .. '/PoC]]  </span></span>'
end
end
gridText = gridText .. '</div>'
if data.support then
gridText = gridText .. '<div class="poc-champion-reinforcement"><span class="lor-tooltip" data-param="' .. data.id .. ',' .. data.support[1] .. ',' .. data.support[2] .. ', 50%">[[File:LoR Card icon.png|17px|link=|alt=Reinforcements]]</span></div>'
end
-- Closing champion container
gridText = gridText .. '</div>'
end
-- Closing grid container
gridText = gridText .. '</div>'
return gridText
end
function p.pocChampionNavigation(frame)
local args = lib.frameArguments(frame)
local size = args["size"] or "80px"
local constellationOnly = args["constellationOnly"] or false
local pocData = require('Module:LoRPoCData/data')
local lorTable = {}
local s = ""
local navText = ""
pocData = pocData["Champion"]
local championtable = {}
for champion, data in pairs(pocData) do
if constellationOnly ~= false then
if (data["playable"]) and (data["playable"]["constellationRegion"]) then
table.insert(championtable, data)
end
else
if (data["playable"]) then
table.insert(championtable, data)
end
end
end
table.sort(championtable, function(a, b) return a.name < b.name end)
for _, data in pairs(championtable) do
navText = navText .. '[[File:' .. data.id .. '.png|' .. size .. '|link=LoR:' .. data.name .. '/PoC]]'
end
return navText
end
function p.pocWeekliesDateRange()
local today = os.date("*t")
local current_wday = today.wday
local days_to_add = (2 - current_wday) % 7 -- 2 corresponds to Monday
if days_to_add == 0 then
days_to_add = 7
end
days_to_add = days_to_add - 1 -- Last day is non-inclusive
local next_sunday_time = os.time(today) + days_to_add * 86400 -- 86400 seconds in a day
local next_sunday = os.date("*t", next_sunday_time)
local next_month_name = os.date("%B", next_sunday_time)
local prev_monday_time = next_sunday_time - 6 * 86400
local prev_monday = os.date("*t", prev_monday_time)
local prev_month_name = os.date("%B", prev_monday_time)
return string.format("%s %d – %s %d", prev_month_name, prev_monday.day, next_month_name, next_sunday.day)
end
function p.subtypeContent(frame)
local args = lib.frameArguments(frame)
local cardcode = args['code'] or args[1] or nil
if not cardcode then
return ''
end
local lorData = mw.loadData('Module:LoRData/data')
subtypetext = ''
for _, subtype in pairs(lorData[cardcode].subtype) do
subtypelink = '[[LoR:' .. subtype .. '|' .. subtype .. ']]'
if subtypetext ~= '' then
subtypetext = subtypetext .. ", " .. subtypelink
else
subtypetext = subtypetext .. subtypelink
end
subtypetext = subtypetext .. "[[" .. "Category:LoR " .. subtype .. "]]"
end
return frame:preprocess(subtypetext)
end
function p.regionContent(frame)
local args = lib.frameArguments(frame)
local cardcode = args['code'] or args[1] or nil
if not cardcode then
return ''
end
local lorData = mw.loadData('Module:LoRData/data')
if not lorData[cardcode] then
return ''
end
local regions = {}
if not lorData[cardcode].regions then
local regionCode = string.sub(cardcode, 3, 4)
for _, regionData in pairs(REGIONS) do
if regionData.code == regionCode then
table.insert(regions, regionData.name)
break
end
end
else
regions = lorData[cardcode].regions
end
regiontext = ''
for _, region in pairs(regions) do
regionlink = '[[File:' .. region .. ' LoR Region.png|20px]] '
regionlink = regionlink .. '[[LoR:' .. region .. '|' .. region .. ']]'
if regiontext ~= '' then
regiontext = regiontext .. ", " .. regionlink
else
regiontext = regiontext .. regionlink
end
regiontext = regiontext .. "[[" .. "Category:LoR " .. region .. "]]"
end
if regiontext == '' then
regiontext = 'N/A'
end
return frame:preprocess(regiontext)
end
function p.artworkContent(frame)
local args = lib.frameArguments(frame)
local cardcode = args['code'] or args[1] or nil
if not cardcode then
return ''
end
local lorData = mw.loadData('Module:LoRData/data')
local imageSize = '125px'
artworkText = "<ul style='margin-left:0;'>"
artworkText = artworkText .. "<li style='display:inline-block;text-indent:0px;'>[[File:" .. cardcode .. "-full.png|thumb|"
.. imageSize .. "|Primary|left]]</li>"
if lorData[cardcode].alternate then
artworkText = artworkText .. "<li style='display:inline-block;text-indent:0px;'>[[File:" .. cardcode
.. "-alt-full.png|thumb|" .. imageSize .. "|Censored|left]]"
end
for _, artwork in pairs(lorData[cardcode].artworks or {}) do
local extension = 'png'
local artworkParts = lib.split(artwork, "-")
local HD = false
local alternate = false
local display = false
local prerelease = false
local old = false
for k, v in pairs(artworkParts) do
if (v == 'hd') then
HD = true
end
if (v == 'alt') then
alternate = true
end
if (v == 'display') then
display = true
end
if (v == 'pre') then
prerelease = true
end
if (v == 'old') then
old = true
end
end
local artworkContents = {}
if HD then
table.insert(artworkContents, 'HD')
extension = 'jpg'
end
if prerelease then
table.insert(artworkContents, 'Pre-Release')
end
if alternate then
table.insert(artworkContents, '[[LoR:List of cards/Alternate|Censored]]')
end
if display then
table.insert(artworkContents, '[[League Displays]]')
extension = 'jpg'
end
if old then
table.insert(artworkContents, 'Old')
end
if #artworkContents >= 1 then
artworkContent = table.concat(artworkContents, " ")
else
artworkContent = 'Uncategorized'
end
artworkLink = "<li style='display:inline-block;text-indent:0px;'>[[File:" .. cardcode .. "-"
.. artwork .. "-full." .. extension .. "|thumb|" .. imageSize .. "|" .. artworkContent .. "|left]]</li>"
if artworkText ~= '' then
artworkText = artworkText .. " " .. artworkLink
else
artworkText = artworkText .. artworkLink
end
end
artworkText = artworkText .. "</ul>"
return frame:preprocess(artworkText)
end
function p.artworkTooltip(frame)
local args = lib.frameArguments(frame)
local artworkCode = args['code'] or args[1] or nil
if not artworkCode then
return ''
end
local artworkList = lib.split(artworkCode, ",")
local width = 250
if string.find(artworkList[1], '-full') then
width = 300
end
if string.find(artworkList[#artworkList], '%%') then
width = math.floor(width * (tonumber(string.match(artworkList[#artworkList], '%d+')/100) or 1))
table.remove(artworkList, #artworkList)
end
if (#artworkList > 4) then
width = width / 2
end
local artworkTooltip = '<div style="display:inline-flex; flex-wrap:wrap; width:' .. math.min(width * 4, width * #artworkList) .. 'px; box-shadow: rgb(1, 10, 19) 0px 0px 25px 15px;">'
for k, v in pairs(artworkList) do
artworkTooltip = artworkTooltip .. '<div style="display:inline-block; background-color: rgb(1, 10, 19); flex: 1 1;">'
artworkTooltip = artworkTooltip .. '[[File:' .. v .. '.png|' .. width .. 'px]]'
artworkTooltip = artworkTooltip .. '</div>'
end
artworkTooltip = artworkTooltip .. '</div>'
return artworkTooltip
end
function p.randomCard(frame)
local args = lib.frameArguments(frame)
local lorData = mw.loadData('Module:LoRData/data')
local keys = {}
for key, _ in pairs(lorData) do
keys[#keys+1] = key --Store keys in another table
end
local randomCode
-- Random
math.randomseed(os.time())
math.random(); math.random(); math.random()
for x = 1,10 do
randomCode = keys[math.random(1, #keys)]
end
local randomCard = lorData[randomCode]
local link = 'LoR:' .. randomCode
local text = '[[File:' .. randomCode .. '.png|250px|link=' .. link .. ']]'
text = text .. '[[' .. link .. '|' .. randomCard.name .. ']]'
local cardMode = randomCard.mode
if cardMode then
text = text .. ' ([[LoR:List of cards/' .. cardMode .. '|' .. cardMode .. ']])'
end
return frame:preprocess(text)
end
-- VarInit
VarInt.AllButMSB = 0x7f
VarInt.JustMSB = 0x80
function VarInt.Pop(bytes)
local result = 0
local currentShift = 0
local bytesPopped = 0
for i = 1, #bytes do
bytesPopped = bytesPopped + 1
local current = BitOperator.AND(bytes[i], VarInt.AllButMSB)
result = BitOperator.OR(result, BitOperator.LEFT_SHIFT(current, currentShift))
if BitOperator.AND(bytes[i], VarInt.JustMSB) ~= VarInt.JustMSB then
Utility.table_splice(bytes, 1, bytesPopped)
return result
end
currentShift = currentShift + 7
end
-- error('Invalid Deck Code: Byte array did not contain valid varints.')
return nil
end
-- BitOperator
function BitOperator.OR(m, n)
local tbl_m = Utility.to_bits(m)
local tbl_n = Utility.to_bits(n)
Utility.expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
for i = 1, rslt do
if (tbl_m[i] == 0 and tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return Utility.table2number(tbl)
end
function BitOperator.AND(m, n)
local tbl_m = Utility.to_bits(m)
local tbl_n = Utility.to_bits(n)
Utility.expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
for i = 1, rslt do
if (tbl_m[i] == 0 or tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return Utility.table2number(tbl)
end
function BitOperator.RIGHT_SHIFT(n, bits)
Utility.check_int(n)
local high_bit = 0
if (n < 0) then
-- negative
n = BitOperator.NOT(math.abs(n)) + 1
high_bit = 2147483648 -- 0x80000000
end
for i = 1, bits do
n = n / 2
n = BitOperator.OR(math.floor(n), high_bit)
end
return math.floor(n)
end
function BitOperator.NOT(n)
local tbl = Utility.to_bits(n)
local size = math.max(table.getn(tbl), 32)
for i = 1, size do
if (tbl[i] == 1) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return Utility.table2number(tbl)
end
function BitOperator.LEFT_SHIFT(n, bits)
Utility.check_int(n)
if (n < 0) then
-- negative
n = BitOperator.NOT(math.abs(n)) + 1
end
for i = 1, bits do
n = n * 2
end
return BitOperator.AND(n, 4294967295) -- 0xFFFFFFFF
end
-- Utility
function Utility.str_split(str, size)
local result = {}
for i = 1, #str, size do
table.insert(result, str:sub(i, i + size - 1))
end
return result
end
function Utility.dec2bin(num)
local result = ""
repeat
local halved = num / 2
local int, frac = math.modf(halved)
num = int
result = math.ceil(frac) .. result
until num == 0
return result
end
function Utility.padRight(str, length, char)
while #str % length ~= 0 do
str = str .. char
end
return str
end
function Utility.padLeft(str, length, char)
while #str % length ~= 0 do
str = char .. str
end
return str
end
function Utility.table2number(tbl)
local n = table.getn(tbl)
local rslt = 0
local power = 1
for i = 1, n do
rslt = rslt + tbl[i] * power
power = power * 2
end
return rslt
end
function Utility.to_bits(n)
Utility.check_int(n)
if (n < 0) then
-- negative
return Utility.to_bits(bit.bnot(math.abs(n)) + 1)
end
-- to bits table
local tbl = {}
local cnt = 1
while (n > 0) do
local last = math.mod(n, 2)
if (last == 1) then
tbl[cnt] = 1
else
tbl[cnt] = 0
end
n = (n - last) / 2
cnt = cnt + 1
end
return tbl
end
function Utility.check_int(n)
-- checking not float
if (n - math.floor(n) > 0) then
error("trying to use bitwise operation on non-integer!")
end
end
function Utility.expand(tbl_m, tbl_n)
local big = {}
local small = {}
if (table.getn(tbl_m) > table.getn(tbl_n)) then
big = tbl_m
small = tbl_n
else
big = tbl_n
small = tbl_m
end
-- expand small
for i = table.getn(small) + 1, table.getn(big) do
small[i] = 0
end
end
function Utility.table_splice(tab, idx, n, ...) --> removed
local values = {...}
local init_tab_size = #tab
local removed = {}
if n > 0 then
for i = idx, (idx + n - 1) do
table.insert(removed, tab[i])
tab[i] = nil
end
end
local tail = {}
for i = (idx + n), init_tab_size do
table.insert(tail, tab[i])
tab[i] = nil
end
local i = idx
for _, v in ipairs(values) do
tab[i] = v
i = i + 1
end
i = idx + #values
for _, v in ipairs(tail) do
tab[i] = v
i = i + 1
end
return removed
end
function Utility.table_contains(table, val)
for i = 1, #table do
if table[i] == val then
return true
end
end
return false
end
function Utility.comma_value(n) -- credit http://richard.warburton.it
local left, num, right = string.match(n, '^([^%d]*%d)(%d*)(.-)$')
return left .. (num:reverse():gsub('(%d%d%d)', '%1,'):reverse()) .. right
end
return p
-- </pre>
-- [[Category:Lua]]