Module:BTD6 stats: Difference between revisions

mNo edit summary
Pymonkibot (talk | contribs)
m Text replacement - "[[Bloons TD 6 v" to "[[Update history:Bloons TD 6/Version "
 
(83 intermediate revisions by 3 users not shown)
Line 1: Line 1:
-- This code is copyrighted and licensed under the Creative Commons Attribution-NonCommercial-ShareAlike International license, version 4.0. You may reuse and adapt this code for other purposes if:
-- * You give appropriate credit
-- * You are not using this material for commercial purposes
-- * You are releasing it under the same license
-- The subpages of this page are adapted from internal data in Bloons TD 6, which is copyrighted by Ninja Kiwi Limited. Its usage on Blooncyclopedia is believed to be fair use.
-- Further information: https://www.bloonswiki.com/Blooncyclopedia:Copyrights
-- By editing this code, you agree to release your changes under the same terms.
local p = {}
local p = {}
local my_data = {}
local my_data = {}


local template_names = {
local template_names = {
attacks = "BTD6 attack",
attacks ="BTD6 attack",
projectiles = "BTD6 projectile",
projectiles ="BTD6 projectile",
abilities = "BTD6 ability",
collectables="BTD6 collectable",
effects = "BTD6 effect",
abilities ="BTD6 ability",
zones = "BTD6 zone",
effects ="BTD6 effect",
subtowers = "BTD6 tower"
buffs ="BTD6 buff",
zones ="BTD6 zone",
subtowers ="BTD6 tower"
}
}


function parse_tower(frame, new_data, prev_data)
local function parse_beast_table(data, tiers)
local ret = {"\n{|class='wikitable mw-collapsible mw-collapsed'\n|+style='white-space:nowrap'|Beast Power-dependent stats"}
-- local functions to improve performance
local tconcat = table.concat
local sformat = string.format
local msqrt = math.sqrt
local mfloor = math.floor
-- -- -- -- -- --
-- CREATE HEADING
-- -- -- -- -- --
local headingUpper = {}
local upperColspans = {}
local headingLower = {}
local headingEffects = function(effects)
for i, v in ipairs(effects["_order"]) do
local effect = effects[v]
local amount = 0
if v == "Knockback" and effect.lifespan then headingLower[#headingLower+1] = "Lifespan" amount = amount + 1 end
if amount > 0 then
headingUpper[#headingUpper+1] = v
upperColspans[#upperColspans+1] = amount
end
end
end
local headingProjectiles = function(projectiles)
for i, v in ipairs(projectiles["_order"]) do
local projectile = projectiles[v]
local amount = 0
if projectile["maxPierce"]<1 and projectile["pierce"]<99999 then headingLower[#headingLower+1] = "Pierce" amount = amount + 1 end
if projectile["damage"]~=nil and projectile["damage"]>0 then headingLower[#headingLower+1] = "Damage" amount = amount + 1 end
if projectile["damageModifierForStunned"]~=nil then headingLower[#headingLower+1] = "Damage to stunned" amount = amount + 1 end
if amount > 0 then
headingUpper[#headingUpper+1] = v
upperColspans[#upperColspans+1] = amount
end
end
end
local headingAttacks = function(attacks)
for i, v in ipairs(attacks["_order"]) do
local attack = attacks[v]
local amount = 0
if attack.rate and attack.rate < 9999 and not attack.rateMin then headingLower[#headingLower+1] = "Cooldown" amount = amount + 1 end
if attack.initialDamage and attack.initialDamage > 0 then headingLower[#headingLower+1] = "Scratch damage" amount = amount + 1 end
if attack.grapplingDamage and attack.grapplingDamage > 0 then headingLower[#headingLower+1] = "Grappling damage" amount = amount + 1 end
if attack.thrashingProjectileRate then headingLower[#headingLower+1] = "Thrash cooldown" amount = amount + 1 end
if amount > 0 then
headingUpper[#headingUpper+1] = v
upperColspans[#upperColspans+1] = amount
end
-- projectiles
if attack.projectiles then headingProjectiles(attack.projectiles) end
end
end
local headingAbilities = function(abils)
for i, v in ipairs(abils["_order"]) do
local abil = abils[v]
local amount = 0
if abil.cooldown then headingLower[#headingLower+1] = "Cooldown" amount = amount + 1 end
if amount > 0 then
headingUpper[#headingUpper+1] = v
upperColspans[#upperColspans+1] = amount
end
-- projectiles
if abil.projectiles then headingProjectiles(abil.projectiles) end
end
end
local minPower = {
["_002"] = 3,
["_003"] = 8,
["_004"] = 16,
["_005"] = 36,
["_020"] = 3,
["_030"] = 8,
["_040"] = 16,
["_050"] = 36,
["_200"] = 3,
["_300"] = 8,
["_400"] = 16,
["_500"] = 36
}
local maxPower = {
["_200"] = 6,
["_300"] = 24,
["_400"] = 64,
["_500"] = 132,
["_020"] = 6,
["_030"] = 24,
["_040"] = 64,
["_050"] = 132,
["_002"] = 6,
["_003"] = 24,
["_004"] = 64,
["_005"] = 132
}
-- insert headings
if data.speedRangeGyrfalcon > 0 then
headingLower[#headingLower+1] = "Flight speed"
headingUpper[#headingUpper+1]= "Beast"
upperColspans[#upperColspans+1] = 1
end
if data.attacks then headingAttacks(data.attacks) end
if data.abilities then headingAbilities(data.abilities) end
ret[#ret+1] = "!rowspan=2|Power!!rowspan=2|Scale"
for i, v in ipairs(headingUpper) do
ret[#ret+1] = sformat("!colspan=%i|%s", upperColspans[i], v)
end
ret[#ret+1] = "|-\n!" .. table.concat(headingLower, "!!")
-- -- -- -- -- -- --
-- CALCULATE STATS
-- -- -- -- -- -- --
-- current scale
local scale
-- attack cooldown
local insertRate = function(amt)
ret[#ret+1] = sformat("| %.4gs", amt - (data.cooldownScaleRange*scale))
end
-- thrash cooldown
local insertThrashRate = function(amt, thrashRate)
ret[#ret+1] = sformat("| %.4gs", (1 - data.cooldownScaleRange*scale/amt) * thrashRate)
end
-- ability cooldown
local insertAbilityCooldown = function(amt, mainAttackRate)
ret[#ret+1] = sformat("|%.4gs", amt * (1 - (data.cooldownScaleRange*scale/(mainAttackRate - (data.cooldownScaleRange*scale)))))
end
-- damage
local insertDamage = function(amt)
ret[#ret+1] = sformat("|%i", amt + mfloor(data.damageRange*scale))
end
-- dino stun damage
local insertStunDamage = function(amt)
ret[#ret+1] = sformat("|%i", amt + mfloor(data.damageRange*scale/data.stunBonusDivideMicroraptor))
end
-- pierce
local insertPierce = function(amt)
ret[#ret+1] = sformat("|%i", amt + mfloor(data.pierceRange*scale))
end
-- thrash knockback duration
local insertKnockback = function(amt)
ret[#ret+1] = sformat("|%.4gs", amt + (data.thrashKnockbackLifetimeRange*scale))
end
-- bird flight speed
local insertGyrfalconSpeed = function()
ret[#ret+1] = sformat("|%.4g units/s", data.speed + (data.speedRangeGyrfalcon*scale))
end
local parseEffects = function(effects)
for i, v in ipairs(effects["_order"]) do
local effect = effects[v]
if v == "Knockback" and effect.duration then insertKnockback(effect.lifespan) end
end
end
local parseProjectiles = function(projectiles)
for i, v in ipairs(projectiles["_order"]) do
local projectile = projectiles[v]
if projectile.maxPierce < 1 and projectile.pierce < 99999 then insertPierce(projectile.pierce) end
if projectile.damage and projectile.damage > 0 then insertDamage(projectile.damage) end
if projectile.damageModifierForStunned then insertStunDamage(projectile.damageModifierForStunned) end
if projectile.effects then parseEffects(projectile.effects) end
end
end
local parseAttacks = function(attacks)
for i, v in ipairs(attacks["_order"]) do
local attack = attacks[v]
if attack.rate and attack.rate < 9999 and not attack.rateMin then insertRate(attack.rate) end
if attack.initialDamage and attack.initialDamage > 0 then insertDamage(attack.initialDamage) end
if attack.grapplingDamage and attack.grapplingDamage > 0 then insertDamage(attack.initialDamage) end
if attack.thrashingProjectileRate then insertThrashRate(attack.rate, attack.thrashingProjectileRate) end
-- projectiles
if attack.projectiles then parseProjectiles(attack.projectiles) end
end
end
local parseAbilities = function(abils)
for i, v in ipairs(abils["_order"]) do
local abil = abils[v]
local attackRate = data.attacks["Attack"].rate
-- rate
insertAbilityCooldown(abil.cooldown, attackRate)
if abil.projectiles ~= nil then parseProjectiles(abil.projectiles) end
end
end
 
for i = minPower[tiers], maxPower[tiers] do
scale = ((i - minPower[tiers]) / (maxPower[tiers] - minPower[tiers]))
ret[#ret+1] = sformat("|-\n!%i\n|%.4g%%", i, scale*100)
-- calculations
if data.speedRangeGyrfalcon > 0 then insertGyrfalconSpeed() end
if data.attacks then parseAttacks(data.attacks) end
if data.abilities then parseAbilities(data.abilities) end
end
ret[#ret+1] = "|}"
return table.concat(ret, "\n")
end
 
function parse_paragon_table(data)
local ret = {"\n{|class='wikitable mw-collapsible mw-collapsed'\n|+style='white-space:nowrap'|Degree-dependent stats"}
-- local functions to improve performance
local tinsert = table.insert
local tconcat = table.concat
local sformat = string.format
local msqrt = math.sqrt
local mfloor = math.floor
local degree_requirements = {
0,      2000,  2324,  2666,  3027,  3408,  3808,  4228,  4669,  5131,
5615,  6121,  6650,  7203,  7779,  8379,  9004,  9654,  10330,  11032,
11761,  12518,  13302,  14114,  14955,  15825,  16725,  17655,  18616,  19609,
20633,  21689,  22778,  23900,  25056,  26246,  27471,  28732,  30028,  31360,
32729,  34135,  35579,  37061,  38582,  40143,  41743,  43383,  45064,  46786,
48550,  50356,  52205,  54098,  56034,  58014,  60039,  62109,  64225,  66387,
68596,  70853,  73157,  75509,  77910,  80360,  82860,  85410,  88011,  90664,
93368,  96124,  98933,  101795, 104711, 107681, 110706, 113787, 116923, 120115,
123364, 126670, 130034, 133456, 136937, 140478, 144078, 147738, 151459, 155241,
159085, 162991, 166960, 170993, 175089, 179249, 183474, 187764, 192120, 200000
}
-- -- -- -- -- --
-- CREATE HEADING
-- -- -- -- -- --
local heading_upper = {}
local upper_colspans = {}
local heading_lower = {}
local heading_effects = function(effects)
for i, v in ipairs(effects["_order"]) do
local effect = effects[v]
local amount = 0
if effect["damage"]~=nil then tinsert(heading_lower, "Damage") amount = amount + 1 end
if effect["damageModifierForBoss"]~=nil then tinsert(heading_lower, "Damage to bosses") amount = amount + 1 end
if effect["damageModifierForMoabs"]~=nil then tinsert(heading_lower, "Damage to MOAB-Class") amount = amount + 1 end
if effect["damageModifierForCeramic"]~=nil then tinsert(heading_lower, "Damage to Ceramic") amount = amount + 1 end
if effect["damageModifierForCamo"]~=nil then tinsert(heading_lower, "Damage to Camo") amount = amount + 1 end
if effect["damageModifierForStunned"]~=nil then tinsert(heading_lower, "Damage to stunned") amount = amount + 1 end
if effect["damageModifierForStickied"]~=nil then tinsert(heading_lower, "Damage to stickied") amount = amount + 1 end
if amount > 0 then
tinsert(heading_upper, v)
tinsert(upper_colspans, amount)
end
end
end
local heading_projectiles = function(projectiles)
for i, v in ipairs(projectiles["_order"]) do
local projectile = projectiles[v]
local amount = 0
if projectile["maxPierce"]<1 and projectile["pierce"]<99999 then tinsert(heading_lower, "Pierce") amount = amount + 1 end
if projectile["damage"]~=nil and projectile["damage"]>0 then tinsert(heading_lower, "Damage") amount = amount + 1 end
if projectile["damageModifierForBoss"]~=nil then tinsert(heading_lower, "Damage to bosses") amount = amount + 1 end
if projectile["damageModifierForMoabs"]~=nil then tinsert(heading_lower, "Damage to MOAB-Class") amount = amount + 1 end
if projectile["damageModifierForCeramic"]~=nil then tinsert(heading_lower, "Damage to Ceramic") amount = amount + 1 end
if projectile["damageModifierForCamo"]~=nil then tinsert(heading_lower, "Damage to Camo") amount = amount + 1 end
if projectile["damageModifierForStunned"]~=nil then tinsert(heading_lower, "Damage to stunned") amount = amount + 1 end
if projectile["damageModifierForStickied"]~=nil then tinsert(heading_lower, "Damage to stickied") amount = amount + 1 end
if amount > 0 then
tinsert(heading_upper, v)
tinsert(upper_colspans, amount)
end
if projectile["effects"] ~= nil then heading_effects(projectile["effects"]) end
end
end
local heading_attacks = function(attacks)
for i, v in ipairs(attacks["_order"]) do
local attack = attacks[v]
-- rate
if attack["rate"] ~= nil and attack["rate"] < 9999 and attack["rateMin"] == nil then
tinsert(heading_lower, "Cooldown")
tinsert(heading_upper, v)
tinsert(upper_colspans, 1)
end
-- projectiles
if attack["projectiles"] ~= nil then heading_projectiles(attack["projectiles"]) end
end
end
local heading_abilities = function(abils)
for i, v in ipairs(abils["_order"]) do
local abil = abils[v]
-- rate
tinsert(heading_lower, "Cooldown")
tinsert(heading_upper, v)
tinsert(upper_colspans, 1)
if abil["projectiles"] ~= nil then heading_projectiles(abil["projectiles"]) end
if abil["attacks"] ~= nil then heading_attacks(abil["attacks"]) end
end
end
-- insert headings
if data["projectiles"] ~= nil then heading_attacks(data["projectiles"]) end
if data["attacks"] ~= nil then heading_attacks(data["attacks"]) end
if data["abilities"] ~= nil then heading_abilities(data["abilities"]) end
if data["zones"] ~= nil then heading_effects(data["zones"]) end
tinsert(ret, "!rowspan=2|Degree\n!rowspan=2|Power\n!rowspan=2|Boss multiplier")
for i, v in ipairs(heading_upper) do
tinsert(ret, sformat("!colspan=%i|%s", upper_colspans[i], v))
end
tinsert(ret, "|-\n!" .. table.concat(heading_lower, "!!"))
-- -- -- -- -- -- --
-- CALCULATE STATS
-- -- -- -- -- -- --
-- current degree
local d = 1
-- attack cooldowns and ability cooldowns
local insert_rate = function(amt)
tinsert(ret, sformat("|%.4gs", amt / (1 + (0.01 * msqrt((d-1)*50)))))
end
-- damage
local insert_damage = function(amt)
if d == 100 then tinsert(ret, sformat("|%.4g", amt * 2 + 10))
else tinsert(ret, sformat("|%.4g", amt * (1 + (d-1) * 0.01) + mfloor((d-1)/10))) end
end
-- pierce
local insert_pierce = function(amt)
if d == 100 then tinsert(ret, sformat("|%.4g", amt * 2 + 10))
else tinsert(ret, sformat("|%.4g", mfloor(amt * (1 + (d-1) * 0.01)) + (d-1)/10)) end
end
-- damage modifiers
local insert_damage_mod = function(amt)
if d == 100 then tinsert(ret, sformat("| +%.4g", amt * 2 + 10))
else tinsert(ret, sformat("| +%.4g", amt * (1 + (d-1) * 0.01))) end
end
local parse_effects = function(effects)
for i, v in ipairs(effects["_order"]) do
local effect = effects[v]
if effect["damage"]~=nil then insert_damage(effect["damage"]) end
if effect["damageModifierForBoss"]~=nil then insert_damage_mod(effect["damageModifierForBoss"]) end
if effect["damageModifierForMoabs"]~=nil then insert_damage_mod(effect["damageModifierForMoabs"]) end
if effect["damageModifierForCeramic"]~=nil then insert_damage_mod(effect["damageModifierForCeramic"]) end
if effect["damageModifierForCamo"]~=nil then insert_damage_mod(effect["damageModifierForCamo"]) end
if effect["damageModifierForStunned"]~=nil then insert_damage_mod(effect["damageModifierForStunned"]) end
if effect["damageModifierForStickied"]~=nil then insert_damage_mod(effect["damageModifierForStickied"]) end
end
end
local parse_projectiles = function(projectiles)
for i, v in ipairs(projectiles["_order"]) do
local projectile = projectiles[v]
if projectile["maxPierce"]<1 and projectile["pierce"]<99999 then insert_pierce(projectile["pierce"]) end
if projectile["damage"]~=nil and projectile["damage"]>0 then insert_damage(projectile["damage"]) end
if projectile["damageModifierForBoss"]~=nil then insert_damage_mod(projectile["damageModifierForBoss"]) end
if projectile["damageModifierForMoabs"]~=nil then insert_damage_mod(projectile["damageModifierForMoabs"]) end
if projectile["damageModifierForCeramic"]~=nil then insert_damage_mod(projectile["damageModifierForCeramic"]) end
if projectile["damageModifierForCamo"]~=nil then insert_damage_mod(projectile["damageModifierForCamo"]) end
if projectile["damageModifierForStunned"]~=nil then insert_damage_mod(projectile["damageModifierForStunned"]) end
if projectile["damageModifierForStickied"]~=nil then insert_damage_mod(projectile["damageModifierForStickied"]) end
if projectile["effects"] ~= nil then parse_effects(projectile["effects"]) end
end
end
local parse_attacks = function(attacks)
for i, v in ipairs(attacks["_order"]) do
local attack = attacks[v]
-- rate
if attack["rate"] ~= nil and attack["rate"] < 9999 and attack["rateMin"] == nil then
insert_rate(attack["rate"])
end
-- projectiles
if attack["projectiles"] ~= nil then parse_projectiles(attack["projectiles"]) end
end
end
local parse_abilities = function(abils)
for i, v in ipairs(abils["_order"]) do
local abil = abils[v]
-- rate
insert_rate(abil["cooldown"])
if abil["projectiles"] ~= nil then parse_projectiles(abil["projectiles"]) end
if abil["attacks"] ~= nil then parse_attacks(abil["attacks"]) end
end
end
-- degree loop
while d <= 100 do
-- new row
tinsert(ret, sformat("|-\n!%i\n|%i", d, degree_requirements[d]))
-- boss damage mult
if d < 20 then tinsert(ret, "|×1.0")
elseif d < 40 then tinsert(ret, "|×1.25")
elseif d < 60 then tinsert(ret, "|×1.5")
elseif d < 80 then tinsert(ret, "|×1.75")
elseif d ~= 100 then tinsert(ret, "|×2.0")
else tinsert(ret, "|×2.25") end
-- calculations
if data["projectiles"] ~= nil then parse_attacks(data["projectiles"]) end
if data["attacks"] ~= nil then parse_attacks(data["attacks"]) end
if data["abilities"] ~= nil then parse_abilities(data["abilities"]) end
if data["zones"] ~= nil then parse_effects(data["zones"]) end
d = d + 1
end
tinsert(ret, "|}")
return table.concat(ret, "\n")
end
 
function parse_tower_base(frame, data, header, paragon)
local template = {}
local template = {}
-- local functions to improve performance
local tinsert = table.insert
local tconcat = table.concat
local sformat = string.format
local ssub = string.sub
-- search through tables
for k, v in pairs(data) do
if type(v) == "table" and ssub(k, 1, 1) ~= "_" then
template[k] = {}
-- iterate through attacks/abilities/subtowers
for _, i in ipairs(v["_order"]) do
if k == "subtowers" then tinsert(template[k], sformat(header, i)) end
-- parse sub-table of table
local stats = parse_stats_base(frame, i, v[i], template_names[k])
if k == "subtowers" and paragon then stats = stats .. parse_paragon_table(v[i]) end
tinsert(template[k], stats)
end
template[k] = tconcat(template[k])
elseif type(v) ~= "table" then
template[k] = data[k]
end
end
if paragon then template["paragon stats"] = parse_paragon_table(data) end
return frame:expandTemplate{title = "BTD6 tower", args = template}
end
function parse_stats_base(frame, name, data, template_name)
local template = {name = name}
-- local functions to improve performance
-- local functions to improve performance
local tinsert = table.insert
local tinsert = table.insert
local tconcat = table.concat
local tconcat = table.concat
-- search through tables
for k, v in pairs(data) do
if type(v) == "table" then
template[k] = {}
-- iterate through attacks/abilities/subtowers
for _, i in ipairs(v["_order"]) do
-- parse sub-table of table
tinsert(template[k], parse_stats_base(frame, i, v[i], template_names[k]))
end
template[k] = tconcat(template[k])
else
template[k] = v
end
end
return frame:expandTemplate{title = template_name, args = template}
end
function parse_tower(frame, new_data, prev_data, header)
local template = {}
-- local functions to improve performance
local tinsert = table.insert
local tconcat = table.concat
local sformat = string.format
local ssub = string.sub
-- search through tables
-- search through tables
for k, v in pairs(new_data) do
for k, v in pairs(new_data) do
if tonumber(k) == nil then
if type(v) == "table" and ssub(k, 1, 1) ~= "_" then
template[k] = {}
template[k] = {}
if prev_data[k] == nil then prev_data[k] = {} end
if prev_data[k] == nil then prev_data[k] = {} end
-- iterate through attacks/abilities/subtowers
-- iterate through attacks/abilities/subtowers
for kk, vv in pairs(v) do
for _, i in ipairs(v["_order"]) do
if prev_data[k][kk] == nil then prev_data[k][kk] = {} end
if k == "subtowers" then tinsert(template[k], sformat(header, i)) end
if prev_data[k][i] == nil then prev_data[k][i] = {} end
-- parse sub-table of table
-- parse sub-table of table
tinsert(template[k], parse_stats(frame, kk, vv, prev_data[k][kk], template_names[k]))
local stats = parse_stats(frame, i, v[i], prev_data[k][i], template_names[k])
tinsert(template[k], stats)
end
end
Line 66: Line 610:
-- iterate through attacks/abilities/subtowers
-- iterate through attacks/abilities/subtowers
for kk, vv in pairs(v) do
for _, i in ipairs(v["_order"]) do
if prev_data[k][kk] == nil then prev_data[k][kk] = {} end
if prev_data[k][i] == nil then prev_data[k][i] = {} end
-- parse sub-table of table
-- parse sub-table of table
tinsert(template[k], parse_stats(frame, kk, vv, prev_data[k][kk], template_names[k]))
tinsert(template[k], parse_stats(frame, i, v[i], prev_data[k][i], template_names[k]))
end
end
Line 91: Line 635:
end
end


function parse_tower_full(frame, new_data, prev_data)
function parse_tower_full(frame, new_data, prev_data, header)
local template = {}
local template = {}
Line 97: Line 641:
local tinsert = table.insert
local tinsert = table.insert
local tconcat = table.concat
local tconcat = table.concat
local sformat = string.format
local ssub = string.sub
-- update prev_data first
-- search through tables
for k, v in pairs(new_data) do
for k, v in pairs(new_data) do
if k == "attacks" or k == "abilities" or k == "subtowers" or k == "zones" then
if type(v) == "table" and ssub(k, 1, 1) ~= "_" then
if template[k] == nil then template[k] = {} end
if prev_data[k] == nil then prev_data[k] = {} end
if prev_data[k] == nil then prev_data[k] = {} end
if prev_data[k]["_order"] == nil then
prev_data[k]["_order"] = {}
prev_data[k]["_keys"] = {}
end
-- iterate through attacks/abilities/subtowers
-- iterate through attacks/abilities/subtowers
for kk, vv in pairs(v) do
for _, i in ipairs(v["_order"]) do
if prev_data[k][kk] == nil then prev_data[k][kk] = {} end
if prev_data[k][i] == nil then prev_data[k][i] = {} end
if prev_data[k]["_keys"][i] == nil then
tinsert(prev_data[k]["_order"], i)
prev_data[k]["_keys"][i] = true
end
-- parse sub-table of table
-- parse sub-table of table
parse_stats_full(frame, kk, vv, prev_data[k][kk], template_names[k])
parse_stats_full(frame, i, v[i], prev_data[k][i], template_names[k])
end
end
Line 122: Line 677:
-- iterate through attacks/abilities/subtowers
-- iterate through attacks/abilities/subtowers
for kk, vv in pairs(v) do
for _, i in ipairs(v["_order"]) do
tinsert(template[k], parse_stats_full(frame, kk, nil, vv, template_names[k]))
if k == "subtowers" then tinsert(template[k], sformat(header, i)) end
tinsert(template[k], parse_stats_full(frame, i, nil, v[i], template_names[k]))
end
end
Line 148: Line 704:
for k, v in pairs(new_data) do
for k, v in pairs(new_data) do
if type(v) == "table" then
if type(v) == "table" then
mw.logObject(k)
if prev_data[k] == nil then prev_data[k] = {} end
if prev_data[k] == nil then prev_data[k] = {} end
if prev_data[k]["_order"] == nil then
prev_data[k]["_order"] = {}
prev_data[k]["_keys"] = {}
end
-- iterate through attacks/abilities/subtowers
-- iterate through attacks/abilities/subtowers
for kk, vv in pairs(v) do
for _, i in ipairs(v["_order"]) do
if prev_data[k][kk] == nil then prev_data[k][kk] = {} end
if prev_data[k][i] == nil then prev_data[k][i] = {} end
if prev_data[k]["_keys"][i] == nil then
tinsert(prev_data[k]["_order"], i)
prev_data[k]["_keys"][i] = true
end
-- parse sub-table of table
-- parse sub-table of table
parse_stats_full(frame, kk, vv, prev_data[k][kk], template_names[k])
parse_stats_full(frame, i, v[i], prev_data[k][i], template_names[k])
end
end
Line 169: Line 732:
-- iterate through attacks/abilities/subtowers
-- iterate through attacks/abilities/subtowers
for kk, vv in pairs(v) do
for _, i in ipairs(v["_order"]) do
if prev_data[k][kk] == nil then prev_data[k][kk] = {} end
if prev_data[k][i] == nil then prev_data[k][i] = {} end
-- parse sub-table of table
-- parse sub-table of table
tinsert(template[k], parse_stats_full(frame, kk, nil, vv, template_names[k]))
tinsert(template[k], parse_stats_full(frame, i, nil, v[i], template_names[k]))
end
end
Line 184: Line 747:
return frame:expandTemplate{title = template_name, args = template}
return frame:expandTemplate{title = template_name, args = template}
end
end


-- stats of a single tower
-- stats of a single tower
function p.base_stats(frame)
function p.base_stats(frame)
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s", frame.args[1]))
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1]))
local header_level = frame.args["header level"] ~= nil and frame.args["header level"] or "3"
local header = "<h" .. header_level .. ">%s</h" .. header_level .. ">"
if frame.args[2] == nil then
if data["_000"] ~= nil then
return parse_tower_base(frame, data["_000"], header, false)
else
return parse_tower_base(frame, data, header, false)
end
else
return parse_tower_base(frame, data["_" .. frame.args[2]], header, false)
end
end
 
-- stats of a paragon
function p.paragon(frame)
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1]))
local out = parse_tower_base(frame, data, "<h3>%s</h3>", true)
return string.format("<div class='hatnote'>This data was last updated for: [[Update history:Bloons TD 6/Version %s|version %s]]</div>", data["_last_updated"], data["_last_updated"]) .. out
end
 
-- stat changes of a tower
function p.tower_upgrade_stats(frame)
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1]))
local ret = {}
-- local functions to improve performance
local tinsert = table.insert
local sformat = string.format
local ssub = string.sub
local base_data = data[frame.args[2]]
local upgrade_crosspaths = {
["_000"] = {{}},
["_100"] = {{"_110"}, {"_101"}},
["_200"] = {{"_210", "_220"}, {"_201", "_202"}},
["_010"] = {{"_110"}, {"_011"}},
["_020"] = {{"_120", "_220"}, {"_021", "_022"}},
["_001"] = {{"_101"}, {"_011"}},
["_002"] = {{"_102", "_202"}, {"_012", "_022"}},
["_300"] = {{"_310", "_320"}, {"_301", "_302"}},
["_400"] = {{"_410", "_420"}, {"_401", "_402"}},
["_500"] = {{"_510", "_520"}, {"_501", "_502"}},
["_030"] = {{"_130", "_230"}, {"_031", "_032"}},
["_040"] = {{"_140", "_240"}, {"_041", "_042"}},
["_050"] = {{"_150", "_250"}, {"_051", "_052"}},
["_003"] = {{"_103", "_203"}, {"_013", "_023"}},
["_004"] = {{"_104", "_204"}, {"_014", "_024"}},
["_005"] = {{"_105", "_205"}, {"_015", "_025"}}
}
tinsert(ret, sformat("<div class='hatnote'>This data was last updated for: [[Update history:Bloons TD 6/Version %s|version %s]]</div>", data["_last_updated"], data["_last_updated"]))
return parse_tower(frame, data, my_data)
for i, v in ipairs(upgrade_crosspaths[frame.args[2]]) do
my_data = {}
local result = parse_tower(frame, base_data, my_data, "<h3>%s</h3>", false)
-- inject beast handler stats table
if frame.args[1] == "Beast Handler" and not (frame.args[2] == "_000" or frame.args[2] == "_100" or frame.args[2] == "_010" or frame.args[2] == "_001") then
result = result .. parse_beast_table(base_data.subtowers.Beast, frame.args[2])
end
if i == 1 then tinsert(ret, result) end
for ii, vv in ipairs(v) do
if base_data[vv] ~= nil then
local crosspath = parse_tower(frame, base_data[vv], my_data, "<h4>%s</h4>", false)
if mw.text.trim(crosspath) ~= [[
]] then
tinsert(ret, sformat("<h3>%s-%s-%s</h3>", ssub(vv, 2, 2), ssub(vv, 3, 3), ssub(vv, 4, 4)))
tinsert(ret, crosspath)
end
end
end
end
return table.concat(ret, "\n")
end
end


-- stat changes of a hero
-- stat changes of a hero
function p.hero_level_changes(frame)
function p.hero_level_changes(frame)
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s", frame.args[1]))
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1]))
local ret = {}
local ret = {}
Line 204: Line 844:
-- calculate base tower stats for later comparison
-- calculate base tower stats for later comparison
parse_tower(frame, data, my_data)
parse_tower(frame, data, my_data, "<h3>%s</h3>", false)
for i = 2, 20 do
for i = 2, 20 do
tinsert(ret, sformat("===Level %i===", i))
tinsert(ret, sformat("===Level %i===", i))
tinsert(ret, parse_tower(frame, data[i], my_data))
tinsert(ret, parse_tower(frame, data["_" .. i], my_data, "<h4>%s</h4>", false))
end
end
Line 216: Line 856:
-- stat changes of a hero
-- stat changes of a hero
function p.hero_level_stats(frame)
function p.hero_level_stats(frame)
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s", frame.args[1]))
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1]))
local ret = {}
local ret = {}
Line 225: Line 865:
-- calculate base tower stats for later comparison
-- calculate base tower stats for later comparison
parse_tower(frame, data, my_data)
parse_tower(frame, data, my_data, "<h3>%s</h3>", false)
for i = 2, 20 do
for i = 2, 20 do
tinsert(ret, sformat("===Level %i===", i))
tinsert(ret, sformat("===Level %i===", i))
tinsert(ret, parse_tower_full(frame, data[i], my_data))
tinsert(ret, parse_tower_full(frame, data["_" .. i], my_data, "<h4>%s</h4>", false))
end
end
return table.concat(ret, "\n")
return table.concat(ret, "\n")
end
function p.last_updated(frame)
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1]))
return string.format("<div class='hatnote'>This data was last updated for: [[Update history:Bloons TD 6/Version %s|version %s]]</div>", data["_last_updated"], data["_last_updated"])
end
end


return p
return p