Module:NodeInfo

local U = require("Module:Utils") local format = require('Module:Utils').format local BaseTable = require("Module:BaseTable") local EnemyShip = require("Module:EnemyShip") local Formatting = require('Module:Formatting') local ShipBattleCardKai = require("Module:ShipBattleCardKai") local NodeInfo = BaseTable({   _item_class = EnemyShip,    _table_start = {| class="wikitable typography-xl-optout" style="width:690px",    _header_template = !,    _header_template_simple = ${node_type},	_header_template_basexp = !,    _column_cell_templates = {        node = ${values.node},        formation = ${values.formation},        fleet = ${values.fleet},        as = ${values.as},    },    _column_cell_templates_basexp = { node = ${values.node}, formation = ${values.formation}, basexp = ${values.basexp}, fleet = ${values.fleet}, as = ${values.as}, },   _empty_node_template = ${values.node}    | colspan="3" style="text-align: center;" |Must be my imagination (battle avoided)/No enemies sighted 気のせいだった（戦闘回避）/敵影を見ず. (戦闘なし) , _selection_node_template = ${values.node}   | colspan="3" style="text-align: center;" |You may choose which direction your fleet will go. Admiral, which path will you choose? 艦隊針路選択可能！/艦隊の針路を選択できます. 提督、どちらの針路をとられますか？ , _resource_node_template = ${values.node}   | colspan="3" style="text-align: center; background-color: ${values.bg_color}; color: ${values.color};" |${values.text}, _collapser_template = ${button_display}    , _collapser_end = , _columns = { "node", "formation", "fleet", "as", },   _columns_basexp = { "node", "formation", "basexp", "fleet", "as", },   _day_battle_color = "gold", _night_battle_color = "blue", _night_battle_bg_color = "lightblue", -- #BBDEFB _aerial_battle_bg_color = "lightgreen", -- #C8E6C9 _defense_battle_bg_color = "#81C784", _raid_battle_bg_color = "#81C784", _boss_battle_color = "red", _clear_battle_color = "blue", --_resource_node_bg_color = "lightgreen", _resource_node_bg_color = "initial", --_maelstrom_node_bg_color = "pink", _maelstrom_node_bg_color = "initial", }) function NodeInfo:node(row)   local color, bg_color = "initial", "initial"    if row.tags.boss then        color = self._boss_battle_color    end    if row.tags.aerial then        bg_color = self._aerial_battle_bg_color    end    if row.tags.defense then        bg_color = self._defense_battle_bg_color    end    if row.tags.dogfight then        bg_color = self._defense_battle_bg_color    end    if row.tags.raid then        bg_color = self._raid_battle_bg_color    end    if row.tags.night then        bg_color = self._night_battle_bg_color    end    if row.tags.nighttoday then        bg_color = self._night_battle_bg_color    end    return { values = { node = Formatting:japanese_text(row.node) }, color = color, bg_color = bg_color } end function NodeInfo:formation(row)    if row.tags.final then        row.formation = row.formation .. " (Final) "    end    if row.tags.clear then        row.formation = row.formation .. " (Clear) " end local color = "initial" if row.tags.boss then color = self._boss_battle_color end return { values = { formation = row.formation }, color = color } end function NodeInfo:basexp(row) color = "initial" if row.tags.boss then color = self._boss_battle_color end return { values = { basexp = row.basexp }, color = color } end function NodeInfo:fleet(row) return { values = { fleet = row.fleet } } end function NodeInfo:as(row) color = "initial" if row.tags.boss then color = self._boss_battle_color end return { values = { as = row.as }, color = color } end function NodeInfo:upcase(str) str = str:gsub("(%s)(%l)", function(a, b) return a .. string.upper(b) end) str = str:gsub("^(%l)", function(a) return string.upper(a) end) return str end function NodeInfo:insert_item(node, formation, basexp, fleet, as, tags, as_complete) --[=[   -- Can give weird results when major contributors to air power are unknown -- : use tooltips with explanations instead local air_parity = (as_complete or as > 0) and string.format("%.1d", math.ceil((2./3.) * as)) or "??" local air_superiority = (as_complete or as > 0) and string.format("%.1d", math.ceil(as * (3 / 2))) or "??" local air_supremacy = (as_complete or as > 0) and tostring(as * 3) or "??" local air_string = not as_complete and as > 0 and (air_parity .. "+/" .. air_superiority .. "+/" .. air_supremacy .. "+") or (air_parity .. "/" .. air_superiority .. "/" .. air_supremacy) --]=]   local air_denial_string = "?" local air_parity_string = "?" local air_superiority_string = "?" local air_supremacy_string = "?" if as_complete then local air_denial = as > 0 and math.floor(as / 3 + 1) or 0 local air_parity = as > 0 and math.floor(as * 2 / 3 + 1) or 0 local air_superiority = math.ceil(as * 3 / 2) local air_supremacy = as * 3 air_denial_string = string.format("%.1d", air_denial) air_parity_string = string.format("%.1d", air_parity) air_superiority_string = string.format("%.1d", air_superiority) air_supremacy_string = string.format("%.1d", air_supremacy) end local air_string = air_denial_string .. "/" .. air_parity_string .. " " .. air_superiority_string .. "/" .. air_supremacy_string if basexp == 0 then table.insert(self._items, {			node = node,			formation = formation,			fleet = fleet,			as = air_string,			tags = tags,		}) else table.insert(self._items, {			node = node,			formation = formation,			basexp = basexp,			fleet = fleet,			as = air_string,			tags = tags,		}) end end function NodeInfo:create_items --Modes are as follows: --1 = Node --2 = Tag processing --3 = Resource type --4 = Amount of resources --5 = Formation --6 = Base node exp --7 = Fleet building local mode = 1 local node, formation, basexp = nil, nil, 0 local fleet = {} local as_rating, as_complete = 0, true local tags = {} local resource for index, item_key in ipairs(self._args) do       if item_key == "-" then if mode == 7 then --We're at a break and have built a full row; time to insert it               self:insert_item(node, formation, basexp, table.concat(fleet, " "), as_rating, tags, as_complete) end table.insert(self._items, "break") fleet, as_rating, as_complete = {}, 0, true tags = {} mode = 1 else if mode == 1 then --First item should always be the node node = item_key mode = 2 elseif mode == 2 then self._node_type = string.lower(string.match(item_key, "(.-)/") or item_key) if mw.ustring.find(string.lower(item_key), "resource") or string.lower(item_key) == "storm" then local split = mw.ustring.find(item_key, '/') if split then tags[string.lower(mw.ustring.sub(item_key, 1, split - 1))] = true item_key = mw.ustring.sub(item_key, split + 1) end tags[string.lower(item_key)] = true mode = 3 elseif string.lower(item_key) == "empty" then table.insert(self._items, node .. "/empty") mode = 1 elseif string.lower(item_key) == "select" then table.insert(self._items, node .. "/select") mode = 1 else while mw.ustring.find(item_key, '/') do		               local split = mw.ustring.find(item_key, '/') tags[string.lower(mw.ustring.sub(item_key, 1, split - 1))] = true item_key = mw.ustring.sub(item_key, split + 1) end tags[string.lower(item_key)] = true if tags.basexp then -- used to communicate the need to account for node xp while building rows\ self._is_basexp = true end mode = 5 end elseif mode == 3 then resource = self:upcase(item_key) mode = 4 elseif mode == 4 then local amount = item_key if tags["storm"] and mw.ustring.sub(amount, 1, 1) ~= "-" then amount = "-" .. amount end local boss = tags["boss"] and "true" or "false" local string = node .. "/" .. resource .. "/" .. amount .. "/" .. boss table.insert(self._items, string) mode = 1 elseif mode == 5 then formation = self:upcase(item_key) mode = 6 else -- Only add basexp to the list of values if included as a tag if mode == 6 and tags.basexp then basexp = item_key mode = 7 else --Fleets are of variable size, so we append onto a string until we hit the next node declaration local split = mw.ustring.find(item_key, '/') local ship_name, ship_suffix if split then ship_name = mw.ustring.sub(item_key, 1, split - 1) ship_suffix = mw.ustring.sub(item_key, split + 1) else ship_name = item_key ship_suffix = "" end local ship = EnemyShip(ship_name, ship_suffix) local ship_air_power = ship:air_power(tags.raid) local ship_caption = (ship:name or "?") .. " (" .. Formatting:format_enemy_stat(ship:api_id) .. "): " .. (ship:armor or "?") .. " Armor, " .. (ship:hp or "?") .. " HP" .. (ship_air_power ~= 0 and ", " .. (ship_air_power or "?") .. " AP" or "") table.insert(fleet, ShipBattleCardKai:get{		               ship = ship,		                caption = ship_caption,		                link = ship:link,		                flagship = #fleet == 0,		                hd = true,		                size = '160px'		            }) if ship_air_power then as_rating = as_rating + ship_air_power else as_complete = false end if mode == 6 then mode = 7 end end end end end if mode == 7 then self:insert_item(node, formation, basexp, table.concat(fleet, " "), as_rating, tags, as_complete) end end function NodeInfo:create_data_rows for index, item in ipairs(self._items) do		local row_values if type(item) == "string" then row_values = item else local columnuse = nil if item.tags.basexp then columnuse = self._columns_basexp else columnuse = self._columns end row_values = {} for _, column in ipairs(columnuse) do		       row_values[column] = self[column](self, item) end if index > 1 then for _, column in ipairs(columnuse) do		           for i = index - 1, 1, -1 do		                if column == "node" then local previous_cell = self._data_rows[i][column] if previous_cell then if row_values[column].values.node == previous_cell.values.node then previous_cell.rowspan = previous_cell.rowspan and previous_cell.rowspan + 1 or 2 row_values[column] = nil else row_values[column].rowspan = 1 row_values[column].colspan = 1 end break end end end end else for _, column in ipairs(columnuse) do		           row_values[column].rowspan = 1 row_values[column].colspan = 1 end end end table.insert(self._data_rows, row_values) end end function NodeInfo:format_node_type local node_types = { normal = 'Normal Battle Node', boss = 'Boss Battle Node', resource = 'Resource Node', storm = 'Maelstrom Node', empty = 'Empty Node', select = 'Selection Node', night = 'Night Battle Node', aerial = 'Aerial Battle Node', defense = 'Air Defense Node', nighttoday = 'Night to Day Battle Node', raid = 'Air Raids', }   return self._args["comment"] or node_types[self._node_type] or "Fleet" end function NodeInfo:is_simple_node_type return self._node_type == 'resource' or self._node_type == 'storm' or self._node_type == 'empty' or self._node_type == 'select' end function NodeInfo:create_header local header_string = nil if self:is_simple_node_type then header_string = format{ self._header_template_simple, node_type = self:format_node_type }	elseif self._is_basexp then header_string = format{ self._header_template_basexp, node_type = self:format_node_type }	else header_string = format{ self._header_template, node_type = self:format_node_type }	end self._header = header_string self._header_bottom = header_string end function NodeInfo:start_rows self._rows = {} if self._args["toggle_id"] then table.insert(self._rows, format{self._collapser_template,           toggle_id = self._args["toggle_id"],            button_display = self._args["button_display"] or "Show/Hide Formation Table",        }) end table.insert(self._rows, self._table_start) table.insert(self._rows, self._header) end function NodeInfo:process_resource_node(resource, amount) --Amount may or may not be just numbers local action, units, node_type, bg_color = "Gained", "", "Resource", self._resource_node_bg_color if mw.ustring.sub(amount, 1, 1) == "-" then action = "Lost" amount = mw.ustring.sub(amount, 2) node_type = "Storm" bg_color = self._maelstrom_node_bg_color end if mw.ustring.find(amount, " ") then local split = mw.ustring.find(amount, " ") units = mw.ustring.sub(amount, split + 1) amount = mw.ustring.sub(amount, 1, split - 1) end local text = action .. " " .. amount .. " " .. resource .. " " .. units return text, node_type, bg_color end

function NodeInfo:build_rows local bg_color for index, row_values in ipairs(self._data_rows) do       if row_values ~= "break" then table.insert(self._rows, self._row_starter) if row_values == "header" then table.insert(self._rows, self._header) elseif type(row_values) == "table" then if row_values["node"] then bg_color = row_values["node"].bg_color elseif bg_color == nil then bg_color = "initial" end if self._is_basexp then for _, column in ipairs(self._columns_basexp) do			           if row_values[column] then row_values[column].bg_color = bg_color end if row_values[column] then table.insert(self._rows, format(self._column_cell_templates_basexp[column] or self._cell, row_values[column])) end end else for _, column in ipairs(self._columns) do			           if row_values[column] then row_values[column].bg_color = bg_color end if row_values[column] then table.insert(self._rows, format(self._column_cell_templates[column] or self._cell, row_values[column])) end end end elseif mw.ustring.find(row_values, '/') then --node/resource/amount/boss local values = {} while mw.ustring.find(row_values, '/') do   	            local split = mw.ustring.find(row_values, '/') if split then table.insert(values, mw.ustring.sub(row_values, 1, split - 1)) row_values = mw.ustring.sub(row_values, split + 1) end end table.insert(values, row_values) if values[2] == "empty" then table.insert(self._rows, format{self._empty_node_template, values = { node = Formatting:japanese_text(values[1]) } }) elseif values[2] == "select" then table.insert(self._rows, format{self._selection_node_template, values = { node = Formatting:japanese_text(values[1]) } }) else local resource = Formatting:format_image{values[2] .. ".png", caption = self:upcase(values[2]), size = "22x22px"} local text, node_type, bg_color = self:process_resource_node(resource, values[3]) local color = "initial" if values[4] == "true" then color = self._boss_battle_color end table.insert(self._rows, format{self._resource_node_template, values = {   	                node = Formatting:japanese_text(values[1]),    	                text = text,    	                color = color,    	                bg_color = bg_color,    	            }}) end end end end end function NodeInfo:finish_rows table.insert(self._rows, self._row_starter) table.insert(self._rows, self._header_bottom or self._header) table.insert(self._rows, self._table_end) if self._args["toggle_id"] then table.insert(self._rows, self._collapser_end) end end U.registerTableTests(NodeInfo, {   { "A", "Normal/Clear", "Line Ahead", "Northern Princess" } }) -- p.run_table_tests return NodeInfo