Module:LoRUtility: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
(Add function for LoR artwork content) |
mNo edit summary |
||
Line 519: | Line 519: | ||
for _, artwork in pairs(lorData[cardcode].artworks) do |
for _, artwork in pairs(lorData[cardcode].artworks) do |
||
artworkContent = 'Another Artwork' |
artworkContent = 'Another Artwork' |
||
local extension = 'png' |
|||
if artwork == 'hd' then |
if artwork == 'hd' then |
||
artworkContent = 'HD Artwork' |
artworkContent = 'HD Artwork' |
||
extension = 'jpg' |
|||
elseif artwork == 'alt-hd' then |
elseif artwork == 'alt-hd' then |
||
artworkContent = 'HD Alternate Artwork' |
artworkContent = 'HD Alternate Artwork' |
||
extension = 'jpg' |
|||
end |
end |
||
artworkLink = "<li style='display: inline-block;'>[[File:" .. cardcode .. "-" .. artwork .. " |
artworkLink = "<li style='display: inline-block;'>[[File:" .. cardcode .. "-" .. artwork .. "." .. extension .. "|thumb|135px|" .. artworkContent .. "|left]]</li>" |
||
if artworkText ~= '' then |
if artworkText ~= '' then |
Revision as of 11:29, 11 December 2020
Documentation for this module may be created at Module:LoRUtility/doc
-- <pre>
-- Inherit from https://github.com/SwitchbladeBot/runeterra
local lib = require('Module:Feature')
local MAX_KNOWN_VERSION = 2
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"},
[10] = {code = "MT", name = "Targon"}
}
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; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
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 rarities = {}
local champions = {}
local shardValues = {
["None"] = 0,
["Common"] = 100,
["Rare"] = 300,
["Epic"] = 1200,
["Champion"] = 3000,
}
local topChampion = nil
local topFollower = nil
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"
else
cardType = "Follower"
end
end
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
end
regions[regionName] = (regions[regionName] or 0) + count
else
table.insert(
deckList,
{
code = cardCode,
name = cardData.name,
count = count,
region = regionName,
type = cardType
}
)
end
end
for i = 3, 0, -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
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 .. '" is not exist'
end
handleCardData(lorData[cardCode], fourPlusNumber, cardCode, regionData.name)
end
if args['detail'] then
return {
shards = totalShards,
topUnitCode = (topChampion or topFollower).code,
regions = regions,
champions = champions,
followerCount = followerCount,
spellCount = spellCount,
landmarkCount = landmarkCount,
rarities = rarities
}
else
return deckList
end
end
function p.headerDeck(frame)
local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
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 rarities = deckDetail['rarities'] or nil
local template = mw.html.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 = mw.html.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=' .. regionName .. ' (Legends of Runeterra)]]')
: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=Shard (Legends of Runeterra)]]' .. Utility.comma_value(totalShards))
:done()
deckHeader:done()
local deckInfo = mw.html.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', '170px')
:css('left', '20px')
:css('font-size', '20px')
:css('color', '#FFE3B0')
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Follower card.svg|25px|link=Follower (Legends of Runeterra)]]' .. followerCount .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Spell card.svg|25px|link=Spell (Legends of Runeterra)]]' .. spellCount .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Keyword Landmark.png|25px|link=Landmark (Legends of Runeterra)]]' .. landmarkCount .. ' ')
:done()
:tag('br')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Common rarity.svg|25px|link=Card_types_(Legends_of_Runeterra)#By_rarity]]' .. (rarities[1] or 0) .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Rare rarity.svg|25px|link=Card_types_(Legends_of_Runeterra)#By_rarity]]' .. (rarities[2] or 0) .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Epic rarity.svg|25px|link=Card_types_(Legends_of_Runeterra)#By_rarity]]' .. (rarities[3] or 0) .. ' ')
:done()
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Champion rarity.svg|25px|link=Card_types_(Legends_of_Runeterra)#By_rarity]]' .. (rarities[4] or 0) .. ' ')
:done()
:done()
if #champions then
local deckChampions = mw.html.create('div')
:addClass('deck-champions')
:css('position', 'absolute')
:css('bottom', '20px')
:css('left', '20px')
:css('font-size', '20px')
:css('color', '#FFE3B0')
:tag('span')
:addClass('inline-image')
:wikitext('[[File:Champion card.svg|25px|link=Champion (Legends of Runeterra)]]')
:done()
for _, deckData in pairs(champions) do
deckChampions
:tag('span')
:addClass('inline-image')
:wikitext('[[File:' .. deckData.code .. '.png|60px|link='.. deckData.code .. ' (Legends of Runeterra)]]')
: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; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
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 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 = cardType .. '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 = ""
template = template .. p.headerDeck{code = args['code'], name = args['name'], description = args['description']}
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.subtypeContent(frame)
local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
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 = '[[' .. subtype .. ' (Legends of Runeterra)|' .. 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.artworkContent(frame)
local args; if frame.args == nil then args = lib.arguments(frame) else args = lib.arguments(frame.args) end
local cardcode = args['code'] or args[1] or nil
if not cardcode then
return ''
end
local lorData = mw.loadData('Module:LoRData/data')
artworkText = "<ul style='margin-left:0'>"
artworkText = artworkText .. "<li style='display: inline-block;'>[[File:" .. cardcode .. "-full.png|thumb|135px|Main Artwork|left]]</li>"
if lorData[cardcode].alternate then
artworkText = artworkText .. "<li style='display: inline-block;'>[[File:" .. cardcode .. "-alt-full.png|thumb|135px|Alternate Artwork|left]]"
end
for _, artwork in pairs(lorData[cardcode].artworks) do
artworkContent = 'Another Artwork'
local extension = 'png'
if artwork == 'hd' then
artworkContent = 'HD Artwork'
extension = 'jpg'
elseif artwork == 'alt-hd' then
artworkContent = 'HD Alternate Artwork'
extension = 'jpg'
end
artworkLink = "<li style='display: inline-block;'>[[File:" .. cardcode .. "-" .. artwork .. "." .. extension .. "|thumb|135px|" .. artworkContent .. "|left]]</li>"
if artworkText ~= '' then
artworkText = artworkText .. " " .. artworkLink
else
artworkText = artworkText .. artworkLink
end
end
artworkText = artworkText .. "</ul>"
return frame:preprocess(artworkText)
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.RIGHT_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]]