• Welcome to the Kancolle Wiki!
  • If you have any questions regarding site content, account registration, etc., please visit the KanColle Wiki Discord

Difference between revisions of "Module:NodeInfo"

From Kancolle Wiki
Jump to navigation Jump to search
(Attempt to hide the node xp column when the node does not have variable experience.)
(Undo revision 1023616 by Alephnaught27 (talk))
Line 11: Line 11:
 
     _header_template = [[!#
 
     _header_template = [[!#
 
     !Formation
 
     !Formation
 +
!Base EXP
 
     !${node_type}
 
     !${node_type}
 
     !AD/AP<br>AS/AS+]],
 
     !AD/AP<br>AS/AS+]],
 
     _header_template_simple = [[!#
 
     _header_template_simple = [[!#
 
     !colspan="3"|${node_type}]],
 
     !colspan="3"|${node_type}]],
_header_template_basexp = [[!#
 
    !Enemy<br>Form.
 
!Base EXP
 
    !${node_type}
 
    !AD/AP<br>AS/AS+]],
 
 
     _column_cell_templates = {
 
     _column_cell_templates = {
 
         node = [[| colspan="${colspan}" rowspan="${rowspan}" style="text-align: center; color: ${color}; background-color: ${bg_color};" |${values.node}]],
 
         node = [[| colspan="${colspan}" rowspan="${rowspan}" style="text-align: center; color: ${color}; background-color: ${bg_color};" |${values.node}]],
Line 92: Line 88:
 
     return { values = { formation = row.formation }, color = color }
 
     return { values = { formation = row.formation }, color = color }
 
end
 
end
+
 
 
function NodeInfo:basexp(row)
 
function NodeInfo:basexp(row)
 
return { values = { basexp = row.basexp } }
 
return { values = { basexp = row.basexp } }
Line 140: Line 136:
 
     end
 
     end
 
     local air_string = air_denial_string .. "/" .. air_parity_string .. "<br>" .. air_superiority_string .. "/" .. air_supremacy_string
 
     local air_string = air_denial_string .. "/" .. air_parity_string .. "<br>" .. air_superiority_string .. "/" .. air_supremacy_string
if self._is_basexp then
+
    table.insert(self._items, {
table.insert(self._items, {
+
        node = node,
node = node,
+
        formation = formation,
formation = formation,
+
basexp = basexp,
basexp = basexp,
+
        fleet = fleet,
fleet = fleet,
+
        as = air_string,
as = air_string,
+
        tags = tags,
tags = tags,
+
    })
})
 
else
 
table.insert(self._items, {
 
node = node,
 
formation = formation,
 
fleet = fleet,
 
as = air_string,
 
tags = tags,
 
})
 
end
 
 
end
 
end
 
   
 
   
Line 177: Line 163:
 
     local tags = {}
 
     local tags = {}
 
     local resource
 
     local resource
self._is_basexp = false
 
 
 
for index, item_key in ipairs(self._args) do
 
for index, item_key in ipairs(self._args) do
 
if item_key == "-" then
 
if item_key == "-" then
Line 196: Line 180:
 
    mode = 2
 
    mode = 2
 
elseif mode == 2 then
 
elseif mode == 2 then
-- underscores should be exclusive to modifier specification during mode 2
+
    self._node_type = string.lower(string.match(item_key, "(.-)/") or item_key)
if mw.ustring.find(item_key, '_') then
 
-- capture modifiers here, then trim them off the node type
 
if mw.ustring.find(item_key, 'BaseEXP') then
 
self._is_basexp = true
 
end
 
local nodetypesolo = mw.ustring.sub(item_key, 1, mw.ustring.find(item_key, '_') - 1)
 
self._node_type = string.lower(string.match(nodetypesolo, "(.-)/") or nodetypesolo)
 
item_key = nodetypesolo;
 
else
 
self._node_type = string.lower(string.match(item_key, "(.-)/") or item_key)
 
end
 
 
    if mw.ustring.find(string.lower(item_key), "resource") or string.lower(item_key) == "storm" then
 
    if mw.ustring.find(string.lower(item_key), "resource") or string.lower(item_key) == "storm" then
 
        local split = mw.ustring.find(item_key, '/')
 
        local split = mw.ustring.find(item_key, '/')
Line 225: Line 198:
 
        while mw.ustring.find(item_key, '/') do
 
        while mw.ustring.find(item_key, '/') do
 
            local split = mw.ustring.find(item_key, '/')
 
            local split = mw.ustring.find(item_key, '/')
tags[string.lower(mw.ustring.sub(item_key, 1, split - 1))] = true
+
            tags[string.lower(mw.ustring.sub(item_key, 1, split - 1))] = true
 
            item_key = mw.ustring.sub(item_key, split + 1)
 
            item_key = mw.ustring.sub(item_key, split + 1)
 
        end
 
        end
Line 247: Line 220:
 
        mode = 6
 
        mode = 6
 
    else
 
    else
if mode == 6 and self._is_basexp then
+
if mode == 6 and tonumber(item_key) then
 
basexp = item_key
 
basexp = item_key
 
else
 
else
Line 260: Line 233:
 
ship_suffix = ""
 
ship_suffix = ""
 
end
 
end
+
 
 
local ship = EnemyShip(ship_name, ship_suffix)
 
local ship = EnemyShip(ship_name, ship_suffix)
 
local ship_air_power = ship:air_power(tags.raid)
 
local ship_air_power = ship:air_power(tags.raid)
+
 
 
local ship_caption =
 
local ship_caption =
 
(ship:name() or "?")
 
(ship:name() or "?")
Line 269: Line 242:
 
.. (ship:armor() or "?") .. " Armor, " .. (ship:hp() or "?") .. " HP"
 
.. (ship:armor() or "?") .. " Armor, " .. (ship:hp() or "?") .. " HP"
 
.. (ship_air_power ~= 0 and ", " .. (ship_air_power or "?") .. " AP" or "")
 
.. (ship_air_power ~= 0 and ", " .. (ship_air_power or "?") .. " AP" or "")
+
 
 
table.insert(fleet, ShipBattleCardKai:get{
 
table.insert(fleet, ShipBattleCardKai:get{
 
ship = ship,
 
ship = ship,
Line 276: Line 249:
 
flagship = #fleet == 0
 
flagship = #fleet == 0
 
})
 
})
+
 
 
if ship_air_power then
 
if ship_air_power then
 
as_rating = as_rating + ship_air_power
 
as_rating = as_rating + ship_air_power
Line 353: Line 326:
 
function NodeInfo:is_simple_node_type()
 
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'
 
     return self._node_type == 'resource' or self._node_type == 'storm' or self._node_type == 'empty' or self._node_type == 'select'
end
 
 
function NodeInfo:is_basexp()
 
return self._is_basexp
 
 
end
 
end
 
   
 
   
 
function NodeInfo:create_header()
 
function NodeInfo:create_header()
local header_string = nil
+
    local header_string = format{
if self:is_simple_node_type() then
+
        self:is_simple_node_type() and self._header_template_simple or self._header_template,
header_string = format{
+
        node_type = self:format_node_type()
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 = header_string
 
     self._header_bottom = header_string
 
     self._header_bottom = header_string

Revision as of 21:35, 30 June 2018

Documentation for this module may be created at Module:NodeInfo/doc

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:680px"]],
    _header_template = [[!#
    !Formation
	!Base EXP
    !${node_type}
    !AD/AP<br>AS/AS+]],
    _header_template_simple = [[!#
    !colspan="3"|${node_type}]],
    _column_cell_templates = {
        node = [[| colspan="${colspan}" rowspan="${rowspan}" style="text-align: center; color: ${color}; background-color: ${bg_color};" |${values.node}]],
        formation = [[| style="text-align: center; background-color: ${bg_color}; color: ${color};" |${values.formation}]],
        basexp = [[| style="text-align: center; background-color: ${bg_color}; color: ${color};" |${values.basexp}]],
		fleet = [[| style="width: 490px; background-color: ${bg_color};" |${values.fleet}]],
        as = [[| style="text-align: center; background-color: ${bg_color}; color: ${color};" |${values.as}]],
    },
    _empty_node_template = [[| style="text-align: center;" |${values.node}
    | colspan="3" style="text-align: center;" |Must be my imagination (battle avoided)/No enemies sighted<br /><span lang="ja">気のせいだった(戦闘回避)/敵影を見ず。(戦闘なし)</span>]],
    _selection_node_template = [[|style="text-align: center;" |${values.node}
    | colspan="3" style="text-align: center;" |You may choose which direction your fleet will go. Admiral, which path will you choose?<br /><span lang="ja">艦隊針路選択可能!/艦隊の針路を選択できます。提督、どちらの針路をとられますか?</span>]],
    _resource_node_template = [[| style="text-align: center; background-color: ${values.bg_color}; color: ${values.color};" |${values.node}
    | colspan="3" style="text-align: center; background-color: ${values.bg_color}; color: ${values.color};" |${values.text}]],
    _collapser_template = [[<div class="mw-customtoggle-${toggle_id} wikia-menu-button">${button_display}</div>
    <div class="mw-collapsible mw-collapsed" id="mw-customcollapsible-${toggle_id}">]],
    _collapser_end = [[</div>]],
    _columns = {
        "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",
    --_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 .. "<br />(Final)"
    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)
	return { values = { basexp = row.basexp } }
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
    -- [[Category:Todo]] : 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 .. "<br>" .. air_superiority_string .. "/" .. air_supremacy_string
    table.insert(self._items, {
        node = node,
        formation = formation,
		basexp = basexp,
        fleet = fleet,
        as = air_string,
        tags = tags,
    })
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
			        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
				if mode == 6 and tonumber(item_key) then
					basexp = item_key
				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
					})

					if ship_air_power then
						as_rating = as_rating + ship_air_power
					else
						as_complete = false
					end
					if mode == 6 then
						-- only check for the base node experience once
						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
			row_values = {}
			for _, column in ipairs(self._columns) do
				row_values[column] = self[column](self, item)
			end
			if index > 1 then
			    for _, column in ipairs(self._columns) 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(self._columns) 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 = format{
        self:is_simple_node_type() and self._header_template_simple or self._header_template,
        node_type = self:format_node_type()
    }
    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
				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
    	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", "Boss", "Line Ahead", "Northern Princess" }
})
-- p.run_table_tests()
 
return NodeInfo