Changes

redo and add NB damage
Line 1: Line 1:  +
-- **
 +
-- Damage calculations are based on http://kancollecalc.web.fc2.com/damage_formula.html and WikiWiki combat page.
 +
-- NB CI rate formula is from WikiWiki combat page (also, 検証、仮説スレ18).
 +
--
    
local Combat = {}
 
local Combat = {}
   −
-- * Damage calculator.
+
-- * Experimental values.
--
  −
-- TBD
  −
--
  −
--  http://kancollecalc.web.fc2.com/damage_formula.html
  −
--  http://kancollecalc.web.fc2.com/damage.js
  −
--
  −
--  http://wikiwiki.jp/kancolle/?%C0%EF%C6%AE%A4%CB%A4%C4%A4%A4%A4%C6
  −
--  http://wikiwiki.jp/kancolle/?%BB%D9%B1%E7%B4%CF%C2%E2
  −
--
     −
local engagement_modifiers = {
+
Combat.modifier = {
green_t = 1.2,
  −
parallel = 1.0,
  −
head_on = 0.8,
  −
red_t = 0.6
  −
}
     −
local spotting_modifiers = {
+
comined_firepower = {
cut_in = {
+
ctf_first = 0,
main_second = 1.1,
+
ctf_second = 10,
main_radar = 1.2,
+
stf_first = 10,
main_ap = 1.3,
+
stf_second = -5,
main_main = 1.5,
   
},
 
},
double = 1.2
  −
}
     −
local comined_fp = {
+
comined_torpedo = -5,
ctf_first = 0,
  −
ctf_second = 10,
  −
stf_first = 10,
  −
stf_second = -5
  −
}
     −
local comined_torp = -5
+
night_contact = 5,
   −
local formation_modifiers = {
+
formation = {
line_ahead = 1.0,
+
line_ahead = 1.0,
double_line = 0.8,
+
double_line = 0.8,
diamond = 0.7,
  −
echelon = 0.6,
  −
line_abreast = 0.6,
  −
combined = {
  −
line_ahead = 1.1,
   
diamond = 0.7,
 
diamond = 0.7,
double_line = 1.0,
+
echelon = 0.6,
line_abreast = 0.8,
+
line_abreast = 0.6,
}
+
combined = {
}
+
line_ahead = 1.1,
 +
diamond = 0.7,
 +
double_line = 1.0,
 +
line_abreast = 0.8,
 +
},
 +
},
 +
 
 +
engagement = {
 +
green_t = 1.2,
 +
parallel = 1.0,
 +
head_on = 0.8,
 +
red_t = 0.6,
 +
},
 +
 
 +
health = {
 +
normal = 1.0,
 +
chuuha = 0.7,
 +
taiha = 0.4,
 +
},
   −
local health_modifiers = {
+
anti_ground = 2.5,
normal = 1.0,
+
wg_bonus = 75,
chuuha = 0.7,
  −
taiha = 0.4
  −
}
     −
function ammo_modifier(ammo)
+
cap = {
return ammo >= 5 and 1 or ammo / 5
+
day = 150,
end
+
night = 300,
 +
},
   −
local ap_modifiers = {
+
spotting = {
main_ap = 1.08,
+
cut_in = {
main_ap_radar = 1.1,
+
main_main = 1.5,
main_second_ap = 1.15,
+
main_ap = 1.3,
main_second_ap_radar = 1.15
+
main_radar = 1.2,
}
+
main_second = 1.1,
 +
},
 +
double = 1.2,
 +
},
   −
local critical_modifier = 1.5
+
night_attack = {
 +
cut_in = {
 +
main = 2,
 +
main_misc = 1.75,
 +
torpedo = 1.5,
 +
mixed = 1.3,
 +
},
 +
double = 1.2,
 +
normal = 1,
 +
},
   −
local anti_ground_modifier = 2.5
+
critical = 1.5,
local anti_ground_wg_bonus = 75
     −
local db_cap = 150
+
ap = {
local nb_cap = 300
+
main_ap = 1.08,
 +
main_ap_radar = 1.1,
 +
main_second_ap = 1.15,
 +
main_second_ap_radar = 1.15,
 +
},
   −
local equip_coeffs = {
  −
small_gun = 1,
  −
medium_gun = 1,
  −
main_gun = 1.5,
  −
ap = 1,
  −
torpedo = 1.2,
   
}
 
}
   −
function default_modifiers(critical)
+
function Combat.ammo_modifier(ammo_bars)
return {
+
return ammo_bars >= 5 and 1 or ammo_bars / 5
combined_fp = 0,
  −
combined_torp = 0,
  −
anti_ground_bonus = 0,
  −
engagement = engagement_modifiers.parallel,
  −
formation = formation_modifiers.line_ahead,
  −
health = health_modifiers.normal,
  −
anti_ground = 1,
  −
anti_sub = 1,
  −
night = 1,
  −
cap = db_cap,
  −
spotting = 1,
  −
contact = 1,
  −
expert = 1,
  −
critical = critical and critical_modifier or 1,
  −
ap = 1,
  −
ammo = 1,
  −
}
   
end
 
end
   −
function shelling_attack_power(fp, equip_bonus, modifiers)
+
-- * Combat object with ship-independent values as fields.
return 5 + fp + equip_bonus + modifiers.combined_fp + modifiers.anti_ground_bonus
+
--  Ship and enemy values are passed via arguments.
end
     −
function torpedo_attack_power(torp, equip_bonus, modifiers)
+
function Combat:new(is_night_battle)
return 5 + torp + equip_bonus + modifiers.combined_torp
+
local stage = {
 +
combined_firepower = 0,
 +
combined_torpedo = 0,
 +
formation = self.modifier.formation.line_ahead,
 +
engagement = self.modifier.engagement.parallel,
 +
night_contact = 0,
 +
is_night_battle = is_night_battle,
 +
cap = is_night_battle and self.modifier.cap.night or self.modifier.cap.day,
 +
contact = 1, -- TODO
 +
expert = 1, -- TODO
 +
}
 +
setmetatable(stage, self)
 +
self.__index = self
 +
return stage
 
end
 
end
   −
function damage_pre_cap(attack_power, modifiers)
+
function Combat:damage_pre_cap(ship)
return  
+
return self.engagement * self.formation * ship.health * ship.anti_ground * ship.anti_sub * ship.night_attack * ship.attack_power
modifiers.engagement * modifiers.formation * modifiers.health *
  −
modifiers.anti_ground * modifiers.anti_sub *
  −
modifiers.night *
  −
attack_power
   
end
 
end
   −
function damage_cap(attack_power, modifiers)
+
function Combat:damage_cap(attack_power)
if attack_power > modifiers.cap then
+
if attack_power > self.cap then
attack_power = modifiers.cap + math.sqrt(attack_power - modifiers.cap)
+
attack_power = self.cap + math.sqrt(attack_power - self.cap)
 
end
 
end
 
return math.floor(attack_power)
 
return math.floor(attack_power)
 
end
 
end
   −
function damage_post_cap(attack_power, modifiers, armor)
+
function Combat:damage_post_cap(attack_power, ship, enemy_armor)
attack_power =
+
attack_power = ship.spotting * self.contact * math.floor(self.expert * ship.critical * math.floor(ship.ap * attack_power))
modifiers.spotting * modifiers.contact *
+
-- Use a note on a caller side instead
math.floor(modifiers.expert * modifiers.critical *
+
--if ship.night_attack_x2 then
math.floor(modifiers.ap * attack_power))
+
-- attack_power = 2 * attack_power
if armor then
+
--end
local min_armor = armor * 0.7
+
if enemy_armor then
local max_armor = armor * 0.7 + math.max(armor - 1, 0) * 0.6
+
local min_armor = enemy_armor * 0.7
local min = math.floor((attack_power - max_armor) * modifiers.ammo)
+
local max_armor = enemy_armor * 0.7 + math.max(enemy_armor - 1, 0) * 0.6
local max = math.floor((attack_power - min_armor) * modifiers.ammo)
+
local min = math.floor((attack_power - max_armor) * ship.ammo)
 +
local max = math.floor((attack_power - min_armor) * ship.ammo)
 
return { min, max }
 
return { min, max }
 
else
 
else
return math.floor(attack_power * modifiers.ammo)
+
return math.floor(attack_power * ship.ammo)
 
end
 
end
 
end
 
end
   −
function damage(attack_power, modifiers, armor)
+
-- TODO: pass enemy table, do additional detection (e.g. torpedo = 0 vs. ground type enemies
return damage_post_cap(damage_cap(damage_pre_cap(attack_power, modifiers), modifiers), modifiers, armor)
+
-- or attack_power = 0 when BB vs. enemy submarines, etc.)
 +
function Combat:damage(ship, enemy_armor)
 +
return self:damage_post_cap(self:damage_cap(self:damage_pre_cap(ship)), ship, enemy_armor)
 
end
 
end
   −
-- * CI rate formula.
+
-- TODO: carrier shelling, aerial, ASW, support expedition.
   −
local cut_in_types = {
+
Combat.nb_cut_in_types = {
torpedo = { k = 70, cap = 60 },
+
torpedo = { k = 70, luck_cap = 60 },
mixed = { k = 70, cap = 70 },
+
mixed = { k = 70, luck_cap = 70 },
main = { k = 50, cap = 55 }
+
main = { k = 50, luck_cap = 55 },
 
}
 
}
   −
function cut_in_rate(luck, t)
+
function Combat.nb_cut_in_rate(ship, modifier)
if luck <= t.cap then
+
if ship._luck <= modifier.luck_cap then
return math.floor(math.sqrt(t.k * luck))
+
return math.floor(math.sqrt(modifier.k * ship._luck))
 
else
 
else
return cut_in_rate(t.cap, t)
+
return Combat.nb_cut_in_rate(modifier.luck_cap, modifier)
 
end
 
end
 
end
 
end
   −
-- * Tables.
+
function Combat:shelling(ship)
 +
ship.attack_power = 5 + ship._firepower_max + ship.equipment_bonus.firepower + self.combined_firepower + ship.anti_ground_bonus
 +
return ship
 +
end
   −
local format = require('Module:StringInterpolation').format
+
function Combat:torpedo(ship)
local Ship = require('Module:Ship')
+
ship.attack_power = 5 + ship._torpedo_max + ship.equipment_bonus.torpedo + self.combined_torpedo
local Formatting = require('Module:Formatting')
+
return ship
local StatIcons = require('Module:StatIcons')
+
end
   −
local templates = {
+
function Combat:night_battle(ship)
 
+
if ship.health == self.modifier.health.taiha then
main = [[{| class="wikitable sortable typography-xl-optout" style="width:100%;"
+
ship.attack_power = 0
! colspan="2" |
+
else
! colspan="4" |Day Battle
+
ship.attack_power = self.night_contact + ship._firepower_max + ship._torpedo_max + ship.equipment_bonus.firepower + ship.equipment_bonus.torpedo
! colspan="5" |Night Battle
+
end
|-
+
return ship
!Rank
  −
!Name
  −
!${fp}
  −
!${torp}
  −
!<span title="Damage: normal attack without equipment, ${db_attack_note} with maxed equipment (all other modifiers = 1, see notes)">Hit</span>
  −
!<span title="Normal torpedo salvo damage, all modifiers = 1, see notes">Salvo</span>
  −
!${fp_plus_torp}
  −
!<span title="Maximal damage from double attack (combined)">DA</span>
  −
!<span title="Maximal damage from cut-in">CI</span>
  −
!${luck_minus_cap}
  −
!<span title="Torpedo/mixed cut-in rate, gun cut-in rate (only luck dependent part, no bonuses)">CI%</span>
  −
${rows}|}]],
  −
 
  −
    carrier = [[{| class="wikitable sortable typography-xl-optout" style="width:100%;"
  −
!Rank
  −
!Name
  −
!${fp}
  −
!<span title=""></span>
  −
!<span title=""></span>
  −
${rows}|}]],
  −
 
  −
main_row = [[|-
  −
|${rank}
  −
|${name}
  −
|${fp}
  −
|${torp}
  −
|${db_attack}
  −
|${db_torp}
  −
|${fp_plus_torp}
  −
|${nb_da}
  −
|${nb_ci}
  −
|${luck_minus_cap}
  −
|${nb_ci_rate}
  −
]],
  −
 
  −
    carrier_row = [[|-
  −
|${rank}
  −
|${name}
  −
|${fp}
  −
|
  −
|
  −
]],
  −
 
  −
}
  −
 
  −
local map_types = {
  −
    dd = "main", cl = "main", clt = "main", ca = "main", bb = "main",
  −
    cvl = "carrier", cv = "carrier",
  −
}
  −
 
  −
function Combat.table(frame)
  −
    local type = frame.args["type"]
  −
    local db_attack_notes = {
  −
        dd = "normal attack",
  −
        cl = "double attack",
  −
        clt = "normal attack",
  −
        ca = "double attack",
  −
    }
  −
return format{
  −
templates[map_types[type]],
  −
rows = frame.args[1] or "",
  −
db_attack_note = db_attack_notes[type] or "?",
  −
fp = Formatting:format_image{StatIcons.firepower, caption = Formatting:format_stat_name("firepower")},
  −
torp = Formatting:format_image{StatIcons.torpedo, caption = Formatting:format_stat_name("torpedo")},
  −
fp_plus_torp =
  −
Formatting:format_image{StatIcons.firepower, caption = Formatting:format_stat_name("firepower")}
  −
.. "+" ..
  −
Formatting:format_image{StatIcons.torpedo, caption = Formatting:format_stat_name("torpedo")},
  −
luck_minus_cap =
  −
    '<span title="Luck cap">60</span>−' ..
  −
Formatting:format_image{StatIcons.luck, caption = Formatting:format_stat_name("luck")}
  −
}
   
end
 
end
   −
function get_db_attack_string(fp, t)
+
-- * Ship object with extra fields related to combat.
    local modifiers = default_modifiers(false)
  −
    local equip_fp = {
  −
        dd = 2 * 3 + math.floor(2 * math.sqrt(10)),
  −
        cl = 2 * 10 + math.floor(2 * math.sqrt(10)),
  −
        clt = 2 * 8 + math.floor(2 * math.sqrt(10)),
  −
        ca = 2 * 10 + math.floor(2 * math.sqrt(10)) + 3,
  −
    }
  −
    local normal = damage(shelling_attack_power(fp, 0, modifiers), modifiers)
  −
    if t == "cl" or t == "ca" then
  −
        modifiers.spotting = spotting_modifiers.double
  −
    end
  −
    local special = damage(shelling_attack_power(fp, equip_fp[t] or 0, modifiers), modifiers)
  −
    return normal .. ", " .. special
  −
end
     −
function Combat.table_row(frame)
+
local Ship = {}
   −
local rank = frame.args[1]
+
function Ship:new(ship, equip, night_attack, spotting)
local ship_key = frame.args[2]
  −
local typ = frame.args["type"]
  −
local note = frame.args["note"]
     −
local name, suffix = Ship:process_ship_key(ship_key)
+
self._firepower_max = ship._firepower_max or 0
local ship_table = Ship:get_table(name, suffix)
+
self._torpedo_max = ship._torpedo_max or 0
 +
self._luck = ship._luck or 0
   −
if rank and typ and name and ship_table and ship_table._type then
+
self.equipment = equip or {}
    local fp = ship_table._firepower_max
+
self.equipment_bonus = { firepower = 0, torpedo = 0 }
    local torp = ship_table._torpedo_max or 0
+
for _, eq in pairs(self.equipment) do
    local luck = ship_table._luck
+
if eq.firepower then
local luck_diff = 60 - luck
+
self.equipment_bonus.firepower = self.equipment_bonus.firepower + eq.firepower + (eq.k and eq.rank and eq.k * math.sqrt(eq.rank) or 0)
return format{
+
end
templates[map_types[typ] .. "_row"],
+
if eq.torpedo then
rank = rank,
+
self.equipment_bonus.torpedo = self.equipment_bonus.torpedo + eq.torpedo + (eq.k and eq.rank and eq.k * math.sqrt(eq.rank) or 0)
name =
+
end
    note and format{'[[${name}|<span title="${note}">${name}</span>]]<sup>?</sup>', name = name, note = note} or
  −
    string.format("[[%s]]", name),
  −
fp = fp,
  −
torp = torp,
  −
fp_plus_torp = fp + torp,
  −
db_attack = get_db_attack_string(fp, typ),
  −
db_torp = "",
  −
nb_da = "",
  −
nb_ci = "",
  −
luck_minus_cap = luck_diff < 0 and "−" .. -luck_diff or luck_diff,
  −
nb_ci_rate =
  −
    cut_in_rate(luck, cut_in_types.torpedo) .. "%, " ..
  −
    cut_in_rate(luck, cut_in_types.main) .. "%",
  −
}
  −
else
  −
return ""
   
end
 
end
 +
self.equipment_bonus.firepower = math.floor(self.equipment_bonus.firepower)
 +
self.equipment_bonus.torpedo = math.floor(self.equipment_bonus.torpedo)
 +
 +
self.health = ship.health or Combat.modifier.health.normal
 +
self.anti_ground = ship.anti_ground and Combat.modifier.anti_ground or 1
 +
self.anti_ground_bonus = ship.wg and Combat.modifier.wg_bonus or 0
 +
self.anti_sub = ship.anti_sub or 1
 +
self.night_attack = night_attack or 1
 +
self.night_attack_x2 = night_attack == Combat.modifier.night_attack.cut_in.torpedo or
 +
  night_attack == Combat.modifier.night_attack.cut_in.mixed
 +
self.spotting = spotting or 1
 +
self.critical = ship.critical or 1
 +
self.ap = ship.ap or 1
 +
self.ammo = ship.ammo or 1
 +
return self
    
end
 
end
  −
return Combat
 
cssedit, gkautomate
7,064

edits