• 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
(Enable Item test)
(another item attempt. doesn't work. revert if needed.)
 
(2 intermediate revisions by the same user not shown)
Line 10: Line 10:
 
local EquipmentGraphicKai = require("Module:EquipmentGraphicKai")
 
local EquipmentGraphicKai = require("Module:EquipmentGraphicKai")
 
local EquipmentCollection = require("Module:Data/Equipment")
 
local EquipmentCollection = require("Module:Data/Equipment")
 +
local ItemCollection = require('Module:Collection/Items')
 
-- local AllEquipmentCollection = require("Module:Collection/EquipmentByApiId")
 
-- local AllEquipmentCollection = require("Module:Collection/EquipmentByApiId")
 
local Development = require("Module:Development")
 
local Development = require("Module:Development")
Line 31: Line 32:
 
     base_names = function() return require('Module:Collection/ShipsBase') end,
 
     base_names = function() return require('Module:Collection/ShipsBase') end,
 
     all_names = function() return require('Module:Collection/Ships') end,
 
     all_names = function() return require('Module:Collection/Ships') end,
    all_names = function() return require('Module:Collection/Items') end,
 
    all_names = function() return require('Module:Collection/Items') end,
 
 
     remodel_names = function()
 
     remodel_names = function()
 
     local all_names = require('Module:Collection/Ships')
 
     local all_names = require('Module:Collection/Ships')
Line 48: Line 47:
 
     end,
 
     end,
 
     enemy = function() return require('Module:Collection/EnemyShips') end,
 
     enemy = function() return require('Module:Collection/EnemyShips') end,
    enemy = function() return require('Module:Collection/Items') end,
 
 
     equipment = function()
 
     equipment = function()
 
     equipment = true
 
     equipment = true
 
     return U.imap(EquipmentCollection, function(e) return e._name end)
 
     return U.imap(EquipmentCollection, function(e) return e._name end)
 +
    end,
 +
    item = function()
 +
    item = true
 +
    return U.imap(ItemCollection, function(e) return e._name end)
 
     end,
 
     end,
 
     --[[
 
     --[[
Line 419: Line 421:
 
        for _, e in ipairs(enumerating_function()) do
 
        for _, e in ipairs(enumerating_function()) do
 
        local ship
 
        local ship
        if equipment then
+
        if equipment or item then
 
        ship = Equipment(e)
 
        ship = Equipment(e)
 
        else
 
        else

Latest revision as of 15:05, 25 October 2025

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 ItemCollection = require('Module:Collection/Items')
-- 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,
    item = function()
    	item = true
    	return U.imap(ItemCollection, 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
    		if equipment then --attempted to stop equipment from being filtered out, as equip cards weren't showing up. Don't know if this only works for equip and if I broke the original funtion's intention. This may not work at filtering out errors -chocolatecravinghobo
    		else
    			return ' '
    		end
    	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 or item 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)
									if type(obj) == 'nil' then
										obj = '??'
										break
									end
								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"}))
	 mw.log(interpret({'@all_names', '~class.name~Akizuki Class', '?api_id', '?banner', '?link', '?japanese_name', '?code_link', '?class_number', '?firepower_max', '?torpedo_max', '?night_battle_power', '?aa_max', '?asw_max', '?los_max', '?luck', '?hp', '?armor_max', '?evasion_max', '?slots', '?fuel', '?ammo', sort = 'class_number,rarity,api_id'}))
end

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