Module:Ship infobox

From Corsair Cove Official Wiki
Revision as of 19:16, 7 August 2026 by Aeredor (talk | contribs) (Landlubber path banner gray and unlocked by default)

This module builds the ship infobox HTML. It is invoked from Template:Ship infobox.

Usage

Method invoked by template Argument from template Description of argument
main() name Canonical ship name, as seen in-game (e.g. Bedlam, Wrath Bringer).

Template Examples

With a named argument:

{{#invoke:Ship infobox|main|name=Bedlam}}

Or a positional argument:

{{#invoke:Ship infobox|main|Bedlam}}


API

main()
Entry point. Looks up the ship, builds the infobox, and returns HTML.

No other functions are exported.

Errors

  • Ship name is required — name or positional arg missing or empty.
  • Ship not found — name not in Module:Ships_data.

Dependencies

Templates called at runtime


--[[
Generates the infobox for Template:Ship infobox.

For testing on the debug console, copy and paste this line of code:
=p.main({args = {name = "Bedlam"}, expandTemplate = function (_, t) return mw.getCurrentFrame():expandTemplate(t) end})

]]

---@type ShipsDataModule
local ships_data = require("Module:Ships_data")

---@diagnostic disable: undefined-field
local resourceData = mw.loadData("Module:ResourceData")



local CSS_INFOBOX = "cc-infobox-ship"

local CSS_H = CSS_INFOBOX .. "-header"
local CSS_H_TIER = CSS_H .. "-tier"
local CSS_H_NAME = CSS_H .. "-name"
local CSS_H_CLASS = CSS_H .. "-class"
local CSS_H_ICON = CSS_H .. "-icon"
local CSS_H_DESCRIPTION = CSS_H .. "-description"

local function CSS_RECOLOR_PATH(path)
  if (not path) or (type(path) ~= "string") or (path == "") or (path == "None") then 
    return "cc-panel-recolor-gray"
  end
  return "cc-panel-recolor-" .. path:lower()
end

local CSS_S = CSS_INFOBOX .. "-stats"
local CSS_S_TITLE = CSS_S .. "-title"
local CSS_S_FULLROW = CSS_S .. "-fullrow"
local CSS_S_ROW = CSS_S .. "-row"
local CSS_S_ROW_COL = CSS_S_ROW .. "-col"

local CSS_STAT_MAX = "cc-ship-stat-max"

local CSS_W = CSS_INFOBOX .. "-crew"
local CSS_W_TITLE = CSS_W .. "-title"
local CSS_W_ROW = CSS_W .. "-row"
local CSS_W_ROW_COL = CSS_W_ROW .. "-col"
local CSS_W_ROW_COL_ADV = CSS_W_ROW_COL .. "-advanced"

local CSS_C = CSS_INFOBOX .. "-construction"
local CSS_C_TITLE = CSS_C .. "-title"
local CSS_C_PIER = CSS_C .. "-pier"
local CSS_C_TABLE = CSS_C .. "-table"
local CSS_C_TABLE_QTY = CSS_C_TABLE .. "-qty"

local CSS_U = CSS_INFOBOX .. "-unlock"
local CSS_U_TITLE = CSS_U .. "-title"
local CSS_U_REQ = CSS_U .. "-requirements"
local CSS_U_REQ_POINTS = CSS_U_REQ .. "-points"

local function CSS_U_REQ_POINTS_PATH(path)
  return CSS_U_REQ_POINTS .. "-" .. path:lower()
end



--#region Private helper functions

--- Retrieves the specified value from the given stats array.
---@param stats ShipStatAmount[]
---@param stat_name string
---@return number
local function get_stat_value(stats, stat_name)
  for _, pair in ipairs(stats) do
    if pair.stat == stat_name then
      return pair.qty
    end
  end
  return 0
end

---@alias ExpandTemplateFunction fun(title: string, args: table): string

--- Hides dependency on the frame object from template expansion.
---@return ExpandTemplateFunction
local function make_template_expander(frame)
  return function(title, args)
    return frame:expandTemplate({ title = title, args = args })
  end
end



--#region Private helper functions for header section

--- Builds the header section.
---@param ship ShipRecord
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_header_section(ship, callTemplate)
  local tier_icon = callTemplate("Ship tier icon", { ship.tier, path = ship.principle_path, size = "md" })
  local header_section = mw.html.create("div"):addClass(CSS_H)
  header_section
      :tag("div"):addClass(CSS_H_TIER)
          :wikitext(tier_icon)
      :done()
      :tag("div"):addClass(CSS_H_NAME)
          :tag("span")
              :wikitext(ship.name)
          :done()
      :done()
      :tag("div"):addClass(CSS_H_CLASS):addClass(CSS_RECOLOR_PATH(ship.principle_path))
          :tag("span")
              :wikitext(ship.class .. " Tier " .. ship.tier)
          :done()
      :done()
      :tag("div"):addClass(CSS_H_ICON)
          :wikitext("[[File:" .. ship.icon_filename .. "|x128px]]")
      :done()
      :tag("div"):addClass(CSS_H_DESCRIPTION)
          :wikitext(ship.description)
      :done()
  return header_section
end

--#endregion



--#region Private helper functions for stats section

--- Builds one full-row stat with name and base value (only).
---@param stats ShipStatAmount[]
---@param stat_name string
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_stats_fullrow(stats, stat_name, callTemplate)
  local stat_fullrow = mw.html.create("div"):addClass(CSS_S_FULLROW)
  stat_fullrow
      :tag("div"):addClass(CSS_S_ROW_COL)
          :wikitext(callTemplate(stat_name, { size = "sm" }))
      :done()
      :tag("div")
          :wikitext(tostring(get_stat_value(stats, stat_name)))
      :done()
  return stat_fullrow
end

--- Builds one stat column with icon, base, and max value.
---@param stats_base ShipStatAmount[]
---@param stats_max ShipStatAmount[]
---@param stat_name string
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_stats_column(stats_base, stats_max, stat_name, callTemplate)
  local stat_column = mw.html.create("div"):addClass(CSS_S_ROW_COL)
  stat_column
      :tag("div")
          :wikitext(callTemplate(stat_name .. " icon", { size = "sm" }))
      :done()
      :tag("div")
          :wikitext(tostring(get_stat_value(stats_base, stat_name)))
      :done()
      :tag("div"):addClass(CSS_STAT_MAX)
          :wikitext(tostring(get_stat_value(stats_max, stat_name)))
      :done()
  return stat_column
end

--- Builds the stats section.
---@param ship ShipRecord
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_stats_section(ship, callTemplate)
  local stats_section = mw.html.create("div"):addClass(CSS_S)
  stats_section
      :tag("div"):addClass(CSS_S_TITLE)
          :wikitext("Stats")
      :done()
      :tag("hr")
      :done()
      :node(build_stats_fullrow(ship.stats_base, "Health", callTemplate))
      :node(build_stats_fullrow(ship.stats_base, "Crew", callTemplate))
      :node(build_stats_fullrow(ship.stats_base, "Supplies", callTemplate))
      :tag("div"):addClass(CSS_S_ROW)
          :tag("div"):addClass(CSS_S_ROW_COL)
              :tag("div")
                  :wikitext("Base")
              :done()
              :tag("div"):addClass(CSS_STAT_MAX)
                  :wikitext("Max")
              :done()
          :done()
          :node(build_stats_column(ship.stats_base, ship.stats_max, "Gun damage", callTemplate))
          :node(build_stats_column(ship.stats_base, ship.stats_max, "Boarding damage", callTemplate))
          :node(build_stats_column(ship.stats_base, ship.stats_max, "Speed", callTemplate))
          :node(build_stats_column(ship.stats_base, ship.stats_max, "Manoeuvrability", callTemplate))
          :node(build_stats_column(ship.stats_base, ship.stats_max, "Armour", callTemplate))
      :done()
  return stats_section
end

--#endregion



--#region Private helper functions for crew section

--- Builds one crew column with label, icon, and quantity.
---@param crew_type string
---@param qty number
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_crew_column(crew_type, qty, callTemplate)
  local is_crew_advanced = {
    ["Swabbies"] = false,
    ["Scallywags"] = true,
    ["Picaroons"] = true,
    ["Bombardiers"] = true,
    ["Crow's Eyes"] = true,
    ["Buccaneers"] = true,
    ["Navigators"] = true,
    ["Renegades"] = true,
    ["Marauders"] = true,
    ["Corsairs"] = true,
  }
  local column = mw.html.create("div"):addClass(CSS_W_ROW_COL)
  column
      :tag("div")
          :wikitext(crew_type)
      :done()
      :tag("div"):addClass()
          :wikitext(callTemplate("Pirate icon", { crew_type, size = "md" } ))
      :done()
      :tag("div")
          :wikitext(tostring(qty))
      :done()
  return column
end

--- Builds the crew section.
---@param ship ShipRecord
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_crew_section(ship, callTemplate)
  local crew_section = mw.html.create("div"):addClass(CSS_W)
  crew_section
      :tag("div"):addClass(CSS_W_TITLE)
          :wikitext("Crew")
      :done()
      :tag("hr")
      :done()
  local crew_row = mw.html.create("div"):addClass(CSS_W_ROW)
  for _, crew in ipairs(ship.crew_types) do
    crew_row:node(build_crew_column(crew.type, crew.qty, callTemplate))
  end
  crew_section:node(crew_row)
  return crew_section
end

--#endregion



--#region Private helper functions for construction section

--- Appends the construction pier wikitext.
---@param pier string
---@param callTemplate ExpandTemplateFunction
---@return string
local function write_construction_pier_wikitext(pier, callTemplate)
  local pier_icons = {
    ["Shallow Pier"] = "T ICO Pier lvl 1.png",
    ["Trade Pier"] = "T ICO Pier lvl 2.png",
    ["Deep-water Pier"] = "T ICO Pier lvl 3.png",
  }
  return "[[File:" .. pier_icons[pier] .. "|x32px|link=" .. pier .. "|" .. pier .. "]] [[" .. pier .. "]]"
end

--- Appends qty, icon, and name cells for one construction cost to a table row.
---@param row HtmlNode
---@param resource string
---@param qty number
---@param callTemplate ExpandTemplateFunction
local function append_construction_cost(row, resource, qty, callTemplate)
  local icon_file = resourceData[resource]["icon_file"]
  row
      :tag("td"):addClass(CSS_C_TABLE_QTY)
          :tag("span")
              :wikitext(tostring(qty))
          :done()
      :done()
      :tag("td")
          :tag("span")
              :wikitext("[[File:" .. icon_file .. "|x32px|link=" .. resource .. "|" .. resource .. "]]")
          :done()
      :done()
      :tag("td")
          :tag("span")
              :wikitext(resource)
          :done()
      :done()
end

--- Builds the construction table.
---@param construction_costs ShipResourceAmount[]
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_construction_table(construction_costs, callTemplate)
  local construction_table = mw.html.create("table")
  local row
  for index, cost in ipairs(construction_costs) do
    if index % 2 == 1 then
      row = mw.html.create("tr")
    end
    if index % 2 == 0 then
      row:tag("td"):attr("style", "min-width: 16px;"):wikitext(" "):done()
    end
    append_construction_cost(row, cost.resource, cost.qty, callTemplate)
    if index % 2 == 0 or index == #construction_costs then
      construction_table:node(row)
    end
  end
  return construction_table
end

--- Builds the construction section.
---@param ship ShipRecord
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_construction_section(ship, callTemplate)
  local construction_section = mw.html.create("div"):addClass(CSS_C)
  construction_section
      :tag("div"):addClass(CSS_C_TITLE)
          :wikitext("Construction")
      :done()
      :tag("hr")
      :done()
      :tag("div"):addClass(CSS_C_PIER)
          :wikitext(write_construction_pier_wikitext(ship.pier, callTemplate))
      :done()
      :tag("div"):addClass(CSS_C_TABLE)
          :node(build_construction_table(ship.construction_costs, callTemplate))
      :done()
  return construction_section
end

--#endregion



--#region Private helper functions for unlock section

--- Builds one unlock points block with icon, color block, and quantity.
---@param path string
---@param qty number
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function append_points_block(path, qty, callTemplate)
  local path_icons = {
    Notoriety = "T_ICO_VEC_P_Notoriety1.png",
    Empire = "T_ICO_VEC_P_Empire2.png",
    Seafaring = "T_ICO_VEC_P_Sea.png",
    Wealth = "T_ICO_VEC_P_Wealth.png",
  }
  local block = mw.html.create("div"):addClass(CSS_U_REQ_POINTS_PATH(path))
      :wikitext("[[File:" .. path_icons[path] .. "|x24px|link=" .. path .. "|" .. path .. "]]")
      :tag("span")
          :wikitext(tostring(qty))
      :done()
  return block
end

--- Builds the unlock points blocks.
---@param unlock_points ShipUnlockAmount[]
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_unlock_points_block(unlock_points, callTemplate)
  local row = mw.html.create("div"):addClass(CSS_U_REQ_POINTS)
  for _, point in ipairs(unlock_points) do
    row:node(append_points_block(point.path, point.qty, callTemplate))
  end
  return row
end

--- Builds the unlock section.
---@param ship ShipRecord
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_unlock_section(ship, callTemplate)
  local unlock_section = mw.html.create("div"):addClass(CSS_U)
  if #ship.unlock_points == 0 then
    unlock_section
        :tag("div"):addClass(CSS_U_TITLE)
            :wikitext("Unlock")
        :done()
        :tag("hr")
        :done()
        :tag("div"):addClass(CSS_U_REQ)
            :tag("div")
                :wikitext("Unlocked by building first pier.")
            :done()
        :done()
  else
    unlock_section
        :tag("div"):addClass(CSS_U_TITLE)
            :wikitext("Unlock")
        :done()
        :tag("hr")
        :done()
        :tag("div"):addClass(CSS_U_REQ)
            :tag("div")
                :wikitext("Spend points:")
            :done()
            :node(build_unlock_points_block(ship.unlock_points, callTemplate))
        :done()
  end
  return unlock_section
end

--#endregion



--- Orchestrates the infobox.
---@param ship ShipRecord
---@param callTemplate ExpandTemplateFunction
---@return HtmlNode
local function build_infobox_for_ship(ship, callTemplate)
  local html = mw.html.create("div"):addClass("cc-infobox-ship")
  html:node(build_header_section(ship, callTemplate))
  html:node(build_stats_section(ship, callTemplate))
  html:node(build_crew_section(ship, callTemplate))
  html:node(build_construction_section(ship, callTemplate))
  html:node(build_unlock_section(ship, callTemplate))
  return html
end

--#endregion



--#region Infobox main

local p = {}

--- Entry point for {{#invoke:Ship infobox|main|name=...}}.
function p.main(frame)
  local args = frame.args
  local name = args.name or args[1]
  if name == "" then
    return "'''Ship name is required'''"
  end
  local ship = ships_data.get_ship_by_name(name)
  if not ship then
    return "'''Ship not found'''"
  end
  local callTemplate = make_template_expander(frame)
  local html = build_infobox_for_ship(ship, callTemplate)
  return html
end


return p

--#endregion