Documentation for this module may be created at Module:DropList/doc
local find = require('Module:Utils').find
local format = require('Module:StringInterpolation').format
local getArgs = require('Module:GetArgs')
local Ship = require('Module:Ship')
local Formatting = require('Module:Formatting')
-- * Rarity definitions.
-- sync with http://kancolle.wikia.com/wiki/Template:DropList/doc
local rare_ships = {
'Agano', 'Akashi', 'Akitsu Maru', 'Akitsushima', 'Akizuki', 'Amagi', 'Amatsukaze', 'Arashi', 'Asagumo', 'Asashimo',
'Bismarck', 'Graf Zeppelin', 'Hagikaze', 'Harusame', 'Hatsukaze', 'Hatsuzuki', 'Hayashimo', 'Hayasui', 'I-401', 'Isokaze',
'Kashima', 'Katsuragi', 'Kawakaze', 'Kazagumo', 'Kiyoshimo', 'Libeccio', 'Littorio', 'Maikaze', 'Maruyu', 'Mikuma',
'Mizuho', 'Musashi', 'Noshiro', 'Nowaki', 'Okinami', 'Ooyodo', 'Prinz Eugen', 'Roma', 'Sakawa', 'Taigei',
'Taihou', 'Takanami', 'Tanikaze', 'Teruzuki', 'Tokitsukaze', 'U-511', 'Umikaze', 'Unryuu', 'Uzuki', 'Yahagi',
'Yamato', 'Z1', 'Z3', 'Zara',
}
local ignored_ships = {
'Akatsuki', 'Akebono', 'Aoba', 'Arare', 'Arashio', 'Asashio', 'Ashigara', 'Ayanami', 'Chitose', 'Chiyoda',
'Choukai', 'Fubuki', 'Fumizuki', 'Furutaka', 'Haguro', 'Hatsuharu', 'Hatsushimo', 'Hatsuyuki', 'Hibiki', 'Houshou',
'I-168', 'Ikazuchi', 'Inazuma', 'Isonami', 'Isuzu', 'Jintsuu', 'Kagerou', 'Kako', 'Kasumi', 'Kikuzuki',
'Kisaragi', 'Kiso', 'Kuma', 'Kuroshio', 'Maya', 'Michishio', 'Mikazuki', 'Miyuki', 'Mochizuki', 'Murakumo',
'Murasame', 'Mutsuki', 'Nachi', 'Nagatsuki', 'Naka', 'Natori', 'Nenohi', 'Oboro', 'Ooshio', 'Samidare',
'Satsuki', 'Sazanami', 'Shigure', 'Shikinami', 'Shiranui', 'Shiratsuyu', 'Shirayuki', 'Suzukaze', 'Tama', 'Tatsuta',
'Tenryuu', 'Ushio', 'Wakaba', 'Yura', 'Yuudachi',
}
-- * Definitions for parser/formatter.
local args_grammar = {
node = '^%s*(%a)%s*$',
comma_list = '[^,]+',
ship_and_nodes = '^%s*(.-)%s*:%s*(.-)%s*$',
just_node = '^%s*(%a)%s*$',
node_and_diff = '^%s*(%a)%s*/%s*(%S-)%s*$',
node_diff_tooltip = '^%s*(%a)%s*/%s*(%S-)%s*{(.-)}%s*$',
-- TODO:
-- * Add battle ranks:
-- node_diff_rank = '^%s*(%a)%s*/%s*(%S-)%s*/%s*(%a)%s*$',
-- * Hide difficulty:
-- node_rank = '^%s*(%a)%s*//%s*(%a)%s*$',
-- * Tooltips with extra info?
}
local diff_colors = {
['Easy'] = 'B9F6CA',
['Medium'] = 'FFE0B2',
['Hard'] = 'FFCDD2',
['?'] = 'BBDEFB'
}
local diff_names = {
['Easy'] = 'Easy+',
['Medium'] = 'Medium+',
['Hard'] = 'Hard+',
['?'] = '?'
}
-- * parser/formatter.
function parseArgs(args)
local tbl = { nodes = {}, rows = {}, debug = '' }
function log(message, value)
tbl.debug = tbl.debug .. string.format('%s: %s\n', message, value)
end
if not args.nodes then
log('info', 'empty table')
return tbl
end
local boss_nodes = {}
for boss_node_ in string.gmatch(args.boss or "", args_grammar.comma_list) do
local boss_node = boss_node_:match(args_grammar.node)
if boss_node then
local boss_node = string.upper(boss_node)
if find(boss_nodes, boss_node) then
log('boss node duplicate', boss_node)
else
table.insert(boss_nodes, boss_node)
end
else
log('boss node syntax error', boss_node_)
end
end
if #boss_nodes == 0 then
log('warning', 'no boss node(s) specified')
end
for node_ in string.gmatch(args.nodes, args_grammar.comma_list) do
local node = node_:match(args_grammar.node)
if node then
local node = string.upper(node)
if find(tbl.nodes, node, 'name') then
log('node duplicate', node)
else
local is_boss = find(boss_nodes, node)
table.insert(tbl.nodes, { name = node, boss = is_boss })
end
else
log('node syntax error', node_)
end
end
for _, boss_node in pairs(boss_nodes) do
if not find(tbl.nodes, boss_node, 'name') then
log('boss node ignored', boss_node)
end
end
-- ship args
for arg_name, ship_and_nodes in pairs(args) do
if tonumber(arg_name) then
local ship, nodes = ship_and_nodes:match(args_grammar.ship_and_nodes)
if ship and nodes then
local ship_table = Ship:get_table(ship, '')
if ship_table and ship_table._type then
if find(tbl.rows, ship, 'ship') then
log('ship duplicate', ship)
elseif find(ignored_ships, ship) then
log('ship ignored', ship)
else
table.insert(tbl.rows, {
ship = ship,
rare = find(rare_ships, ship),
type = Formatting:format_ship_code(ship_table._type) or '?',
nodes = {}
})
local row = tbl.rows[#tbl.rows]
for _, node in pairs(tbl.nodes) do
row.nodes[node.name] = nil
end
for node_arg in string.gmatch(nodes, args_grammar.comma_list) do
local node, diff, tooltip = node_arg:match(args_grammar.node_diff_tooltip)
if not node then
node, diff = node_arg:match(args_grammar.node_and_diff)
end
if not node then
node = node_arg:match(args_grammar.just_node)
end
diff = diff_names[diff] and diff or '?'
if node and diff then
local node = string.upper(node)
if row.nodes[node] then
log('ship node duplicate', string.format('%s for %s', node, ship))
elseif not find(tbl.nodes, node, 'name') then
log('node ignored', string.format('%s for %s', node, ship))
else
row.nodes[node] = {
color = diff_colors[diff],
diff = diff_names[diff],
tooltip = tooltip,
}
end
else
log('ship node syntax error', string.format('%s for %s', node_arg, ship))
end
end
end
else
log('ship ignored', ship)
end
else
log('ship syntax error', ship_and_nodes)
end
end
end
return tbl
end
local table_format = {
header = '{| class="article-table sortable" align="center" width="100%" style="text-align:center;"\n!Type\n!<span style="border-bottom:1px dotted;">Ship<sup>[[Template:DropList/doc|?]]</sup></span>\n',
header_node = '!${node}\n',
header_boss_node = '!style="background-color:#FFCDD2;color:red;"|\'\'\'${node}\'\'\'\n',
row = '|- class="toggle-target-droplist-non-rare-ship" style="display:none;"\n',
rare_row = '|-\n',
type_cell = '|${type}\n',
-- TODO: japanese tooltips
ship_cell = '|[[${ship}]]\n',
rare_ship_cell = '|[[${ship}|<span style="color:red;">${ship}</span>]]\n',
node_cell = '|style="background-color:#${color};"|${diff}\n',
empty_cell = '|\n',
footer = '|}\n',
debugger = [[{| style="width:100%;" align="center" cellspacing="0" class="article-table mw-collapsible mw-collapsed"
!Script warnings
|-
|<pre>${debug}</pre>
|}]]
}
function showTable(tbl)
local res = table_format.header
function add(str)
res = res .. str
end
function add_row(row)
add(row.rare and table_format.rare_row or table_format.row)
add(format{table_format.type_cell, type = row.type})
add(format{
row.rare and table_format.rare_ship_cell or table_format.ship_cell,
ship = row.ship
})
for _, node in pairs(tbl.nodes) do
local node = row.nodes[node.name]
add(node and format{
table_format.node_cell, color = node.color, diff = node.tooltip and Formatting:tooltip(node.diff, node.tooltip) or node.diff
} or table_format.empty_cell)
end
end
-- header
for _, node in pairs(tbl.nodes) do
add(format{
node.boss and table_format.header_boss_node or table_format.header_node,
node = node.name
})
end
-- rows
for _, row in pairs(tbl.rows) do
-- TODO: Sort by type/name
if row.rare then
add_row(row)
end
end
for key, row in pairs(tbl.rows) do
-- TODO: Sort by type/name
if not row.rare then
add_row(row)
end
end
add(table_format.footer)
if tbl.debug ~= '' then
add(format{table_format.debugger, debug = tbl.debug})
end
return res
end
local DropList = {}
function DropList.show(frame, args_)
local args = args_ or getArgs{frame = frame:getParent()}
return showTable(parseArgs(args))
end
return DropList