• 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:Calc"

From Kancolle Wiki
Jump to navigation Jump to search
m
 
(163 intermediate revisions by 3 users not shown)
Line 1: Line 1:
local Utils = require("Module:Utils")
+
local U = require("Module:Core")
local format = require("Module:StringOperations").format
 
 
local Formatting = require("Module:Formatting")
 
local Formatting = require("Module:Formatting")
 +
local Equipment = require("Module:Equipment")
 +
local Iterator = require("Module:Iterator")
 +
local ShipCapabilities = require("Module:ShipCapabilities")
 +
local ShipCardKai = require("Module:ShipCardKai")
 +
local ShipBattleCardKai = require("Module:ShipBattleCardKai")
 +
local Combat = require("Module:CalcCombat")
 +
local EquipmentCardKai = require("Module:EquipmentCardKai")
 +
local EquipmentGraphicKai = require("Module:EquipmentGraphicKai")
 +
local EquipmentCollection = require("Module:Data/Equipment")
 +
-- local AllEquipmentCollection = require("Module:Collection/EquipmentByApiId")
 +
local Development = require("Module:Development")
 +
local ShipsByApiId = require("Module:Collection/ShipsByApiId")
 
local Ship = require("Module:Ship")
 
local Ship = require("Module:Ship")
local ShipIterator = require("Module:ShipIterator")
 
  
 
local args = nil
 
local args = nil
 +
-- local Ship = nil
 
local ship = nil
 
local ship = nil
local frame = nil
+
local shipCapabilities = {}
 +
local target = nil
 +
local equipment = nil
 +
local sequence = nil
 +
local sequence_length = nil
 +
local sequence_position = nil
 +
-- local sorted = false
 +
-- local env = {}
  
 
local enumerating_functions = {
 
local enumerating_functions = {
 
+
     args = function() return mw.text.split(args.args, "%s*,%s*") end,
     args = function()
+
    base_names = function() return require('Module:Collection/ShipsBase') end,
        return mw.text.split(args.args, "%s*,%s*")
+
    all_names = function() return require('Module:Collection/Ships') end,
 +
    remodel_names = function()
 +
    local all_names = require('Module:Collection/Ships')
 +
    local base_names = require('Module:Collection/ShipsBase')
 +
    local result = {}
 +
    for _, name in ipairs(all_names) do
 +
    if not U.includes(base_names, name) then
 +
    local ship = Ship(name)
 +
    if ship._implementation_date then
 +
    table.insert(result, name)
 +
    end
 +
    end
 +
    end
 +
    return result
 
     end,
 
     end,
 
+
    enemy = function() return require('Module:Collection/EnemyShips') end,
     base_names = function()
+
     equipment = function()
        return ShipIterator.baseForms
+
    equipment = true
 +
    return U.imap(EquipmentCollection, function(e) return e._name end)
 
     end,
 
     end,
 
+
    --[[
     all_names = function()
+
     allEquipment = function()
         return ShipIterator.allForms
+
    equipment = true
 +
        local result = {}
 +
        for i = 1, 700 do
 +
            if AllEquipmentCollection[i] then
 +
                table.insert(result, AllEquipmentCollection[i])
 +
            end
 +
        end
 +
         return result
 
     end,
 
     end,
      
+
     enemyEquipment = function()
    enemy = function()
+
    equipment = true
         return ShipIterator.enemyForms
+
        local result = {}
 +
        for i = 501, 700 do
 +
            if AllEquipmentCollection[i] then
 +
                table.insert(result, AllEquipmentCollection[i])
 +
            end
 +
        end
 +
         return result
 
     end,
 
     end,
 +
    ]]--
 +
}
  
}
+
local function format_lua(lua)
 +
return tostring(type(lua) == "table" and table.concat(lua, args.concat_value or ", ") or lua)
 +
end
  
-- TODO: have a syntax for calling Module:Formatting functions?
 
 
local formatting_functions = {
 
local formatting_functions = {
 
+
    air_power = function(ship) return ship.air_power and ship:air_power() or -1 end,
     night_battle_power = function(ship)
+
     equipment_range = function(ship) return U.imax(U.imap(ship._equipment or {}, function (e) return Equipment(e.equipment):range() or 0 end), 0) end,
         return (ship:firepower_max() or 0) + (ship:torpedo_max() or 0)
+
    equipment_range_diff = function(ship)
 +
         local equipment_range = U.imax(U.imap(ship._equipment or {}, function (e) return Equipment(e.equipment):range() or 0 end), 0)
 +
        return (ship:range() or 0) - equipment_range
 +
    end,
 +
    night_battle_power = function(ship) return (ship:firepower_max() or 0) + (ship:torpedo_max() or 0) end,
 +
    format_day_battle = function(ship)
 +
        local mode, attack_power = shipCapabilities:day_battle()
 +
        return shipCapabilities:format_day_battle(mode, attack_power)
 +
    end,
 +
    format_night_battle = function(ship)
 +
        local mode, attack_power = shipCapabilities:night_battle()
 +
        return shipCapabilities:format_night_battle(mode, attack_power)
 +
    end,
 +
    format_opening_torpedo = function(ship)
 +
        return shipCapabilities:format_torpedo(shipCapabilities:opening_torpedo())
 +
    end,
 +
    format_closing_torpedo = function(ship)
 +
        return shipCapabilities:format_torpedo(shipCapabilities:closing_torpedo())
 +
    end,
 +
    format_asw_attack = function(ship)
 +
        local attack_power, opening, day, night, uncertain = shipCapabilities:asw_attack()
 +
        return shipCapabilities:format_asw_attack(attack_power, opening, day, night, uncertain)
 
     end,
 
     end,
      
+
     format_opening_airstrike = function(ship)
    code = function(ship)
+
         return shipCapabilities:format_opening_airstrike(shipCapabilities:opening_airstrike())
         return Formatting:format_ship_code(ship:type())
 
 
     end,
 
     end,
 
+
     slots = function(ship) return format_lua(U.imap(ship._equipment or {}, function (e) return e and e.size or '?' end)) end,
     -- overrides ShipData:type
+
     equips = function(ship)
     type = function(ship)
+
         return 'style="text-align:left"|' .. U.ijoin(U.imap(ship._equipment or {}, function (e)
         return Formatting:format_ship_type(ship:type())
+
            if not e or e.equipment == nil then
 +
                return '(?)'
 +
            end
 +
            if e.equipment == false then
 +
                return '[[File:Xx_c.png]]'
 +
            end
 +
            local icon = type(e.equipment) == 'string' and Equipment(e.equipment):icon() or e.equipment:icon()
 +
            local link = type(e.equipment) == 'string' and e.equipment or e.equipment:name()
 +
            return string.format('%s [[%s]]', Formatting:format_image{Formatting:format_equipment_icon(icon), link = link}, link)
 +
        end), "<br>")
 
     end,
 
     end,
 
+
    code = function(obj) return equipment and Formatting:format_equipment_type(obj:type()) or Formatting:format_ship_code(obj:type()) end,
     -- overrides ShipData:link
+
     code_link = function(obj) return string.format("[[%s]]", Formatting:format_ship_code(obj:type())) end,
     link = function(ship)
+
    type = function(obj) return equipment and Formatting:format_equipment_type(obj:type()) or Formatting:format_ship_type(obj:type()) end,
         return Formatting:format_link(ship:link())
+
     icon = function(obj)
 +
        if obj.hp then
 +
            if obj:id() >= 1501 then
 +
                return string.format("[[File:Enemy Icon %s.png|100px]]", obj:unique_name():gsub('- Damaged', 'Damaged'))
 +
            else
 +
                return string.format("[[File:Ship Icon %s.png|100px]]", obj:name())
 +
            end
 +
         else
 +
        return obj.icon and ([=[[[File:]=] .. Formatting:format_equipment_icon(obj:icon()) .. "]]") or ''
 +
            -- return obj.icon and ([[<span data-sort-value="]] .. (obj._type or '0') .. [=[">[[File:]=] .. Formatting:format_equipment_icon(obj:icon()) .. "]]</span>") or ''
 +
        end
 
     end,
 
     end,
 
+
    icon_damaged = function(obj) return string.format("[[File:Ship Icon %s Damaged.png|100px]]", obj:name()) end,
     -- overrides ShipData:implementation_date
+
     link = function(obj) return string.format("[[%s]]", equipment and obj:name() or obj:unique_name()) end,
 +
    class = function(ship) return ship:class() and ship:class():name() or "?" end,
 
     implementation_date = function(ship)
 
     implementation_date = function(ship)
 
         local date = ship:implementation_date()
 
         local date = ship:implementation_date()
         return not date and "??" or format{
+
         return not date and "??" or string.format("%s/%s/%s", date[1], date[2] < 10 and "0" .. date[2] or date[2], date[3] < 10 and "0" .. date[3] or date[3])
            "${year}/${month}/${day}",
+
    end,
            year = date[1],
+
    banner = function(obj)
            month = date[2] < 10 and "0" .. date[2] or date[2],
+
    if not obj or not obj.lua_name then
            day = date[3] < 10 and "0" .. date[3] or date[3],
+
    return ' '
         }
+
    end
 +
         return ShipBattleCardKai:Asset({ obj:lua_name(), hd = true, size = "160px" })
 +
    end,
 +
    card = function(obj)
 +
    if not obj or not obj.lua_name then
 +
    return ' '
 +
    end
 +
        if obj.hp then
 +
            return ShipCardKai:Asset({ obj:lua_name(), hd = true, size = "160px" })
 +
        else
 +
            return EquipmentCardKai:Asset({ obj:name(), size = "75px", link = "nil" })
 +
        end
 +
    end,
 +
    item = function(obj) return EquipmentGraphicKai:Asset({ obj:name(), size = "75px", link = "nil" }) end,
 +
    fairy = function(obj) return EquipmentGraphicKai:Asset({ obj:name(), size = "75px", fairy = "only", link = "nil" }) end,
 +
    full = function(obj) return EquipmentGraphicKai:Asset({ obj:name(), size = "75px", fairy = "true", link = "nil" }) end,
 +
    card_ = function(o) return string.format("[[File:%s Card.png|75px]]", o:name()) end,
 +
    equipment_ = function(o) return string.format("[[File:%s Equipment.png|75px]]", o:name()) end,
 +
    character_ = function(o) return string.format("[[File:%s Character.png|75px]]", o:name()) end,
 +
    full_ = function(o) return string.format("[[File:%s Full.png|75px]]", o:name()) end,
 +
    format_morale = function(ship)
 +
        local morale = ship:morale() or 49
 +
        return morale <= 19 and "Red" or morale <= 29 and "Orange" or morale <= 49 and "Normal" or "Sparkle"
 +
    end,
 +
    hit_rate = function(ship, target)
 +
        local r = Combat.hit_rate(ship, target)
 +
        if r then
 +
            ship.hit_rate = r
 +
            return r .. "%"
 +
        else
 +
            return "nil"
 +
        end
 +
    end,
 +
    critical_hit_rate = function(ship, target)
 +
        local r = Combat.critical_hit_rate(ship, target)
 +
        if r then
 +
            ship.critical_hit_rate = r
 +
            return r .. "%"
 +
        else
 +
            return "nil"
 +
        end
 +
    end,
 +
    scrap_string = function(eq)
 +
        local scrap = eq:scrap()
 +
        return string.format("%s/%s/%s/%s", scrap.fuel or 0, scrap.ammo or 0, scrap.steel or 0, scrap.bauxite or 0)
 +
    end,
 +
    development = function(eq) return Development.formatResources(eq) end,
 +
    development_rate = function(eq) return Development.formatRates(eq) end,
 +
    development_hq = function(eq) return Development.formatHQ(eq) end,
 +
    backMinusRarity = function(ship) return (ship:back() or 0) - (ship:rarity() or 0) end,
 +
    rarity_bg = function(ship)
 +
        return string.format('style="background:%s"|%s<br>%s', Formatting:format_ship_back(ship:rarity()), ship:rarity() or '??', Formatting:format_ship_rarity(ship:rarity()))
 +
    end,
 +
    back_bg = function(ship)
 +
        return string.format('style="background:%s"|%s<br>%s', Formatting:format_ship_back(ship:back()), ship:back() or '??', Formatting:format_ship_rarity(ship:back()))
 +
    end,
 +
    same_day_remodel_links = function(ship)
 +
    local result = { string.format("[[%s]]", ship:name()) }
 +
    local visited = {}
 +
visited[ship:name()] = true
 +
    while true do
 +
    local next = ship:remodel_to()
 +
    if not next then break end
 +
    ship = Ship(next)
 +
    if ship._implementation_date then break end
 +
    if visited[ship:name()] then break end
 +
    visited[ship:name()] = true
 +
    table.insert(result, string.format("[[%s]]", ship:name()))
 +
    end
 +
    return table.concat(result, ", ")
 
     end,
 
     end,
 
 
}
 
}
  
function format_lua(lua)
+
--[[
     if type(lua) == "table" then
+
local function addFormattingFunctions(name, table)
         return tostring(table.concat(lua, args.concat_value or ", "))
+
     for k, v in pairs(table) do
    else
+
         formatting_functions[name .. "." .. k] = v
        return tostring(lua)
 
 
     end
 
     end
 
end
 
end
  
function format_value(key)
+
addFormattingFunctions("FitData", require("Module:CalcFit"))
    local formatting_function = formatting_functions[key]
+
addFormattingFunctions("Assets", require("Module:CalcAsset"))
    if formatting_function then
+
]]--
        return formatting_function(ship)
+
 
    else
+
local function format_value(key, ship, target)
        local lua = ship[key]
+
local keys = mw.text.split(key, "%s*%.%s*")
        if type(lua) == "function" then
+
local result = ship
            return format_lua(lua(ship))
+
for _, key in ipairs(keys) do
 +
    local formatting_function = formatting_functions[key]
 +
    if formatting_function then
 +
        result = formatting_function(result, target)
 +
    else
 +
    local lua = result[key] or result['_' .. key]
 +
    if type(lua) == "function" then
 +
        result = lua(result)
 +
    else
 +
        result = lua
 +
    end
 +
    end
 +
if type(result) ~= 'table' then
 +
return format_lua(result)
 +
end
 +
    --[[
 +
    if shipCapabilities[key] then
 +
        local a, b = shipCapabilities[key](shipCapabilities)
 +
        return format_lua(b or a)
 +
    end
 +
    ]]--
 +
end
 +
return format_lua(result)
 +
end
 +
 
 +
local function format_arg(arg)
 +
    return args.format == "table" and ("| " .. arg .. "\n") or arg
 +
end
 +
 
 +
--[[
 +
local function interpret_setter(s)
 +
    local kv = mw.text.split(s, "%s*~%s*")
 +
    local k = kv[1]
 +
    local v = kv[2]
 +
    local c = kv[3]
 +
    if k and v and c then
 +
        env[c] = env[c] or require("Module:" .. c)
 +
        env[k] = env[c](v)
 +
        if k == "_" then
 +
            ship = env[k]
 +
        end
 +
        return true
 +
    elseif k and v then
 +
        if k == "_equipment" then
 +
            local equipment = {}
 +
            local accuracy = 0
 +
            local ng = 0
 +
            for eq in string.gmatch(v, '([^,]+)') do
 +
                local eqObj = Equipment(eq)
 +
                table.insert(equipment, { equipment = eqObj })
 +
                accuracy = accuracy + (eqObj:shelling_accuracy() or 0)
 +
                if eqObj:type() == 3 then
 +
                    ng = ng + 1
 +
                end
 +
            end
 +
            ship._equipment = equipment
 +
            ship._accuracy = accuracy
 +
            if ng > 0 then
 +
                ship._ng = ng
 +
            end
 
         else
 
         else
             return format_lua(lua)
+
             if v ~= "nil" and ship then
 +
                ship[k] = tonumber(v)
 +
            else
 +
                env[k] = v
 +
            end
 
         end
 
         end
 +
        return true
 +
    else
 +
        return false
 
     end
 
     end
 
end
 
end
  
local sequence = nil
+
local function trigger()
local sequence_position = nil
+
    local ship_key = sequence[sequence_position]
 +
    sequence_position = sequence_position + 1
 +
    ship = Ship(ship_key)
 +
    if ship:id() then
 +
        shipCapabilities = ShipCapabilities{ ship = ship }
 +
        equipment = false
 +
    else
 +
        ship = Equipment(ship_key)
 +
        equipment = true
 +
    end
 +
end
  
function interpret_arg(arg)
+
local function interpret_arg(arg)
 
     local prefix = string.sub(arg, 1, 1)
 
     local prefix = string.sub(arg, 1, 1)
     if prefix == "@" then
+
    local prefix2 = string.sub(arg, 1, 2)
         local enumerator = string.sub(arg, 2)
+
     if arg == "-" then return format_arg("")
         local enumerating_function = enumerating_functions[enumerator]
+
elseif arg == "!@" then trigger(false)
         if enumerating_function and not sequence then
+
    elseif prefix2 == "!!" then
             sequence = enumerating_function()
+
         local key = string.sub(arg, 3)
             sequence_position = 1
+
        target = Ship(key)
 +
    elseif prefix == "!" then
 +
         local key = string.sub(arg, 2)
 +
         if not interpret_setter(key) then
 +
             local parts = string.gmatch(key, '([^:]+)')
 +
            local i = 1
 +
             for part in parts do
 +
                if i == 1 then
 +
                    key = part
 +
                    ship = Ship(key)
 +
                    if ship:hp() then
 +
                        shipCapabilities = ShipCapabilities{ ship = ship }
 +
                        equipment = false
 +
                    else
 +
                        ship = Equipment(key)
 +
                        equipment = true
 +
                    end
 +
                else
 +
                    interpret_setter(part)
 +
                end
 +
                i = i + 1
 +
            end
 
         end
 
         end
    elseif arg == "!@" then
 
        local ship_key = sequence[sequence_position]
 
        sequence_position = sequence_position + 1
 
        ship = Ship(ship_key)
 
    elseif prefix == "!" then
 
        local ship_key = string.sub(arg, 2)
 
        ship = Ship(ship_key)
 
 
     elseif prefix == "#" and args.format == "table" then
 
     elseif prefix == "#" and args.format == "table" then
 
         return "|-\n"
 
         return "|-\n"
     elseif prefix == "?" then
+
     if prefix == "?" then
         prefix = string.sub(arg, 2, 2)
+
         local prefix2 = string.sub(arg, 1, 2)
         if prefix == "?" then
+
         if prefix2 == "??" then return format_arg(format_value(string.sub(arg, 3), target, ship))
            local key = string.sub(arg, 3)
+
        elseif prefix2 == "?#" then return "?# is unimplemented" -- frame:preprocess(format{string.sub(arg, 3), this = env[this] or ""})
            return frame:preprocess(format{
 
                '{{${template}|${ship}}}',
 
                template = key,
 
                ship = ship:name('/')
 
            })
 
 
         else
 
         else
            local key = string.sub(arg, 2)
+
if test then
            local value = format_value(key)
+
return format_arg(format_value(string.sub(arg, 2), ship, target))
            if args.format == "table" then
+
else
                return "|" .. value .. "\n"
+
return nil
            else
+
end
                return value
 
            end
 
        end
 
 
     else
 
     else
         return arg
+
         return format_arg(arg)
 
     end
 
     end
 
end
 
end
  
local special_functions = {
+
     Ship = args.enemy and require("Module:EnemyShip") or require("Module:Ship")
     apply = function(args_)
+
    if args.from and args.to then
        local template = args_[2]
+
         sequence = {}
         local values = {}
+
         for i = tonumber(args.from), tonumber(args.to) do
         for i = 3, #args_ do
+
             table.insert(sequence, i)
             table.insert(values, format{
 
                '{{${template}|${arg}}}',
 
                template = template,
 
                arg = args_[i]
 
            })
 
 
         end
 
         end
         return frame:preprocess(table.concat(values, args.concat or ""))
+
         sequence_position = 1
 
     end
 
     end
}
 
  
function interpret_args(args_)
+
        else
    args = args_
+
            local buildIterator = Iterator[enumerator]
    local special_function = special_functions[args[1]]
+
            if buildIterator then
    if special_function then
+
                sequence = {}
        return special_function(args)
+
                sequence_position = 1
    else
+
                local iterator = buildIterator(args)
        local values = {}
+
                while iterator.next() do
        repeat
+
                    table.insert(sequence, iterator.current())
            for _, arg in ipairs(args_) do
+
                end
                local value = interpret_arg(arg)
+
                sequence_length = #sequence
                if value then
+
            end
                    table.insert(values, value)
+
 
                end
+
]]--
            end
+
 
        until not sequence or sequence_position > #sequence
+
local function interpret(args_)
        return table.concat(values, args.concat or "")
+
args = args_
 +
local filterArgs = {}
 +
    for _, arg in ipairs(args) do
 +
    local prefix = string.sub(arg, 1, 1)
 +
    if prefix == "~" then
 +
    -- ~x.y.z~a.b.c means obj.x.y.z == a or obj.x.y.z == b or obj.x.y.z == c
 +
    -- obj.x.y.z can be obj:x():y():z(), etc.
 +
    local ksvs = mw.text.split(string.sub(arg, 2), "%s*~%s*")
 +
    local ks = mw.text.split(ksvs[1], "%s*%.%s*")
 +
    local vs = mw.text.split(ksvs[2], "%s*,%s*")
 +
    table.insert(filterArgs, {ks, vs})
 +
    end
 
     end
 
     end
end
+
    local actions = {}
 +
    for _, arg in ipairs(args) do
 +
    local prefix = string.sub(arg, 1, 1)
 +
    if prefix == "@" then
 +
    local enumerator = string.sub(arg, 2)
 +
        local enumerating_function = enumerating_functions[enumerator]
 +
        if enumerating_function then
 +
        sequence = {}
 +
        for _, e in ipairs(enumerating_function()) do
 +
        local ship
 +
        if equipment then
 +
        ship = Equipment(e)
 +
        else
 +
        ship = Ship(e)
 +
        end
 +
        if #filterArgs > 0 then
 +
    local test = true
 +
for _, ksvs in ipairs(filterArgs) do
 +
local test2 = false
 +
 
 +
local obj = ship
 +
for _, k in ipairs(ksvs[1]) do
 +
local g = obj[k]
 +
if type(g) == 'function' then
 +
obj = g(obj)
 +
elseif type(g) == 'table' then
 +
obj = g
 +
else
 +
obj = g
 +
break
 +
end
 +
end
 +
local v1 = tostring(obj)
  
local Calc = {}
+
for _, v2 in ipairs(ksvs[2]) do
 +
if v1 == v2 then
 +
test2 = true
 +
break
 +
end
 +
end
  
function Calc.format(frame_, args_)
+
if not test2 then
     frame = frame_
+
test = false
     return interpret_args(args_ or Utils.getTemplateArgs(frame_).explicit)
+
break
 +
end
 +
end
 +
if test then
 +
table.insert(sequence, ship)
 +
end
 +
        else
 +
        table.insert(sequence, ship)
 +
        end
 +
        end
 +
        sequence_position = 1
 +
sequence_length = #sequence
 +
        end
 +
    elseif prefix == '!' then
 +
    local name = string.sub(arg, 2)
 +
    local obj = Ship(name)
 +
    if not obj:hp() then
 +
    equipment = true
 +
    obj = Equipment(name)
 +
    end
 +
    sequence = {}
 +
    table.insert(sequence, obj)
 +
        sequence_position = 1
 +
sequence_length = 1
 +
    elseif arg == "#" or prefix == "?" then
 +
    table.insert(actions, string.sub(arg, 2))
 +
    end
 +
    end
 +
    if args.sort and sequence then
 +
    local sorts = mw.text.split(args.sort, "%s*,%s*")
 +
    table.sort(sequence, function(a, b)
 +
    for _, sort in ipairs(sorts) do
 +
  local x = a[sort](a)
 +
  local y = b[sort](b)
 +
  if x == nil then return true end
 +
  if y == nil then return false end
 +
  if x < y then return true end
 +
  if x > y then return false end
 +
    end
 +
return false
 +
end)
 +
    end
 +
    local values = {}
 +
    repeat
 +
    if sequence then
 +
    ship = sequence[sequence_position]
 +
    sequence_position = sequence_position + 1
 +
    end
 +
        for _, arg in ipairs(actions) do
 +
        if arg == '' then
 +
        if args.format == "table" then
 +
        table.insert(values, "|-\n")
 +
        end
 +
        else
 +
            local value = ship and format_arg(format_value(arg, ship)) -- interpret_arg(arg)
 +
            if value then
 +
                table.insert(values, value)
 +
            end
 +
            end
 +
        end
 +
     until not sequence_length or sequence_position > sequence_length
 +
     return table.concat(values, args.concat or (args.format == "table" and "" or ", "))
 
end
 
end
  
function Calc.test()
+
local function test()
 
+
mw.log(interpret({"@base_names", "~is_battleship~true", "?link", sort = "type,id"}))
 +
mw.log(interpret({"@base_names", "~class.name~Kagerou Class", "?link", sort = "type,id"}))
 +
mw.log(interpret({"!Saiun", "?icon"}))
 +
mw.log(interpret({"!Ayanami", "?scrap_string", "?scrap.fuel", "?scrap_fuel", "?_scrap_fuel"}))
 
end
 
end
  
return Calc
+
-- print(p.test())
 +
return {
 +
format = function(frame) return interpret(U.getTemplateArgs(frame).explicit) end,
 +
test = test,
 +
}

Latest revision as of 18:08, 17 October 2024

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

local U = require("Module:Core")
local Formatting = require("Module:Formatting")
local Equipment = require("Module:Equipment")
local Iterator = require("Module:Iterator")
local ShipCapabilities = require("Module:ShipCapabilities")
local ShipCardKai = require("Module:ShipCardKai")
local ShipBattleCardKai = require("Module:ShipBattleCardKai")
local Combat = require("Module:CalcCombat")
local EquipmentCardKai = require("Module:EquipmentCardKai")
local EquipmentGraphicKai = require("Module:EquipmentGraphicKai")
local EquipmentCollection = require("Module:Data/Equipment")
-- local AllEquipmentCollection = require("Module:Collection/EquipmentByApiId")
local Development = require("Module:Development")
local ShipsByApiId = require("Module:Collection/ShipsByApiId")
local Ship = require("Module:Ship")

local args = nil
-- local Ship = nil
local ship = nil
local shipCapabilities = {}
local target = nil
local equipment = nil
local sequence = nil
local sequence_length = nil
local sequence_position = nil
-- local sorted = false
-- local env = {}

local enumerating_functions = {
    args = function() return mw.text.split(args.args, "%s*,%s*") end,
    base_names = function() return require('Module:Collection/ShipsBase') end,
    all_names = function() return require('Module:Collection/Ships') end,
    remodel_names = function()
    	local all_names = require('Module:Collection/Ships')
    	local base_names = require('Module:Collection/ShipsBase')
    	local result = {}
    	for _, name in ipairs(all_names) do
    		if not U.includes(base_names, name) then
    			local ship = Ship(name)
    			if ship._implementation_date then
    				table.insert(result, name)
    			end
    		end
    	end
    	return result
    end,
    enemy = function() return require('Module:Collection/EnemyShips') end,
    equipment = function()
    	equipment = true
    	return U.imap(EquipmentCollection, function(e) return e._name end)
    end,
    --[[
    allEquipment = function()
    	equipment = true
        local result = {}
        for i = 1, 700 do
            if AllEquipmentCollection[i] then
                table.insert(result, AllEquipmentCollection[i])
            end
        end
        return result
    end,
    enemyEquipment = function()
    	equipment = true
        local result = {}
        for i = 501, 700 do
            if AllEquipmentCollection[i] then
                table.insert(result, AllEquipmentCollection[i])
            end
        end
        return result
    end,
    ]]--
}

local function format_lua(lua)
	return tostring(type(lua) == "table" and table.concat(lua, args.concat_value or ", ") or lua)
end

local formatting_functions = {
    air_power = function(ship) return ship.air_power and ship:air_power() or -1 end,
    equipment_range = function(ship) return U.imax(U.imap(ship._equipment or {}, function (e) return Equipment(e.equipment):range() or 0 end), 0) end,
    equipment_range_diff = function(ship)
        local equipment_range = U.imax(U.imap(ship._equipment or {}, function (e) return Equipment(e.equipment):range() or 0 end), 0)
        return (ship:range() or 0) - equipment_range
    end,
    night_battle_power = function(ship) return (ship:firepower_max() or 0) + (ship:torpedo_max() or 0) end,
    format_day_battle = function(ship)
        local mode, attack_power = shipCapabilities:day_battle()
        return shipCapabilities:format_day_battle(mode, attack_power)
    end,
    format_night_battle = function(ship)
        local mode, attack_power = shipCapabilities:night_battle()
        return shipCapabilities:format_night_battle(mode, attack_power)
    end,
    format_opening_torpedo = function(ship)
        return shipCapabilities:format_torpedo(shipCapabilities:opening_torpedo())
    end,
    format_closing_torpedo = function(ship)
        return shipCapabilities:format_torpedo(shipCapabilities:closing_torpedo())
    end,
    format_asw_attack = function(ship)
        local attack_power, opening, day, night, uncertain = shipCapabilities:asw_attack()
        return shipCapabilities:format_asw_attack(attack_power, opening, day, night, uncertain)
    end,
    format_opening_airstrike = function(ship)
        return shipCapabilities:format_opening_airstrike(shipCapabilities:opening_airstrike())
    end,
    slots = function(ship) return format_lua(U.imap(ship._equipment or {}, function (e) return e and e.size or '?' end)) end,
    equips = function(ship)
        return 'style="text-align:left"|' .. U.ijoin(U.imap(ship._equipment or {}, function (e)
            if not e or e.equipment == nil then
                return '(?)'
            end
            if e.equipment == false then
                return '[[File:Xx_c.png]]'
            end
            local icon = type(e.equipment) == 'string' and Equipment(e.equipment):icon() or e.equipment:icon()
            local link = type(e.equipment) == 'string' and e.equipment or e.equipment:name()
            return string.format('%s [[%s]]', Formatting:format_image{Formatting:format_equipment_icon(icon), link = link}, link)
        end), "<br>")
    end,
    code = function(obj) return equipment and Formatting:format_equipment_type(obj:type()) or Formatting:format_ship_code(obj:type()) end,
    code_link = function(obj) return string.format("[[%s]]", Formatting:format_ship_code(obj:type())) end,
    type = function(obj) return equipment and Formatting:format_equipment_type(obj:type()) or Formatting:format_ship_type(obj:type()) end,
    icon = function(obj)
        if obj.hp then
            if obj:id() >= 1501 then
                return string.format("[[File:Enemy Icon %s.png|100px]]", obj:unique_name():gsub('- Damaged', 'Damaged'))
            else
                return string.format("[[File:Ship Icon %s.png|100px]]", obj:name())
            end
        else
        	return obj.icon and ([=[[[File:]=] .. Formatting:format_equipment_icon(obj:icon()) .. "]]") or ''
            -- return obj.icon and ([[<span data-sort-value="]] .. (obj._type or '0') .. [=[">[[File:]=] .. Formatting:format_equipment_icon(obj:icon()) .. "]]</span>") or ''
        end
    end,
    icon_damaged = function(obj) return string.format("[[File:Ship Icon %s Damaged.png|100px]]", obj:name()) end,
    link = function(obj) return string.format("[[%s]]", equipment and obj:name() or obj:unique_name()) end,
    class = function(ship) return ship:class() and ship:class():name() or "?" end,
    implementation_date = function(ship)
        local date = ship:implementation_date()
        return not date and "??" or string.format("%s/%s/%s", date[1], date[2] < 10 and "0" .. date[2] or date[2], date[3] < 10 and "0" .. date[3] or date[3])
    end,
    banner = function(obj)
    	if not obj or not obj.lua_name then
    		return ' '
    	end
        return ShipBattleCardKai:Asset({ obj:lua_name(), hd = true, size = "160px" })
    end,
    card = function(obj)
    	if not obj or not obj.lua_name then
    		return ' '
    	end
        if obj.hp then
            return ShipCardKai:Asset({ obj:lua_name(), hd = true, size = "160px" })
        else
            return EquipmentCardKai:Asset({ obj:name(), size = "75px", link = "nil" })
        end
    end,
    item = function(obj) return EquipmentGraphicKai:Asset({ obj:name(), size = "75px", link = "nil" }) end,
    fairy = function(obj) return EquipmentGraphicKai:Asset({ obj:name(), size = "75px", fairy = "only", link = "nil" }) end,
    full = function(obj) return EquipmentGraphicKai:Asset({ obj:name(), size = "75px", fairy = "true", link = "nil" }) end,
    card_ = function(o) return string.format("[[File:%s Card.png|75px]]", o:name()) end,
    equipment_ = function(o) return string.format("[[File:%s Equipment.png|75px]]", o:name()) end,
    character_ = function(o) return string.format("[[File:%s Character.png|75px]]", o:name()) end,
    full_ = function(o) return string.format("[[File:%s Full.png|75px]]", o:name()) end,
    format_morale = function(ship)
        local morale = ship:morale() or 49
        return morale <= 19 and "Red" or morale <= 29 and "Orange" or morale <= 49 and "Normal" or "Sparkle"
    end,
    hit_rate = function(ship, target)
        local r = Combat.hit_rate(ship, target)
        if r then
            ship.hit_rate = r
            return r .. "%"
        else
            return "nil"
        end
    end,
    critical_hit_rate = function(ship, target)
        local r = Combat.critical_hit_rate(ship, target)
        if r then
            ship.critical_hit_rate = r
            return r .. "%"
        else
            return "nil"
        end
    end,
    scrap_string = function(eq)
        local scrap = eq:scrap()
        return string.format("%s/%s/%s/%s", scrap.fuel or 0, scrap.ammo or 0, scrap.steel or 0, scrap.bauxite or 0)
    end,
    development = function(eq) return Development.formatResources(eq) end,
    development_rate = function(eq) return Development.formatRates(eq) end,
    development_hq = function(eq) return Development.formatHQ(eq) end,
    backMinusRarity = function(ship) return (ship:back() or 0) - (ship:rarity() or 0) end,
    rarity_bg = function(ship)
        return string.format('style="background:%s"|%s<br>%s', Formatting:format_ship_back(ship:rarity()), ship:rarity() or '??', Formatting:format_ship_rarity(ship:rarity()))
    end,
    back_bg = function(ship)
        return string.format('style="background:%s"|%s<br>%s', Formatting:format_ship_back(ship:back()), ship:back() or '??', Formatting:format_ship_rarity(ship:back()))
    end,
    same_day_remodel_links = function(ship)
    	local result = { string.format("[[%s]]", ship:name()) }
    	local visited = {}
		visited[ship:name()] = true
    	while true do
    		local next = ship:remodel_to()
    		if not next then break end
    		ship = Ship(next)
    		if ship._implementation_date then break end
    		if visited[ship:name()] then break end
    		visited[ship:name()] = true
    		table.insert(result, string.format("[[%s]]", ship:name()))
    	end
    	return table.concat(result, ", ")
    end,
}

--[[
local function addFormattingFunctions(name, table)
    for k, v in pairs(table) do
        formatting_functions[name .. "." .. k] = v
    end
end

addFormattingFunctions("FitData", require("Module:CalcFit"))
addFormattingFunctions("Assets", require("Module:CalcAsset"))
]]--

local function format_value(key, ship, target)
	local keys = mw.text.split(key, "%s*%.%s*")
	local result = ship
	for _, key in ipairs(keys) do
    	local formatting_function = formatting_functions[key]
    	if formatting_function then
        	result = formatting_function(result, target)
    	else
    		local lua = result[key] or result['_' .. key]
    		if type(lua) == "function" then
        		result = lua(result)
    		else
        		result = lua
    		end
    	end
		if type(result) ~= 'table' then
			return format_lua(result)
		end
    	--[[
    	if shipCapabilities[key] then
        	local a, b = shipCapabilities[key](shipCapabilities)
        	return format_lua(b or a)
    	end
    	]]--
	end
	return format_lua(result)
end

local function format_arg(arg)
    return args.format == "table" and ("| " .. arg .. "\n") or arg
end

--[[
local function interpret_setter(s)
    local kv = mw.text.split(s, "%s*~%s*")
    local k = kv[1]
    local v = kv[2]
    local c = kv[3]
    if k and v and c then
        env[c] = env[c] or require("Module:" .. c)
        env[k] = env[c](v)
        if k == "_" then
            ship = env[k]
        end
        return true
    elseif k and v then
        if k == "_equipment" then
            local equipment = {}
            local accuracy = 0
            local ng = 0
            for eq in string.gmatch(v, '([^,]+)') do
                local eqObj = Equipment(eq)
                table.insert(equipment, { equipment = eqObj })
                accuracy = accuracy + (eqObj:shelling_accuracy() or 0)
                if eqObj:type() == 3 then
                    ng = ng + 1
                end
            end
            ship._equipment = equipment
            ship._accuracy = accuracy
            if ng > 0 then
                ship._ng = ng
            end
        else
            if v ~= "nil" and ship then
                ship[k] = tonumber(v)
            else
                env[k] = v
            end
        end
        return true
    else
        return false
    end
end

local function trigger()
    local ship_key = sequence[sequence_position]
    sequence_position = sequence_position + 1
    ship = Ship(ship_key)
    if ship:id() then
        shipCapabilities = ShipCapabilities{ ship = ship }
        equipment = false
    else
        ship = Equipment(ship_key)
        equipment = true
    end
end

local function interpret_arg(arg)
    local prefix = string.sub(arg, 1, 1)
    local prefix2 = string.sub(arg, 1, 2)
    if arg == "-" then return format_arg("")
	elseif arg == "!@" then trigger(false)
    elseif prefix2 == "!!" then
        local key = string.sub(arg, 3)
        target = Ship(key)
    elseif prefix == "!" then
        local key = string.sub(arg, 2)
        if not interpret_setter(key) then
            local parts = string.gmatch(key, '([^:]+)')
            local i = 1
            for part in parts do
                if i == 1 then
                    key = part
                    ship = Ship(key)
                    if ship:hp() then
                        shipCapabilities = ShipCapabilities{ ship = ship }
                        equipment = false
                    else
                        ship = Equipment(key)
                        equipment = true
                    end
                else
                    interpret_setter(part)
                end
                i = i + 1
            end
        end
    elseif prefix == "#" and args.format == "table" then
        return "|-\n"
    if prefix == "?" then
        local prefix2 = string.sub(arg, 1, 2)
        if prefix2 == "??" then return format_arg(format_value(string.sub(arg, 3), target, ship))
        elseif prefix2 == "?#" then return "?# is unimplemented" -- frame:preprocess(format{string.sub(arg, 3), this = env[this] or ""})
        else
	if test then
		return format_arg(format_value(string.sub(arg, 2), ship, target))
	else
		return nil
	end
    else
        return format_arg(arg)
    end
end

    Ship = args.enemy and require("Module:EnemyShip") or require("Module:Ship")
    if args.from and args.to then
        sequence = {}
        for i = tonumber(args.from), tonumber(args.to) do
            table.insert(sequence, i)
        end
        sequence_position = 1
    end

	        else
	            local buildIterator = Iterator[enumerator]
	            if buildIterator then
	                sequence = {}
	                sequence_position = 1
	                local iterator = buildIterator(args)
	                while iterator.next() do
	                    table.insert(sequence, iterator.current())
	                end
	                sequence_length = #sequence
	            end

]]--

local function interpret(args_)
	args = args_
	local filterArgs = {}
    for _, arg in ipairs(args) do
    	local prefix = string.sub(arg, 1, 1)
    	if prefix == "~" then
    		-- ~x.y.z~a.b.c means obj.x.y.z == a or obj.x.y.z == b or obj.x.y.z == c
    		-- obj.x.y.z can be obj:x():y():z(), etc.
    		local ksvs = mw.text.split(string.sub(arg, 2), "%s*~%s*")
    		local ks = mw.text.split(ksvs[1], "%s*%.%s*")
    		local vs = mw.text.split(ksvs[2], "%s*,%s*")
    		table.insert(filterArgs, {ks, vs})
    	end
    end
    local actions = {}
    for _, arg in ipairs(args) do
    	local prefix = string.sub(arg, 1, 1)
    	if prefix == "@" then
    		local enumerator = string.sub(arg, 2)
	        local enumerating_function = enumerating_functions[enumerator]
	        if enumerating_function then
	        	sequence = {}
	        	for _, e in ipairs(enumerating_function()) do
	        		local ship
	        		if equipment then
	        			ship = Equipment(e)
	        		else
	        			ship = Ship(e)
	        		end
	        		if #filterArgs > 0 then
				    	local test = true
						for _, ksvs in ipairs(filterArgs) do
							local test2 = false

							local obj = ship
							for _, k in ipairs(ksvs[1]) do
								local g = obj[k]
								if type(g) == 'function' then
									obj = g(obj)
								elseif type(g) == 'table' then
									obj = g
								else
									obj = g
									break
								end
							end
							local v1 = tostring(obj)

							for _, v2 in ipairs(ksvs[2]) do
								if v1 == v2 then
									test2 = true
									break
								end
							end

							if not test2 then
								test = false
								break
							end
						end
						if test then
							table.insert(sequence, ship)
						end
	        		else
	        			table.insert(sequence, ship)
	        		end
	        	end
	        	sequence_position = 1
				sequence_length = #sequence
	        end
	    elseif prefix == '!' then
	    	local name = string.sub(arg, 2)
	    	local obj = Ship(name)
	    	if not obj:hp() then
	    		equipment = true
	    		obj = Equipment(name)
	    	end
	    	sequence = {}
	    	table.insert(sequence, obj)
	        sequence_position = 1
			sequence_length = 1
	    elseif arg == "#" or prefix == "?" then
	    	table.insert(actions, string.sub(arg, 2))
	    end
    end
    if args.sort and sequence then
    	local sorts = mw.text.split(args.sort, "%s*,%s*")
    	table.sort(sequence, function(a, b)
    		for _, sort in ipairs(sorts) do
  				local x = a[sort](a)
   				local y = b[sort](b)
   				if x == nil then return true end
   				if y == nil then return false end
   				if x < y then return true end
   				if x > y then return false end
    		end
			return false
		end)
    end
    local values = {}
    repeat
    	if sequence then
    		ship = sequence[sequence_position]
    		sequence_position = sequence_position + 1
    	end
        for _, arg in ipairs(actions) do
        	if arg == '' then
        		if args.format == "table" then
        			table.insert(values, "|-\n")
        		end
        	else
            	local value = ship and format_arg(format_value(arg, ship)) -- interpret_arg(arg)
            	if value then
	                table.insert(values, value)
            	end
            end
        end
    until not sequence_length or sequence_position > sequence_length
    return table.concat(values, args.concat or (args.format == "table" and "" or ", "))
end

local function test()
	 mw.log(interpret({"@base_names", "~is_battleship~true", "?link", sort = "type,id"}))
	 mw.log(interpret({"@base_names", "~class.name~Kagerou Class", "?link", sort = "type,id"}))
	 mw.log(interpret({"!Saiun", "?icon"}))
	 mw.log(interpret({"!Ayanami", "?scrap_string", "?scrap.fuel", "?scrap_fuel", "?_scrap_fuel"}))
end

-- print(p.test())
return {
	format = function(frame) return interpret(U.getTemplateArgs(frame).explicit) end,
	test = test,
}