Tests[]
All tests passed.
Name | Expected | Actual | |
---|---|---|---|
testFastSupportLevels | |||
testFastWarriorLevels | |||
testNormalSupportLevels | |||
testNormalWarriorLevels |
local mArguments -- lazy load
local cargo = require("Module:Cargo")
local cargo_store = cargo.store
local mm = require("Module:Math")
local yesno = require('Module:Yesno')
local p = {}
local BLACK = mw.ustring.char(0x2605)
local WHITE = mw.ustring.char(0x2606)
local NAMESPACE = mw.title.getCurrentTitle().namespace
local FULLPAGENAME = mw.title.getCurrentTitle().fullText
local multis = {1.0, 1.1, 1.2, 1.3, 1.4, 1.7, 2.0, 2.3, 2.6, 3.0}
local cooldowns = {8, 7, 6, 5, 4, 3, 2, 2, 1, 1}
local i18n = {
categories = {
-- Tracking categories
non_support_with_no_atk = "Non-support abilities with no attack",
non_support_with_no_brk = "Non-support abilities with no break power",
non_support_with_fixed_atk = "Non-support abilities with fixed attack",
non_support_with_fixed_brk = "Non-support abilities with fixed break power",
non_support_with_cooldown = "Non-support abilities with cooldown",
support_with_varied_atk = "Support abilities with attack",
support_with_varied_brk = "Support abilities with break power",
support_with_fixed_atk = "Support abilities with fixed attack",
support_with_fixed_brk = "Support abilities with fixed break power",
support_with_no_cooldown = "Support abilities with no cooldown",
},
errors = {
rarity_empty = "No rarity specified.",
rarity_not_a_number = "Rarity is not a number: %q",
rarity_out_of_bounds = "Rarity is outside the range of 1-5: %d",
}
}
local function number_to_stars(n)
local n = tonumber(n) or 0
return string.rep(WHITE, n / 10) .. string.rep(BLACK, n % 10)
end
local function parse_number(s)
return tonumber(s) or 0
end
local function parse_stars(s)
local s = tostring(s)
:gsub("★", BLACK)
:gsub("★", BLACK)
:gsub("★", BLACK)
:gsub("☆", WHITE)
:gsub("☆", WHITE)
:gsub("☆", WHITE)
local pattern = "^%s*(["..BLACK..WHITE.."]*)%s*x?%s*(%d*)%s*$"
local stars, digit = mw.ustring.match(s, pattern)
if digit == "" and stars == "" then
mw.log(string.format("Count not find stars or numbers: %q", s))
return 0
elseif digit == "" and stars ~= "" then
local _, bcount, wcount
_, bcount = string.gsub(stars, BLACK, "")
_, wcount = string.gsub(stars, WHITE, "")
return bcount + wcount * 10
elseif digit ~= "" and (stars == "" or stars == BLACK) then
return tonumber(digit)
elseif digit ~= "" and stars ~= "" then
mw.log(string.format("Number and multiple stars specified: %q", s))
-- Stars were specified multiple times AND digits also follow.
return 0
end
end
function p.get_levels(args)
local rarity = tostring(args.rarity or '')
if mw.text.trim(rarity) == '' then
error(i18n.errors.rarity_empty)
end
local jobtype = tostring(args.jobtype):lower()
local is_support = jobtype == 'support' or jobtype == 'healer'
local rarities = {}
local is_fast
for digit, plus in rarity:gmatch('(%d+)(%+?)') do
digit = tonumber(digit)
if digit < 1 or digit > 5 then
error(i18n.errors.rarity_out_of_bounds:format(digit))
end
rarities[#rarities+1] = digit
is_fast = is_fast or plus == '+'
end
if #rarity == 0 then
error(i18n.errors.rarity_not_a_number:format(rarity))
end
local min_rarity = mm._min(unpack(rarities))
local max_rarity = mm._max(unpack(rarities))
local m, c
if is_support then
m, c = 1, 1
else
m, c = 2, 0
end
local start = 1
local stop = m * max_rarity + c
local step = 1
if is_fast then
start = m * min_rarity + c
step = m
end
local levels = {}
for x=start, stop, step do
levels[#levels+1] = x
end
return levels
end
function p._main(args)
-- Load args into locals
local jobtype = tostring(args.jobtype):lower()
local base_atk = parse_number(args.atk or args.attack)
local base_brk = parse_number(args.brk or args.break_power or args.breakpower)
local base_crt = parse_stars(args.crt or args.crit_chance or args.critchance)
local is_fixed_atk = yesno(args.const_atk or args.const_attack)
local is_fixed_brk = yesno(args.const_brk or args.const_breakpower)
local is_support = jobtype == 'support'
local has_cooldown = args.has_cooldown
if has_cooldown == nil then -- If there is a support with no-cooldown, then this would be "false"
has_cooldown = is_support
else
has_cooldown = yesno(has_cooldown)
end
local lvls = p.get_levels(args)
local atks = {}
local brks = {}
local crts = {}
local cds = {}
-- Handle fixed and varying stats.
for i=1,10 do
local atk
if is_fixed_atk then
atk = base_atk
else
local atk_i = args['atk'..i] or args['attack'..i]
local atk_m = base_atk * multis[i]
atk = atk_i or atk_m
end
atks[i] = mm._round(atk)
local brk
if is_fixed_brk then
brk = base_brk
else
local brk_i = args['brk'..i] or args['breakpower'..i] or args['break_power'..i]
local brk_m = base_brk * multis[i]
brk = brk_i or brk_m
end
brks[i] = mm._round(brk)
crts[i] = base_crt
if has_cooldown then
cds[i] = args['cd'..i] or args['cooldown'..i] or cooldowns[i]
end
end
-- If the stat set contains only one value, then it is a fixed stat.
local atk_set = {}
local atk_len = 0
local brk_set = {}
local brk_len = 0
-- We're only checking the values at each level.
for i,lvl in ipairs(lvls) do
-- Before insert, check if it's already defined.
local k = atks[lvl]
if not atk_set[k] then
atk_len = atk_len + 1
atk_set[k] = true
end
k = brks[lvl]
if not brk_set[k] then
brk_len = brk_len + 1
brk_set[k] = true
end
end
-- no value, or the only value is "0".
local has_no_atk = atk_len == 0 or (atk_len == 1 and atk_set[0])
local has_no_brk = atk_len == 0 or (atk_len == 1 and atk_set[0])
-- If only one value, and is not "0".
local has_fixed_atk = atk_len == 1 and not atk_set[0]
local has_fixed_brk = brk_len == 1 and not brk_set[0]
-- If there are 2 or more different values.
-- Is "0" a valid value?
local has_varied_atk = atk_len > 1 and not atk_set[0]
local has_varied_brk = brk_len > 1 and not brk_set[0]
-- Create data rows for storage and display.
local data_rows = {}
for i,lvl in ipairs(lvls) do
local data_row = {
level = lvl,
attack = atks[lvl],
break_power = brks[lvl],
crit_chance = crts[lvl],
cooldown = cds[lvl],
}
data_rows[#data_rows+1] = data_row
end
-- Storage
if NAMESPACE == 0 or args.demospace == "main" then
local abilities = mw.ext.cargo.query("abilities", "_pageName", {
where = string.format("_pageName = %q", FULLPAGENAME),
})
if #abilities > 0 then
for i,data_row in ipairs(data_rows) do
cargo.store("ability_stats", data_row)
end
end
end
-- Display
local root = mw.html.create("table")
root:addClass("wikitable")
local header = root:tag("tr")
header:tag("th"):wikitext("ALv."):done()
header:tag("th"):wikitext("Attack"):done()
header:tag("th"):wikitext("Break Power"):done()
header:tag("th"):wikitext("Crit Chance"):done()
header:tag("th"):wikitext("Cooldown"):done()
for i,data_row in ipairs(data_rows) do
local lvl = data_row.level
local atk = data_row.attack or 0
local brk = data_row.break_power or 0
local crt = data_row.crit_chance
local stars = number_to_stars(crt)
crt = string.format("%s (%d%%)", stars, crt * 5)
local cd = data_row.cooldown or 0
local tbl_row = root:tag('tr')
tbl_row:tag("td"):wikitext(lvl):done()
tbl_row:tag("td"):wikitext(atk):done()
tbl_row:tag("td"):wikitext(brk):done()
tbl_row:tag("td"):wikitext(crt):done()
tbl_row:tag("td"):wikitext(cd):done()
end
-- Categories
local cats = {}
if NAMESPACE == 0 or args.demospace == "main" then
if is_support then
if has_fixed_atk then
cats[#cats+1] = i18n.categories.support_with_fixed_atk
elseif has_varied_atk then
cats[#cats+1] = i18n.categories.support_with_varied_atk
end
if has_fixed_brk then
cats[#cats+1] = i18n.categories.support_with_fixed_brk
elseif has_varied_brk then
cats[#cats+1] = i18n.categories.support_with_varied_brk
end
if not has_cooldown then
cats[#cats+1] = i18n.categories.support_with_no_cooldown
end
else
if has_no_atk then
cats[#cats+1] = i18n.categories.non_support_with_no_atk
elseif has_fixed_atk then
cats[#cats+1] = i18n.categories.non_support_with_fixed_atk
end
if has_no_brk then
cats[#cats+1] = i18n.categories.non_support_with_no_brk
elseif has_fixed_brk then
cats[#cats+1] = i18n.categories.non_support_with_fixed_brk
end
if has_cooldown then
cats[#cats+1] = i18n.categories.non_support_with_cooldown
end
end
end
for i,cat in ipairs(cats) do
cats[i] = string.format('[[Category:%s]]', cat)
end
-- Output
return tostring(root) .. table.concat(cats)
end
function p.main(frame)
mArguments = require("Module:Arguments")
local args = mArguments.getArgs(frame)
return p._main(args)
end
return p