Module:ShipCapabilities

local BaseData = require("Module:BaseData")

local format = require('Module:StringInterpolation').format

local ShipCapabilities = BaseData{ -- _anti_air_cut_in_types = { -- 	{	-- 		name = "Akizuki (HA HA R)", -- 		ship = {"Akizuki"}, -- 		equipment = {"High-angle Gun", "High-angle Gun", "Radar"}, -- 	},	-- 	{	-- 		name = "Akizuki (HA AAR)", -- 		ship = {"Akizuki"}, -- 		equipment = {"High-angle Gun", "Radar"}, -- 	},	-- 	{	-- 		name = "Akizuki (HA HA)", -- 		ship = {"Akizuki"}, -- 		equipment = {"High-angle Gun", "High-angle Gun"} -- 	},	-- 	{	-- 		name = "Type 3 AAR", -- 		equipment = {"Large Caliber Main Gun", "Type 3 Shell", "Anti-Aircraft Fire Director", "Anti-Air Radar"}, -- 	},	-- 	{	-- 		name = "HAFD HAFD AAR", -- 		equipment = {"High-angle Gun + Fire Director", "High-angle Gun + Fire Director", "Anti-Air Radar"}, -- 	},	-- 	{	-- 		name = "Type 3", -- 		equipment = {"Large Caliber Main Gun", "Type 3 Shell", "Anti-Aircraft Fire Director"}, -- 	},	-- 	{	-- 		name = "HA AAFD AAR", -- 		equipment = {"High-angle Gun", "Anti-Aircraft Fire Director", "Anti-Air Radar"}, -- 	},	-- 	{	-- 		name = "HAFD AAR", -- 		equipment = {"High-angle Gun + Fire Director", "Anti-Air Radar"}, -- 	},	-- 	{	-- 		name = "HA AAFD", -- 		equipment = {"High-angle Gun + Fire Director", "Anti-Air Radar"}, -- 	},	-- 	{	-- 		name = "Maya Kai Ni (HA CD AAR)", -- 		ship = {"Maya/Kai Ni"}, -- 		equipment = {"High-angle Gun", "25mm Triple Autocannon Mount Concentrated Deployment", "Anti-Air Radar"}, -- 	},	-- 	{	-- 		name = "Maya Kai Ni (HA CD)", -- 		ship = {"Maya/Kai Ni"}, -- 		equipment = {"High-angle Gun", "25mm Triple Autocannon Mount Concentrated Deployment"}, -- 	},	-- 	{	-- 		name = "CD AAMG AAR", -- 		equipment = {"25mm Triple Autocannon Mount Concentrated Deployment", "Anti-Air Machine Gun", "Anti-Air Radar"} -- 	},	-- },	_attack_template = "${mode} (${attack_power})", _day_battle_modes = { [1] = "Shelling", [2] = "Bombing", [3] = "Bombing, displayed as Shelling", },	_artillery_spotting_modes = { [1] = "Double Attack (120% x2)", [2] = "Double Attack (120% x2) / Cut-in: Secondary Gun (110%)", [3] = "Double Attack (120% x2) / Cut-in: AP (150%)", [4] = "Cut-in: Secondary Gun (110%)", [5] = "Cut-in: Secondary Gun (110%) / Cut-in: Radar (120%)", [6] = "Cut-in: Secondary Gun (110%) / Cut-in: AP (130%)", },	_night_battle_modes = { [1] = "Single Attack", [2] = "Single Torpedo", [3] = "Double Attack", [4] = "Cut-in: Main Gun", [5] = "Cut-in: Secondary Gun", [6] = "Cut-in: Mixed", [7] = "Cut-in: Torpedo", [8] = "Bombing", },	_night_battle_attack_counts = { [3] = "x2", [6] = "x2", [7] = "x2", },	_asw_attack_modes = { day = "Day", night = "Night", }, }

function ShipCapabilities:create(data) if data.ship then data._ship = data.ship data.ship = nil end setmetatable(data, data) data.__index = self return data end

--function ShipCapabilities:anti_air_cut_in --local result = false --base_name = self._ship:base_name --if base_name == "Akizuki" then --	result = self:akizuki_anti_air_cut_in --elseif base_name == "Maya" and self._ship:suffix == "Kai Ni" then --	result = self:maya_kai_ni_anti_air_cut_in

function ShipCapabilities:_is_asw_surface_ship(ship) if not ship then ship = self._ship end local ship_type = ship:type return ship_type == 2 or ship_type == 3 or ship_type == 4 or ship_type == 21 end

function ShipCapabilities:_is_asw_aviation_ship(ship) if not ship then ship = self._ship end local ship_type = ship:type return ship_type == 6 or ship_type == 7 or ship_type == 10 or ship_type == 16 end

function ShipCapabilities:_is_aviation_ship(ship) if not ship then ship = self._ship end local ship_type = ship:type return ship_type == 6 or ship_type == 7 or ship_type == 10 or ship_type == 11 or ship_type == 14 or ship_type == 16 or ship_type == 17 or ship_type == 18 end

function ShipCapabilities:_is_light_carrier(ship) if not ship then ship = self._ship end return ship:type == 7 end

function ShipCapabilities:_is_carrier(ship) if not ship then ship = self._ship end local ship_type = ship:type return ship_type == 7 or ship_type == 11 or ship_type == 18 end

function ShipCapabilities:_is_submarine(ship) if not ship then ship = self._ship end local ship_type = ship:type return ship_type == 13 or ship_type == 14 end

function ShipCapabilities:_is_installation(ship) if not ship then ship = self._ship end return ship:is_installation end

function ShipCapabilities:_is_main_gun(equipment) local equipment_type = equipment:type return equipment_type == 1 or equipment_type == 2 or equipment_type == 3 or equipment_type == 38 end

function ShipCapabilities:_is_secondary_gun(equipment) return equipment:type == 4 end

function ShipCapabilities:_is_torpedo(equipment) local equipment_type = equipment:type return equipment_type == 5 or equipment_type == 32 end

function ShipCapabilities:_is_bomber(equipment) local equipment_type = equipment:type return equipment_type == 7 or equipment_type == 8 or equipment_type == 11 end

function ShipCapabilities:_is_torpedo_bomber(equipment) return equipment:type == 8 end

function ShipCapabilities:_is_reconnaissance_seaplane(equipment) return equipment:type == 10 end

function ShipCapabilities:_is_seaplane(equipment) local equipment_type = equipment:type return equipment_type == 10 or equipment_type == 11 end

function ShipCapabilities:_is_radar(equipment) local equipment_type = equipment:type return equipment_type == 12 or equipment_type == 13 end

function ShipCapabilities:_is_sonar(equipment) local equipment_type = equipment:type return equipment_type == 14 or equipment_type == 40 end

function ShipCapabilities:_is_depth_charge(equipment) return equipment:type == 15 end

function ShipCapabilities:_is_type_3_shell(equipment) return equipment:type == 18 end

function ShipCapabilities:_is_ap_shell(equipment) return equipment:type == 19 end

function ShipCapabilities:_is_midget_submarine(equipment) return equipment:type == 22 end

function ShipCapabilities:_is_wg(equipment) return equipment:type == 37 end

function ShipCapabilities:_anti_installation local type_3_shells, wgs = 0, 0 for i = 1, self._ship:slots_length or 0 do		local equipment, size = self._ship:slot(i) if equipment then if self:_is_type_3_shell(equipment) then type_3_shells = type_3_shells + 1 elseif self:_is_wg(equipment) then wgs = wgs + 1 end end end return { type_3_shell_equiped = type_3_shells > 0, wg_equiped = wgs > 0 } end

-- Basic attack power for shelling and carrier shelling, including anti-installation modifiers. function ShipCapabilities:day_battle(vs_installation) if not self:_is_submarine then local firepower = self._ship:firepower_leveled if not firepower then return nil end local is_installation = self:_is_installation if not self:_is_carrier and not is_installation then if firepower > 0 then for i = 1, self._ship:slots_length do					local equipment, size = self._ship:slot(i) if equipment then local equipment_firepower = equipment:firepower if equipment_firepower then firepower = firepower + equipment_firepower end end end local anti_installation_modifier = 1 if vs_installation then local anti_installation = self:_anti_installation anti_installation_modifier = anti_installation.type_3_shell_equiped and 2.5 or 1 firepower = firepower + (anti_installation.wg_equiped and 75 or 0) end return 1, (5 + firepower) * anti_installation_modifier end else local total_space = self._ship:total_space if total_space and total_space > 0 then local has_planes, torpedo, bombing = false, self._ship:torpedo_leveled or 0, 0 for i = 1, self._ship:slots_length do					local equipment, size = self._ship:slot(i) if equipment then local is_bomber, equipment_firepower, equipment_torpedo, equipment_bombing = self:_is_bomber(equipment), equipment:firepower, equipment:torpedo, equipment:bombing if is_bomber then if size and size > 0 then has_planes = true else equipment_firepower = 0 equipment_torpedo = 0 equipment_bombing = 0 end end if equipment_firepower then firepower = firepower + equipment_firepower end if equipment_torpedo then torpedo = torpedo + equipment_torpedo end if equipment_bombing then bombing = bombing + equipment_bombing end end end if has_planes then if vs_installation then if bombing > 0 then return false end torpedo = 0 end return is_installation and 3 or 2, 55 + math.floor(1.5 * (firepower + torpedo + math.floor(1.3 * bombing))) end end end end return false end

function ShipCapabilities:format_day_battle(mode, attack_power) if mode then return format{self._attack_template, mode = self._day_battle_modes[mode], attack_power = math.floor(attack_power)} elseif mode == false then return "No Action" end return "??" end

-- Basic attack power for closing torpedo. function ShipCapabilities:closing_torpedo local torpedo = self._ship:torpedo_leveled if torpedo and torpedo > 0 then for i = 1, self._ship:slots_length do			local equipment, size = self._ship:slot(i) if equipment then local equipment_torpedo = equipment:torpedo if equipment_torpedo then torpedo = torpedo + equipment_torpedo end end end return 5 + torpedo end return false end

function ShipCapabilities:format_torpedo(attack_power) if attack_power then return tostring(math.floor(attack_power)) end return "No" end

ShipCapabilities.format_closing_torpedo = ShipCapabilities.format_torpedo

-- Detect possible day time special attack modifiers. function ShipCapabilities:artillery_spotting local total_space = self._ship:total_space if not total_space or total_space <= 0 then return false end local main_guns, secondary_guns, seaplanes, radars, ap_shells = 0, 0, 0, 0, 0 for i = 1, self._ship:slots_length or 0 do		local equipment, size = self._ship:slot(i) if equipment then if self:_is_main_gun(equipment) then main_guns = main_guns + 1 elseif self:_is_secondary_gun(equipment) then secondary_guns = secondary_guns + 1 elseif self:_is_seaplane(equipment) and size and size > 0 then seaplanes = seaplanes + 1 elseif self:_is_radar(equipment) then radars = radars + 1 elseif self:_is_ap_shell(equipment) then ap_shells = ap_shells + 1 end end end if seaplanes >= 1 then if main_guns >= 2 then if ap_shells >= 1 then return 3 elseif secondary_guns >= 1 then return 2 end return 1 elseif main_guns == 1 and secondary_guns >= 1 then if radars >= 1 then return 5 elseif ap_shells >= 1 then return 6 end return 4 end end return false end

function ShipCapabilities:format_artillery_spotting(mode) if mode then return self._artillery_spotting_modes[mode] end return "No" end

-- Basic attack power for night battle carrier shelling. -- Or basic attack power for night battle attack, including night special attack and anti-installation modifiers. function ShipCapabilities:night_battle(vs_installation) local main_guns, secondary_guns, torpedoes, has_planes, firepower, torpedo = 0, 0, 0, false, self._ship:firepower_leveled, self._ship:torpedo_leveled if not firepower or not torpedo then return nil end for i = 1, self._ship:slots_length or 0 do		local equipment, size = self._ship:slot(i) if equipment then local equipment_firepower, equipment_torpedo = equipment:firepower, equipment:torpedo if self:_is_main_gun(equipment) then main_guns = main_guns + 1 elseif self:_is_secondary_gun(equipment) then secondary_guns = secondary_guns + 1 elseif self:_is_torpedo(equipment) then torpedoes = torpedoes + 1 elseif self:_is_bomber(equipment) then if size and size > 0 then has_planes = true else equipment_firepower = 0 equipment_torpedo = 0 end end if equipment_firepower then firepower = firepower + equipment_firepower end if equipment_torpedo then torpedo = torpedo + equipment_torpedo end end end local anti_installation_modifier = 1 if vs_installation then torpedo = 0 local anti_installation = self:_anti_installation anti_installation_modifier = anti_installation.type_3_shell_equiped and 2.5 or 1 firepower = firepower + (anti_installation.wg_equiped and 75 or 0) end if self:_is_carrier then if has_planes and self._ship:night_bombing then return 8, firepower + torpedo end return false else if torpedoes >= 2 and torpedo > 0 then return 7, (firepower + torpedo) * 1.5 elseif main_guns >= 3 and firepower > 0 then return 4, (firepower + torpedo) * 2 * anti_installation_modifier elseif main_guns >= 2 and secondary_guns >= 1 and firepower > 0 then return 5, (firepower + torpedo) * 1.75 * anti_installation_modifier elseif main_guns >= 1 and torpedoes == 1 and firepower > 0 and torpedo > 0 then return 6, (firepower + torpedo) * 1.3 elseif main_guns + secondary_guns >= 2 and firepower > 0 then return 3, (firepower + torpedo) * 1.2 * anti_installation_modifier elseif torpedoes > 0 and torpedo > 0 then return 2, firepower + torpedo elseif firepower > 0 then return 1, (firepower + torpedo) * anti_installation_modifier else return false end end end

function ShipCapabilities:format_night_battle(mode, attack_power) if mode then local multiplier = self._night_battle_attack_counts[mode] if multiplier then attack_power = tostring(math.floor(attack_power)) .. " " .. multiplier end return format{self._attack_template, mode = self._night_battle_modes[mode], attack_power = attack_power} elseif mode == false then return "No Action" end return "Unknown" end

-- Basic attack power for opening torpedo. function ShipCapabilities:opening_torpedo local torpedo = self._ship:torpedo_leveled local opening_torpedo = self._ship:opening_torpedo if (opening_torpedo ~= true) and (opening_torpedo == false or not torpedo or torpedo <= 0) then return false end if self:_is_submarine then local level = self._ship:level if level and level >= 10 then opening_torpedo = true end end for i = 1, self._ship:slots_length or 0 do		local equipment, size = self._ship:slot(i) if equipment then if self:_is_midget_submarine(equipment) then opening_torpedo = true end local equipment_torpedo = equipment:torpedo if equipment_torpedo then torpedo = torpedo + equipment_torpedo end end end if opening_torpedo then return 5 + torpedo end return false end

ShipCapabilities.format_opening_torpedo = ShipCapabilities.format_torpedo

-- Basic attack power for ASW attack * sonar w/ depth charge modifier (pre-cap). function ShipCapabilities:asw_attack local base_asw local asw_override, day, night = self._ship:asw_attack, true, true if asw_override == false then return false elseif self:_is_asw_surface_ship then base_asw = 13 elseif self:_is_asw_aviation_ship then base_asw = 8 if not self._ship:night_bombing then night = false end else return false end local asw_leveled = self._ship:asw_leveled local ship_asw, equipment_asw, depth_charge, sonar, bomber = (asw_leveled or 0), 0, false, false, false for i = 1, self._ship:slots_length or 0 do		local equipment, size = self._ship:slot(i) if equipment then if not self:_is_reconnaissance_seaplane(equipment) then if self:_is_sonar(equipment) then sonar = true elseif self:_is_depth_charge(equipment) then depth_charge = true elseif self:_is_bomber(equipment) then bomber = true end equipment_asw = equipment_asw + (equipment:asw or 0) end end end if self:_is_light_carrier and not bomber then return false end local depth_charge_sonar_boost = 1 if depth_charge and sonar then depth_charge_sonar_boost = 1.15 end return depth_charge_sonar_boost * (base_asw + 1.5 * equipment_asw + 2 * math.sqrt(ship_asw)), day, night, not asw_leveled end

function ShipCapabilities:format_asw_attack(attack_power, day, night, uncertain) if attack_power then attack_power = math.floor(attack_power) local phases = {} if day then table.insert(phases, self._asw_attack_modes.day) end if night then table.insert(phases, self._asw_attack_modes.night) end if #phases == 1 then phases[1] = phases[1] .. " Only" end if uncertain then attack_power = attack_power .. "+"		end return format{self._attack_template, mode = table.concat(phases, "/"), attack_power = attack_power} end return "No" end

-- Basic attack power for opening airstrike (returns an array with attack values per each bomber). function ShipCapabilities:opening_airstrike if not self:_is_aviation_ship then return false end local airstrikes = {} for i = 1, self._ship:slots_length or 0 do		local equipment, size = self._ship:slot(i) if size and size > 0 and equipment and self:_is_bomber(equipment) then local equipment_torpedo, equipment_bombing = equipment:torpedo or 0, equipment:bombing or 0 if self:_is_torpedo_bomber(equipment) then table.insert(airstrikes, 1.5 * (25 + equipment_torpedo * math.sqrt(size))) -- 0.8 is also possible else table.insert(airstrikes, 25 + equipment_bombing * math.sqrt(size)) end end end if #airstrikes > 0 then return airstrikes end return false end

function ShipCapabilities:format_opening_airstrike(attacks) if attacks then local result = {} for _, attack in ipairs(attacks) do			table.insert(result, tostring(math.floor(attack))) end return table.concat(result, ",") end return "No" end

return ShipCapabilities