BryghtShadow (talk | contribs) m (tostring) |
BryghtShadow (talk | contribs) m (Use full page name instead.) |
||
(3 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
+ | local mArguments -- lazy load |
||
− | local getArgs = require("Module:Arguments").getArgs |
||
− | local |
+ | local cargo = require("Module:Cargo") |
+ | local cargo_store = cargo.store |
||
+ | local mm = require("Module:Math") |
||
+ | local yesno = require('Module:Yesno') |
||
local p = {} |
local p = {} |
||
+ | |||
− | local black = '★' |
||
− | local |
+ | 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 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 |
+ | local i18n = { |
+ | categories = { |
||
− | warrior = "Warrior Icon.png", |
||
+ | -- Tracking categories |
||
− | mage = "Mage Icon.png", |
||
+ | non_support_with_no_atk = "Non-support abilities with no attack", |
||
− | ranger = "Ranger Icon.png", |
||
+ | non_support_with_no_brk = "Non-support abilities with no break power", |
||
− | monk = "Monk Icon.png", |
||
+ | non_support_with_fixed_atk = "Non-support abilities with fixed attack", |
||
− | meia = "Mage Icon.png", |
||
+ | non_support_with_fixed_brk = "Non-support abilities with fixed break power", |
||
− | sarah = "Ranger Icon.png", |
||
+ | non_support_with_cooldown = "Non-support abilities with cooldown", |
||
− | sophie = "Monk Icon.png", |
||
+ | 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 |
+ | local function number_to_stars(n) |
− | + | local n = tonumber(n) or 0 |
|
+ | return string.rep(WHITE, n / 10) .. string.rep(BLACK, n % 10) |
||
end |
end |
||
− | local function |
+ | local function parse_number(s) |
− | + | return tonumber(s) or 0 |
|
− | local _, b_count, w_coun |
||
− | if s == nil then return 0 end |
||
− | if total == nil then |
||
− | _, b_count = string.gsub(s, black, '') |
||
− | _, w_count = string.gsub(s, white, '') |
||
− | total = b_count + w_count * 10 |
||
− | end |
||
− | return total |
||
end |
end |
||
− | local function |
+ | local function parse_stars(s) |
− | + | local s = tostring(s) |
|
+ | :gsub("★", BLACK) |
||
− | local w_count = math.floor(total / 10) |
||
+ | :gsub("★", BLACK) |
||
− | local b_count = total % 10 |
||
+ | :gsub("★", BLACK) |
||
− | return string.rep(white, w_count) .. string.rep(black, b_count) |
||
+ | :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 |
end |
||
− | + | function p.get_levels(args) |
|
− | + | local rarity = tostring(args.rarity or '') |
|
+ | if mw.text.trim(rarity) == '' then |
||
− | local filepath = jobtype_icon_paths[s] |
||
+ | error(i18n.errors.rarity_empty) |
||
− | if filepath then |
||
+ | end |
||
− | return string.format('[[File:%s|20x20px|link=]]', filepath) |
||
+ | local jobtype = tostring(args.jobtype):lower() |
||
− | end |
||
+ | local is_support = jobtype == 'support' or jobtype == 'healer' |
||
− | end |
||
+ | local rarities = {} |
||
− | function p.ultimate(frame) |
||
+ | local is_fast |
||
− | local args = getArgs(frame,{ |
||
+ | for digit, plus in rarity:gmatch('(%d+)(%+?)') do |
||
− | wrappers = "Template:Ultimate ability stat" |
||
+ | digit = tonumber(digit) |
||
− | }) |
||
+ | if digit < 1 or digit > 5 then |
||
− | return p._ultimate(args) |
||
+ | 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 |
end |
||
− | function p. |
+ | function p._main(args) |
+ | -- Load args into locals |
||
− | local gauge = {160, 155, 150, 140, 135, 130, 125, 110, 100, 80} |
||
− | + | 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) |
|
− | atk = tonumber(string.match(atk or '', '(%d+)%%?')) or 0 |
||
− | local brk = args.brk or args.breakpower |
||
− | brk = tonumber(string.match(brk or '', '(%d+)%%?')) or 0 |
||
− | local const_atk = yesno(args.const_attack, false) |
||
− | local const_brk = yesno(args.const_breakpower, false) |
||
− | local critchance = args.critchance |
||
− | local extra = args.extra |
||
− | |||
− | local total = count_stars(critchance) |
||
− | local critstars = number_to_stars(total) |
||
− | |||
− | local tbl = { |
||
− | {"Lv", "Ultimate Gauge", "Attack", "Break Power", "Crit Chance"} |
||
− | } |
||
− | for lv=1,10 do |
||
− | local m = multis[lv] |
||
− | local atk_n = atk * m |
||
− | local brk_n = brk * m |
||
+ | local is_fixed_atk = yesno(args.const_atk or args.const_attack) |
||
− | if const_atk then atk_n = atk end |
||
+ | local is_fixed_brk = yesno(args.const_brk or args.const_breakpower) |
||
− | if const_brk then brk_n = brk end |
||
+ | local is_support = jobtype == 'support' |
||
− | local row = { |
||
+ | local has_cooldown = args.has_cooldown |
||
− | lv, |
||
+ | if has_cooldown == nil then -- If there is a support with no-cooldown, then this would be "false" |
||
− | gauge[lv], |
||
+ | has_cooldown = is_support |
||
− | atk_n .. "%", |
||
+ | else |
||
− | brk_n .. "%", |
||
+ | has_cooldown = yesno(has_cooldown) |
||
− | string.format("%s (%d%%)", critstars, total * 5) |
||
+ | end |
||
− | } |
||
− | tbl[#tbl+1] = row |
||
− | end |
||
− | + | local lvls = p.get_levels(args) |
|
+ | local atks = {} |
||
− | root |
||
+ | local brks = {} |
||
− | :wikitext(jobtype_icon(jobtype)) |
||
+ | local crts = {} |
||
− | :tag('b') |
||
+ | local cds = {} |
||
− | :wikitext(name) |
||
+ | |||
− | :done() |
||
+ | -- Handle fixed and varying stats. |
||
− | :newline() |
||
+ | for i=1,10 do |
||
− | :tag("div") |
||
+ | local atk |
||
− | :cssText("font-style:italic") |
||
+ | if is_fixed_atk then |
||
− | :wikitext('"' .. (info or '???') .. '"') |
||
+ | atk = base_atk |
||
− | :done() |
||
+ | else |
||
− | :newline() |
||
+ | local atk_i = args['atk'..i] or args['attack'..i] |
||
− | local wikitable = root:tag('table') |
||
+ | local atk_m = base_atk * multis[i] |
||
− | :addClass('wikitable') |
||
+ | atk = atk_i or atk_m |
||
− | for i,row in ipairs(tbl) do |
||
+ | end |
||
− | local tr = wikitable:tag('tr') |
||
+ | atks[i] = mm._round(atk) |
||
− | local tagname = (i == 1) and 'th' or 'td' |
||
+ | local brk |
||
− | for j,col in ipairs(row) do |
||
+ | if is_fixed_brk then |
||
− | tr |
||
+ | brk = base_brk |
||
− | :tag(tagname) |
||
+ | else |
||
− | :wikitext(col) |
||
+ | local brk_i = args['brk'..i] or args['breakpower'..i] or args['break_power'..i] |
||
− | :done() |
||
+ | local brk_m = base_brk * multis[i] |
||
− | end |
||
+ | brk = brk_i or brk_m |
||
− | tr:done():newline() |
||
− | + | end |
|
+ | brks[i] = mm._round(brk) |
||
− | wikitable:done():newline() |
||
+ | crts[i] = base_crt |
||
− | if extra then |
||
+ | if has_cooldown then |
||
− | root:tag('b') |
||
+ | cds[i] = args['cd'..i] or args['cooldown'..i] or cooldowns[i] |
||
− | :wikitext('Added Effects: ') |
||
+ | end |
||
− | root:wikitext(extra) |
||
− | + | end |
|
− | return tostring(root) |
||
− | end |
||
+ | -- If the stat set contains only one value, then it is a fixed stat. |
||
− | function p._main(args) |
||
− | + | local atk_set = {} |
|
− | + | local atk_len = 0 |
|
− | + | local brk_set = {} |
|
+ | local brk_len = 0 |
||
− | local brk = args.brk or args.breakpower |
||
+ | -- We're only checking the values at each level. |
||
− | local crit = args.crit or args.critchance |
||
+ | for i,lvl in ipairs(lvls) do |
||
− | local const_atk = yesno(args.const_attack, false) |
||
+ | -- Before insert, check if it's already defined. |
||
− | local const_brk = yesno(args.const_breakpower, false) |
||
+ | local k = atks[lvl] |
||
− | local has_cooldown = yesno(args.has_cooldown, false) |
||
− | + | if not atk_set[k] then |
|
+ | atk_len = atk_len + 1 |
||
− | mw.log('ability stat: "rarity" argument is null or empty') |
||
+ | atk_set[k] = true |
||
− | return '' |
||
− | + | end |
|
+ | k = brks[lvl] |
||
− | local cooldowns = {8, 7, 6, 5, 4, 3, 2, 2, 1, 1} |
||
+ | if not brk_set[k] then |
||
− | |||
+ | brk_len = brk_len + 1 |
||
− | local fast = false |
||
+ | brk_set[k] = true |
||
− | local rarities = {} |
||
+ | end |
||
− | for a,b in string.gmatch(rarity, '(-?%d+)(%+?)') do |
||
+ | end |
||
− | a = tonumber(a) |
||
+ | -- no value, or the only value is "0". |
||
− | if a < 1 or 5 < a then |
||
+ | local has_no_atk = atk_len == 0 or (atk_len == 1 and atk_set[0]) |
||
− | error("Rarity outside the range of 1~5: "..a..b) |
||
+ | local has_no_brk = atk_len == 0 or (atk_len == 1 and atk_set[0]) |
||
− | elseif b == '+' then |
||
+ | -- If only one value, and is not "0". |
||
− | fast = true |
||
+ | local has_fixed_atk = atk_len == 1 and not atk_set[0] |
||
− | end |
||
+ | local has_fixed_brk = brk_len == 1 and not brk_set[0] |
||
− | rarities[a] = true |
||
+ | -- If there are 2 or more different values. |
||
− | end |
||
+ | -- Is "0" a valid value? |
||
− | atk = tonumber(atk) or 0 |
||
+ | local has_varied_atk = atk_len > 1 and not atk_set[0] |
||
− | brk = tonumber(brk) or 0 |
||
+ | local has_varied_brk = brk_len > 1 and not brk_set[0] |
||
− | local total = count_stars(crit) |
||
+ | -- Create data rows for storage and display. |
||
− | local critstars = number_to_stars(total) |
||
+ | local data_rows = {} |
||
− | local crit_n = string.format("%s (%d%%)", critstars, total*5) |
||
+ | for i,lvl in ipairs(lvls) do |
||
− | -- If attack is 0, then it's a support ability. |
||
+ | local data_row = { |
||
− | -- If break is 0, then it's a support ability. |
||
+ | level = lvl, |
||
− | local support = atk == 0 or brk == 0 |
||
+ | attack = atks[lvl], |
||
− | local show_atk = atk > 0 |
||
+ | break_power = brks[lvl], |
||
− | local show_brk = brk > 0 |
||
+ | crit_chance = crts[lvl], |
||
− | local show_cooldown = support or (fast and not crit) or has_cooldown |
||
+ | cooldown = cds[lvl], |
||
− | local show_crit = crit ~= nil and total >= 0 |
||
+ | } |
||
− | |||
+ | data_rows[#data_rows+1] = data_row |
||
− | local columns = {} |
||
+ | end |
||
− | local rows = {} |
||
+ | |||
− | local level |
||
+ | -- Storage |
||
− | local m, c -- y = mx + c |
||
+ | if NAMESPACE == 0 or args.demospace == "main" then |
||
− | if support then |
||
+ | local abilities = mw.ext.cargo.query("abilities", "_pageName", { |
||
− | m = 1 |
||
+ | where = string.format("_pageName = %q", FULLPAGENAME), |
||
− | c = 1 |
||
+ | }) |
||
− | else |
||
+ | if #abilities > 0 then |
||
− | m = 2 |
||
+ | for i,data_row in ipairs(data_rows) do |
||
− | c = 0 |
||
+ | cargo.store("ability_stats", data_row) |
||
− | end |
||
+ | end |
||
− | local levels = {} |
||
+ | end |
||
− | if fast then |
||
+ | end |
||
− | for rarity=1,5 do |
||
+ | |||
− | local has_rarity = rarities[rarity] |
||
+ | -- Display |
||
− | if has_rarity then |
||
+ | local root = mw.html.create("table") |
||
− | levels[#levels+1] = rarity * m + c |
||
+ | root:addClass("wikitable") |
||
− | end |
||
+ | local header = root:tag("tr") |
||
− | end |
||
+ | header:tag("th"):wikitext("ALv."):done() |
||
− | else |
||
+ | header:tag("th"):wikitext("Attack"):done() |
||
− | local max_rarity = 0 -- largest rarity |
||
+ | header:tag("th"):wikitext("Break Power"):done() |
||
− | for i=1,5 do |
||
+ | header:tag("th"):wikitext("Crit Chance"):done() |
||
− | if rarities[i] and max_rarity < i then |
||
+ | header:tag("th"):wikitext("Cooldown"):done() |
||
− | max_rarity = i |
||
+ | for i,data_row in ipairs(data_rows) do |
||
− | end |
||
+ | local lvl = data_row.level |
||
− | end |
||
+ | local atk = data_row.attack or 0 |
||
− | for level=1, max_rarity * m + c do |
||
+ | local brk = data_row.break_power or 0 |
||
− | levels[#levels+1] = level |
||
+ | local crt = data_row.crit_chance |
||
− | end |
||
+ | local stars = number_to_stars(crt) |
||
− | end |
||
+ | crt = string.format("%s (%d%%)", stars, crt * 5) |
||
− | table.insert(columns, 'ALv.') |
||
+ | local cd = data_row.cooldown or 0 |
||
− | if show_atk then table.insert(columns, 'Attack') end |
||
− | if show_brk then table.insert(columns, 'Break Power') end |
||
− | if show_crit then table.insert(columns, 'Crit Chance') end |
||
− | if show_cooldown then table.insert(columns, 'Cooldown') end |
||
− | for i,level in ipairs(levels) do |
||
− | local row = {} |
||
− | local m = multis[level] |
||
− | row[#row+1] = level |
||
− | if show_atk then |
||
− | local atk_n = atk |
||
− | if not const_atk then atk_n = round(atk * m) end |
||
− | row[#row+1] = atk_n |
||
− | end |
||
− | if show_brk then |
||
− | local brk_n = brk |
||
− | if not const_brk then brk_n = round(brk * m) end |
||
− | row[#row+1] = brk_n |
||
− | end |
||
− | if show_crit then |
||
− | row[#row+1] = crit_n |
||
− | end |
||
− | if show_cooldown then |
||
− | local cooldown = cooldowns[level] |
||
− | row[#row+1] = cooldown |
||
− | end |
||
− | rows[#rows+1] = row |
||
− | end |
||
+ | local tbl_row = root:tag('tr') |
||
− | local wikitable = mw.html.create('table'):addClass('wikitable'):newline() |
||
+ | tbl_row:tag("td"):wikitext(lvl):done() |
||
− | local tr = mw.html.create('tr') |
||
+ | tbl_row:tag("td"):wikitext(atk):done() |
||
− | for i,th in ipairs(columns) do |
||
− | + | tbl_row:tag("td"):wikitext(brk):done() |
|
+ | tbl_row:tag("td"):wikitext(crt):done() |
||
− | end |
||
+ | tbl_row:tag("td"):wikitext(cd):done() |
||
− | wikitable:node(tr):newline() |
||
+ | end |
||
− | for i,row in ipairs(rows) do |
||
+ | |||
− | tr = mw.html.create('tr') |
||
+ | -- Categories |
||
− | for j,td in ipairs(row) do |
||
+ | local cats = {} |
||
− | tr:tag('td'):wikitext(td) |
||
+ | if NAMESPACE == 0 or args.demospace == "main" then |
||
− | end |
||
+ | if is_support then |
||
− | wikitable:node(tr):newline() |
||
+ | if has_fixed_atk then |
||
− | end |
||
+ | cats[#cats+1] = i18n.categories.support_with_fixed_atk |
||
− | |||
+ | elseif has_varied_atk then |
||
− | local cats = {} |
||
+ | cats[#cats+1] = i18n.categories.support_with_varied_atk |
||
− | if const_atk and const_brk then |
||
+ | end |
||
− | cats[#cats+1] = "Abilities with fixed attack and break power" |
||
− | + | 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 |
||
− | for i,cat in ipairs(cats) do |
||
+ | if has_no_atk then |
||
− | cats[i] = string.format('[[Category:%s]]', cat) |
||
+ | cats[#cats+1] = i18n.categories.non_support_with_no_atk |
||
− | end |
||
+ | elseif has_fixed_atk then |
||
− | wikitable = tostring(wikitable) .. table.concat(cats) |
||
+ | cats[#cats+1] = i18n.categories.non_support_with_fixed_atk |
||
− | return tostring(wikitable) |
||
+ | 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 |
end |
||
function p.main(frame) |
function p.main(frame) |
||
+ | mArguments = require("Module:Arguments") |
||
− | local args = getArgs(frame,{ |
||
+ | local args = mArguments.getArgs(frame) |
||
− | wrappers = "Template:Ability stat" |
||
+ | return p._main(args) |
||
− | }) |
||
− | return p._main(args) |
||
− | end |
||
− | |||
− | function p._test() |
||
− | local args = { |
||
− | {rarity="4+",attack="450",breakpower="1",critchance="0",has_cooldown="n"}, |
||
− | } |
||
− | for i,v in ipairs(args) do |
||
− | mw.log(i, p._main(v)) |
||
− | end |
||
end |
end |
||
Latest revision as of 11:21, 5 February 2019
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