Documentation for this module may be created at Module:Calc/doc
local Utils = require("Module:Utils")
local format = require("Module:StringOperations").format
local Formatting = require("Module:Formatting")
local Equipment = require("Module:Equipment")
local Ship = require("Module:Ship")
local ShipIterator = require("Module:ShipIterator")
local ShipCapabilities = require("Module:ShipCapabilities")
local ShipBattleCardKai = require("Module:ShipBattleCardKai")
local Combat2 = require("Module:Combat2")
local args = nil
local ship = nil
local target = nil
local shipCapabilities = {}
local frame = nil
local debug = {}
function debugLog(s)
table.insert(debug, s)
end
function debugString()
return #debug > 0 and args.debug and ([[<pre>]] .. table.concat(debug, "\n") .. [[</pre>]]) or ""
end
local enumerating_functions = {
args = function()
return mw.text.split(args.args, "%s*,%s*")
end,
base_names = function()
return ShipIterator.baseForms
end,
all_names = function()
return ShipIterator.allForms
end,
enemy = function()
return ShipIterator.enemyForms
end,
}
-- TODO: have a syntax for calling Module:Formatting functions?
local formatting_functions = {
night_battle_power = function(ship)
return (ship:firepower_max() or 0) + (ship:torpedo_max() or 0)
end,
code = function(ship)
return Formatting:format_ship_code(ship:type())
end,
-- overrides ShipData:type
type = function(ship)
return Formatting:format_ship_type(ship:type())
end,
-- overrides ShipData:link
link = function(ship)
return Formatting:format_link(ship:link())
end,
-- overrides ShipData:implementation_date
implementation_date = function(ship)
local date = ship:implementation_date()
return not date and "??" or format{
"${year}/${month}/${day}",
year = date[1],
month = date[2] < 10 and "0" .. date[2] or date[2],
day = date[3] < 10 and "0" .. date[3] or date[3],
}
end,
-- overrides ShipData:card
card = function(ship)
return ShipBattleCardKai:Asset({ ship: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 = Combat2.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 = Combat2.critical_hit_rate(ship, target)
if r then
ship.critical_hit_rate = r
return r .. "%"
else
return "nil"
end
end,
}
function addFormattingFunctions(name, table)
for k, v in pairs(table) do
formatting_functions[name .. "." .. k] = v
end
end
local Plugins = require("Module:Calc/Plugins")
for _, name in ipairs(Plugins) do
local data = require("Module:Calc/Plugins/" .. name)
addFormattingFunctions(name, data)
end
function format_lua(lua)
if type(lua) == "table" then
return tostring(table.concat(lua, args.concat_value or ", "))
else
return tostring(lua)
end
end
function format_value(key, ship, target)
local formatting_function = formatting_functions[key]
if formatting_function then
return formatting_function(ship, target)
else
if shipCapabilities[key] then
local a, b = shipCapabilities[key](shipCapabilities)
return format_lua(b or a)
elseif ship then
local lua = ship[key]
if type(lua) == "function" then
return format_lua(lua(ship))
else
return format_lua(lua)
end
end
end
end
local sequence = nil
local sequence_position = nil
function format_arg(arg)
return args.format == "table" and ("|" .. arg .. "\n") or arg
end
function interpret_setter(s)
local kv = mw.text.split(s, "%s*~%s*")
local k = kv[1]
local v = kv[2]
if k and v then
if k == "_equipment" then
local equipment = {}
local accuracy = 0
for eq in string.gmatch(v, '([^,]+)') do
local eqObj = Equipment(eq)
table.insert(equipment, { equipment = eqObj })
accuracy = accuracy + (eqObj:shelling_accuracy() or 0)
end
ship._equipment = equipment
ship._accuracy = accuracy
else
if v ~= "nil" then
ship[k] = tonumber(v)
end
end
return true
else
return false
end
end
function interpret_arg(arg)
local prefix = string.sub(arg, 1, 1)
local prefix2 = string.sub(arg, 1, 2)
if arg == "" then
return
elseif arg == "-" then
return format_arg("")
elseif prefix == "@" then
local enumerator = string.sub(arg, 2)
local enumerating_function = enumerating_functions[enumerator]
if enumerating_function and not sequence then
sequence = enumerating_function()
sequence_position = 1
end
elseif arg == "!@" then
local ship_key = sequence[sequence_position]
sequence_position = sequence_position + 1
ship = Ship(ship_key)
shipCapabilities = ShipCapabilities{ ship = ship }
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)
shipCapabilities = ShipCapabilities{ ship = ship }
else
interpret_setter(part)
end
i = i + 1
end
end
elseif prefix == "#" and args.format == "table" then
return "|-\n"
elseif 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 frame:preprocess(format{ '{{${template}|${ship}}}', template = string.sub(arg, 3), ship = ship:name('/') })
else
return format_arg(format_value(string.sub(arg, 2), ship, target))
end
else
return format_arg(arg)
end
end
local special_functions = {
apply = function(args_)
local frame_ = frame:getParent()
local template = args_[2]
local values = {}
for i = 3, #args_ do
table.insert(values, format{
'{{${template}|${arg}}}',
template = template,
arg = args_[i]
})
end
return frame_:preprocess(table.concat(values, args.concat or ""))
end
}
function interpret_args(args_)
args = args_
local special_function = special_functions[args[1]]
if special_function then
return special_function(args)
else
local values = {}
debugLog("#args = " .. #args)
repeat
for _, arg in ipairs(args_) do
debugLog("arg = " .. (arg or ""))
local value = interpret_arg(arg)
if value then
debugLog("value = " .. (value or ""))
table.insert(values, value)
end
end
until not sequence or sequence_position > #sequence
return table.concat(values, args.concat or "") .. debugString()
end
end
local Calc = {}
function Calc.format(frame_, args_)
frame = frame_
return interpret_args(args_ or Utils.getTemplateArgs(frame_).explicit)
end
function Calc.test()
mw.log(
Calc.format(nil, {
format = "table",
"!Nagato/Kai Ni",
"!_equipment ~ 41cm Triple Gun Mount Kai, 41cm Triple Gun Mount Kai, Type 13 Air Radar Kai",
"!_level ~ 99",
"!_luck ~ nil",
"!_morale ~ 25",
"!!Destroyer Ro-Class",
"!cl0 ~ 56",
"!cl1 ~ 807",
"!cl2 ~ 137",
"?card",
"?level",
"?format_morale",
"?accuracy",
"?luck",
"??card",
"?cl0",
"?cl1",
"?cl2",
"",
"-",
"?FitData.hit",
"?FitData.error",
"?FitData.crit",
"?hit_rate",
"?critical_hit_rate",
"?FitData.difference",
"?FitData.critical_difference",
"?FitData.accuracy_value",
"?FitData.fit",
})
)
end
return Calc