Module:BTD6 stats: Difference between revisions
Jump to navigation
Jump to search
mNo edit summary |
big refactor, might break a few things Tag: Reverted |
||
| 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: | --[[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 | local prev_data = {} | ||
-- adds the "this data was last updated for" header | |||
function last_updated(version) | |||
return string.format("<div class='hatnote'>This data was last updated for: [[Bloons TD 6 v%s|version %s]]</div>", version, version) | |||
end | |||
-- table of template names for each type of object | |||
local template_names = { | local template_names = { | ||
attacks ="BTD6 attack", | attacks ="BTD6 attack", | ||
| Line 21: | Line 27: | ||
} | } | ||
function | function paragon_table(data) | ||
local ret = {"\n{|class='wikitable mw-collapsible mw-collapsed'\n|+style='white-space:nowrap'|Degree-dependent stats"} | local ret = {"\n{|class='wikitable mw-collapsible mw-collapsed'\n|+style='white-space:nowrap'|Degree-dependent stats"} | ||
| Line 30: | Line 36: | ||
local msqrt = math.sqrt | local msqrt = math.sqrt | ||
local mfloor = math.floor | local mfloor = math.floor | ||
-- -- -- -- -- -- | -- -- -- -- -- -- | ||
| Line 127: | Line 121: | ||
if data["abilities"] ~= nil then heading_abilities(data["abilities"]) end | if data["abilities"] ~= nil then heading_abilities(data["abilities"]) end | ||
tinsert(ret, " | tinsert(ret, "!rowspan=2|Power\n!rowspan=2|Boss multiplier") | ||
for i, v in ipairs(heading_upper) do | for i, v in ipairs(heading_upper) do | ||
| Line 224: | Line 218: | ||
while d <= 100 do | while d <= 100 do | ||
-- new row | -- new row | ||
tinsert(ret, sformat("|-\n! | tinsert(ret, sformat("|-\n!%i", d)) | ||
-- boss damage mult | -- boss damage mult | ||
| Line 256: | Line 250: | ||
local ssub = string.sub | local ssub = string.sub | ||
-- search through | -- search through variables | ||
for k, v in pairs(data) do | for k, v in pairs(data) do | ||
-- subtables (attacks, abilities, etc) | |||
if type(v) == "table" and ssub(k, 1, 1) ~= "_" then | if type(v) == "table" and ssub(k, 1, 1) ~= "_" then | ||
local subtables = {} | |||
for i, subtable_name in ipairs(v["_order"]) do | |||
for | -- insert header for subtowers | ||
if k == "subtowers" then tinsert( | if k == "subtowers" then tinsert(subtables, sformat(header, subtable_name)) end | ||
-- parse subtable of table | |||
local stats = parse_stats_base(frame, subtable_name, v) | |||
-- | -- append stats subtable to output | ||
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats}) | |||
if k == "subtowers" and paragon then | if k == "subtowers" and paragon then tinsert(subtables, paragon_table(v[subtable_name])) end | ||
end | end | ||
template[k] = tconcat( | template[k] = tconcat(subtables) | ||
-- other variables (rate, pierce, etc) | |||
elseif type(v) ~= "table" then | elseif type(v) ~= "table" then | ||
template[k] = data[k] | template[k] = data[k] | ||
| Line 278: | Line 276: | ||
end | end | ||
if paragon then template["paragon stats"] = | -- append paragon table to output | ||
if paragon then template["paragon stats"] = paragon_table(data) end | |||
return frame:expandTemplate{title = "BTD6 tower", args = template} | return frame:expandTemplate{title = "BTD6 tower", args = template} | ||
end | end | ||
function parse_stats_base(frame, | function parse_stats_base(frame, p_name, data) | ||
local template = {name = | local template = {name = p_name} | ||
-- local functions to improve performance | -- local functions to improve performance | ||
local tconcat = table.concat | local tconcat = table.concat | ||
-- search through | -- search through variables | ||
for k, v in pairs(data) do | for k, v in pairs(data[p_name]) do | ||
-- subtables (attacks, abilities, etc) | |||
if type(v) == "table" then | if type(v) == "table" then | ||
local subtables = {} | |||
for i, subtable_name in ipairs(v["_order"]) do | |||
for | -- parse subtable of table | ||
-- parse | subtables[i] = frame:expandTemplate{title = template_names[k], args = parse_stats_base(frame, subtable_name, v)} | ||
end | end | ||
template[k] = tconcat( | template[k] = tconcat(subtables) | ||
-- other variables (rate, pierce, etc) | |||
else | else | ||
template[k] = v | template[k] = v | ||
| Line 308: | Line 307: | ||
end | end | ||
return | return template | ||
end | end | ||
function | function parse_tower_changes(frame, base, changes, header) | ||
local template = {} | local template = {} | ||
| Line 320: | Line 319: | ||
local ssub = string.sub | local ssub = string.sub | ||
-- search through | -- search through variables | ||
for k, v in pairs( | for k, v in pairs(changes) do | ||
-- subtables (attacks, abilities, etc) | |||
if type(v) == "table" and ssub(k, 1, 1) ~= "_" then | if type(v) == "table" and ssub(k, 1, 1) ~= "_" then | ||
local subtables = {} | |||
if prev_data[k] == nil then prev_data[k] = {} end | if prev_data[k] == nil then prev_data[k] = {} end | ||
for i, subtable_name in ipairs(v["_order"]) do | |||
for | if prev_data[k][subtable_name] == nil then prev_data[k][subtable_name] = {} end | ||
-- insert header for subtowers | |||
if prev_data[k][ | if k == "subtowers" then tinsert(subtables, sformat(header, subtable_name)) end | ||
-- parse | -- parse subtable of table | ||
local stats = | -- if neither the base tower nor a previous stat change has this subtable, then treat it as new | ||
if (base[k] == nil or base[k][subtable_name] == nil) and next(prev_data[k][subtable_name]) == nil then | |||
local stats = parse_stats_base(frame, subtable_name, v) | |||
prev_data[k][subtable_name] = stats | |||
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats}) | |||
else | |||
local stats = parse_stats_changes(frame, subtable_name, base[k][subtable_name], v[subtable_name], prev_data[k][subtable_name]) | |||
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats}) | |||
end | |||
end | end | ||
template[k] = tconcat( | template[k] = tconcat(subtables) | ||
-- other variables (rate, pierce, etc) | |||
elseif type(v) ~= "table" then | elseif type(v) ~= "table" then | ||
-- if | -- stat comparison | ||
if prev_data[k] ~= nil then | -- if this stat changed before, use that, otherwise use the base tower's stat | ||
if prev_data[k] ~= nil then template[k] = prev_data[k] | |||
else template[k] = base[k] end | |||
template[k .. " after"] = v | |||
prev_data[k] = v | prev_data[k] = v | ||
end | end | ||
| Line 355: | Line 360: | ||
end | end | ||
function | function parse_stats_changes(frame, p_name, base, changes, prev) | ||
local template = {name = | local template = {name = p_name} | ||
-- local functions to improve performance | -- local functions to improve performance | ||
| Line 363: | Line 368: | ||
-- search through tables | -- search through tables | ||
for k, v in pairs( | for k, v in pairs(changes) do | ||
-- subtables (attacks, abilities, etc) | |||
if type(v) == "table" then | if type(v) == "table" then | ||
local subtables = {} | |||
if prev[k] == nil then prev[k] = {} end | |||
for i, subtable_name in ipairs(v["_order"]) do | |||
if prev[k][subtable_name] == nil then prev[k][subtable_name] = {} end | |||
for | -- parse subtable of table | ||
if | -- if neither the base tower nor a previous stat change has this subtable, then treat it as new | ||
-- parse | if (base == nil or base[k] == nil or base[k][subtable_name] == nil) and next(prev[k][subtable_name]) == nil then | ||
tinsert( | local stats = parse_stats_base(frame, subtable_name, v) | ||
prev[k][subtable_name] = stats | |||
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats}) | |||
else | |||
local stats = parse_stats_changes(frame, subtable_name, base[k][subtable_name], v[subtable_name], prev[k][subtable_name]) | |||
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats}) | |||
end | |||
end | end | ||
template[k] = tconcat( | template[k] = tconcat(subtables) | ||
-- other variables (rate, pierce, etc) | |||
else | else | ||
-- if | -- stat comparison | ||
if | -- if this stat changed before, use that, otherwise use the base tower's stat | ||
if prev[k] ~= nil then template[k] = prev[k] | |||
template[k | else template[k] = base[k] end | ||
template[k .. " after"] = v | |||
prev[k] = v | |||
end | end | ||
end | end | ||
return | return template | ||
end | end | ||
| Line 421: | Line 432: | ||
prev_data[k]["_keys"][i] = true | prev_data[k]["_keys"][i] = true | ||
end | end | ||
-- parse | -- parse subtable of table | ||
parse_stats_full(frame, i, v[i], prev_data[k][i], template_names[k]) | parse_stats_full(frame, i, v[i], prev_data[k][i], template_names[k]) | ||
end | end | ||
| Line 477: | Line 488: | ||
prev_data[k]["_keys"][i] = true | prev_data[k]["_keys"][i] = true | ||
end | end | ||
-- parse | -- parse subtable of table | ||
parse_stats_full(frame, i, v[i], prev_data[k][i], template_names[k]) | parse_stats_full(frame, i, v[i], prev_data[k][i], template_names[k]) | ||
end | end | ||
| Line 494: | Line 505: | ||
for _, i in ipairs(v["_order"]) do | for _, i in ipairs(v["_order"]) do | ||
if prev_data[k][i] == nil then prev_data[k][i] = {} end | if prev_data[k][i] == nil then prev_data[k][i] = {} end | ||
-- parse | -- parse subtable of table | ||
tinsert(template[k], parse_stats_full(frame, i, nil, v[i], template_names[k])) | tinsert(template[k], parse_stats_full(frame, i, nil, v[i], template_names[k])) | ||
end | end | ||
| Line 509: | Line 520: | ||
-- stats of a single tower | -- stats of a single tower | ||
function p. | function p.tower_base(frame) | ||
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", 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_level = frame.args["header level"] ~= nil and frame.args["header level"] or "3" | ||
| Line 529: | Line 540: | ||
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1])) | local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1])) | ||
return last_updated(data["_last_updated"]) .. parse_tower_base(frame, data, "<h3>%s</h3>", false) | |||
end | end | ||
-- stat changes of a tower | -- stat changes of a tower | ||
function p. | function p.tower_upgrade_changes(frame) | ||
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1])) | local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1])) | ||
| Line 544: | Line 553: | ||
local sformat = string.format | local sformat = string.format | ||
local ssub = string.sub | local ssub = string.sub | ||
local upgrade_crosspaths = { | local upgrade_crosspaths = { | ||
| Line 566: | Line 573: | ||
} | } | ||
-- data for this upgrade + crosspaths | |||
local base_data = data[frame.args[2]] | |||
tinsert(ret, last_updated(data["_last_updated"], data["_last_updated"])) | |||
tinsert(ret, parse_tower_base(frame, base_data, "<h3>%s</h3>", false)) | |||
for i, v in ipairs(upgrade_crosspaths[frame.args[2]]) do | for i, v in ipairs(upgrade_crosspaths[frame.args[2]]) do | ||
my_data = {} | |||
for ii, vv in ipairs(v) do | for ii, vv in ipairs(v) do | ||
if base_data[vv] ~= nil then | if base_data[vv] ~= nil then | ||
tinsert(ret, sformat("<h3>%s-%s-%s</h3>", ssub(vv, 2, 2), ssub(vv, 3, 3), ssub(vv, 4, 4))) | tinsert(ret, sformat("<h3>%s-%s-%s</h3>", ssub(vv, 2, 2), ssub(vv, 3, 3), ssub(vv, 4, 4))) | ||
tinsert(ret, | tinsert(ret, parse_tower_changes(frame, base_data, base_data[vv], "<h4>%s</h4>")) | ||
end | end | ||
end | end | ||
| Line 591: | Line 601: | ||
local tinsert = table.insert | local tinsert = table.insert | ||
local sformat = string.format | local sformat = string.format | ||
for i = 2, 20 do | for i = 2, 20 do | ||
tinsert(ret, sformat("===Level %i===", i)) | tinsert(ret, sformat("===Level %i===", i)) | ||
tinsert(ret, | tinsert(ret, parse_tower_changes(frame, data, data["_" .. i], "<h4>%s</h4>")) | ||
end | end | ||
| Line 603: | Line 610: | ||
end | end | ||
-- | -- full stats of a hero for each level | ||
function p.hero_level_stats(frame) | function p.hero_level_stats(frame) | ||
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1])) | local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1])) | ||
| Line 624: | Line 631: | ||
end | end | ||
-- get "last updated" header | |||
function p.last_updated(frame) | function p.last_updated(frame) | ||
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1])) | local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1])) | ||
return | return last_updated(data["_last_modified"]) | ||
end | end | ||
return p | return p | ||
Revision as of 09:51, 6 March 2025
Documentation for this module may be created at Module:BTD6 stats/doc
--[[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 prev_data = {}
-- adds the "this data was last updated for" header
function last_updated(version)
return string.format("<div class='hatnote'>This data was last updated for: [[Bloons TD 6 v%s|version %s]]</div>", version, version)
end
-- table of template names for each type of object
local template_names = {
attacks ="BTD6 attack",
projectiles ="BTD6 projectile",
collectables="BTD6 collectable",
abilities ="BTD6 ability",
effects ="BTD6 effect",
buffs ="BTD6 buff",
zones ="BTD6 zone",
subtowers ="BTD6 tower"
}
function 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
-- -- -- -- -- --
-- 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
tinsert(ret, "!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("|%gs", amt / (1 + (0.01 * msqrt((d-1)*50)))))
end
-- damage
local insert_damage = function(amt)
if d == 100 then tinsert(ret, sformat("|%g", amt * 2 + 10))
else tinsert(ret, sformat("|%g", 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("|%g", amt * 2 + 10))
else tinsert(ret, sformat("|%g", 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("| +%g", amt * 2 + 10))
else tinsert(ret, sformat("| +%g", 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_mod(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", 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
d = d + 1
end
tinsert(ret, "|}")
return table.concat(ret, "\n")
end
function parse_tower_base(frame, data, header, paragon)
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 variables
for k, v in pairs(data) do
-- subtables (attacks, abilities, etc)
if type(v) == "table" and ssub(k, 1, 1) ~= "_" then
local subtables = {}
for i, subtable_name in ipairs(v["_order"]) do
-- insert header for subtowers
if k == "subtowers" then tinsert(subtables, sformat(header, subtable_name)) end
-- parse subtable of table
local stats = parse_stats_base(frame, subtable_name, v)
-- append stats subtable to output
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats})
if k == "subtowers" and paragon then tinsert(subtables, paragon_table(v[subtable_name])) end
end
template[k] = tconcat(subtables)
-- other variables (rate, pierce, etc)
elseif type(v) ~= "table" then
template[k] = data[k]
end
end
-- append paragon table to output
if paragon then template["paragon stats"] = paragon_table(data) end
return frame:expandTemplate{title = "BTD6 tower", args = template}
end
function parse_stats_base(frame, p_name, data)
local template = {name = p_name}
-- local functions to improve performance
local tconcat = table.concat
-- search through variables
for k, v in pairs(data[p_name]) do
-- subtables (attacks, abilities, etc)
if type(v) == "table" then
local subtables = {}
for i, subtable_name in ipairs(v["_order"]) do
-- parse subtable of table
subtables[i] = frame:expandTemplate{title = template_names[k], args = parse_stats_base(frame, subtable_name, v)}
end
template[k] = tconcat(subtables)
-- other variables (rate, pierce, etc)
else
template[k] = v
end
end
return template
end
function parse_tower_changes(frame, base, changes, 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 variables
for k, v in pairs(changes) do
-- subtables (attacks, abilities, etc)
if type(v) == "table" and ssub(k, 1, 1) ~= "_" then
local subtables = {}
if prev_data[k] == nil then prev_data[k] = {} end
for i, subtable_name in ipairs(v["_order"]) do
if prev_data[k][subtable_name] == nil then prev_data[k][subtable_name] = {} end
-- insert header for subtowers
if k == "subtowers" then tinsert(subtables, sformat(header, subtable_name)) end
-- parse subtable of table
-- if neither the base tower nor a previous stat change has this subtable, then treat it as new
if (base[k] == nil or base[k][subtable_name] == nil) and next(prev_data[k][subtable_name]) == nil then
local stats = parse_stats_base(frame, subtable_name, v)
prev_data[k][subtable_name] = stats
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats})
else
local stats = parse_stats_changes(frame, subtable_name, base[k][subtable_name], v[subtable_name], prev_data[k][subtable_name])
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats})
end
end
template[k] = tconcat(subtables)
-- other variables (rate, pierce, etc)
elseif type(v) ~= "table" then
-- stat comparison
-- if this stat changed before, use that, otherwise use the base tower's stat
if prev_data[k] ~= nil then template[k] = prev_data[k]
else template[k] = base[k] end
template[k .. " after"] = v
prev_data[k] = v
end
end
return frame:expandTemplate{title = "BTD6 tower", args = template}
end
function parse_stats_changes(frame, p_name, base, changes, prev)
local template = {name = p_name}
-- local functions to improve performance
local tinsert = table.insert
local tconcat = table.concat
-- search through tables
for k, v in pairs(changes) do
-- subtables (attacks, abilities, etc)
if type(v) == "table" then
local subtables = {}
if prev[k] == nil then prev[k] = {} end
for i, subtable_name in ipairs(v["_order"]) do
if prev[k][subtable_name] == nil then prev[k][subtable_name] = {} end
-- parse subtable of table
-- if neither the base tower nor a previous stat change has this subtable, then treat it as new
if (base == nil or base[k] == nil or base[k][subtable_name] == nil) and next(prev[k][subtable_name]) == nil then
local stats = parse_stats_base(frame, subtable_name, v)
prev[k][subtable_name] = stats
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats})
else
local stats = parse_stats_changes(frame, subtable_name, base[k][subtable_name], v[subtable_name], prev[k][subtable_name])
tinsert(subtables, frame:expandTemplate{title = template_names[k], args = stats})
end
end
template[k] = tconcat(subtables)
-- other variables (rate, pierce, etc)
else
-- stat comparison
-- if this stat changed before, use that, otherwise use the base tower's stat
if prev[k] ~= nil then template[k] = prev[k]
else template[k] = base[k] end
template[k .. " after"] = v
prev[k] = v
end
end
return template
end
function parse_tower_full(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
for k, v in pairs(new_data) do
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]["_order"] == nil then
prev_data[k]["_order"] = {}
prev_data[k]["_keys"] = {}
end
-- iterate through attacks/abilities/subtowers
for _, i in ipairs(v["_order"]) do
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 subtable of table
parse_stats_full(frame, i, v[i], prev_data[k][i], template_names[k])
end
elseif type(v) ~= "table" then
-- update previous value
prev_data[k] = v
end
end
-- update prev_data first
for k, v in pairs(prev_data) do
if k == "attacks" or k == "abilities" or k == "subtowers" or k == "zones" 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
tinsert(template[k], parse_stats_full(frame, i, nil, v[i], template_names[k]))
end
template[k] = tconcat(template[k])
elseif type(v) ~= "table" then
-- update previous value
template[k] = v
end
end
return frame:expandTemplate{title = "BTD6 tower", args = template}
end
function parse_stats_full(frame, name, new_data, prev_data, template_name)
local template = {name = name}
-- local functions to improve performance
local tinsert = table.insert
local tconcat = table.concat
-- update prev_data first
if new_data ~= nil then
for k, v in pairs(new_data) do
if type(v) == "table" then
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
for _, i in ipairs(v["_order"]) do
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 subtable of table
parse_stats_full(frame, i, v[i], prev_data[k][i], template_names[k])
end
else
prev_data[k] = v
end
end
end
for k, v in pairs(prev_data) do
if type(v) == "table" then
template[k] = {}
-- iterate through attacks/abilities/subtowers
for _, i in ipairs(v["_order"]) do
if prev_data[k][i] == nil then prev_data[k][i] = {} end
-- parse subtable of table
tinsert(template[k], parse_stats_full(frame, i, nil, 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
-- stats of a single tower
function p.tower_base(frame)
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]))
return last_updated(data["_last_updated"]) .. parse_tower_base(frame, data, "<h3>%s</h3>", false)
end
-- stat changes of a tower
function p.tower_upgrade_changes(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 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"}}
}
-- data for this upgrade + crosspaths
local base_data = data[frame.args[2]]
tinsert(ret, last_updated(data["_last_updated"], data["_last_updated"]))
tinsert(ret, parse_tower_base(frame, base_data, "<h3>%s</h3>", false))
for i, v in ipairs(upgrade_crosspaths[frame.args[2]]) do
my_data = {}
for ii, vv in ipairs(v) do
if base_data[vv] ~= nil then
tinsert(ret, sformat("<h3>%s-%s-%s</h3>", ssub(vv, 2, 2), ssub(vv, 3, 3), ssub(vv, 4, 4)))
tinsert(ret, parse_tower_changes(frame, base_data, base_data[vv], "<h4>%s</h4>"))
end
end
end
return table.concat(ret, "\n")
end
-- stat changes of a hero
function p.hero_level_changes(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
for i = 2, 20 do
tinsert(ret, sformat("===Level %i===", i))
tinsert(ret, parse_tower_changes(frame, data, data["_" .. i], "<h4>%s</h4>"))
end
return table.concat(ret, "\n")
end
-- full stats of a hero for each level
function p.hero_level_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
-- calculate base tower stats for later comparison
parse_tower(frame, data, my_data, "<h3>%s</h3>", false)
for i = 2, 20 do
tinsert(ret, sformat("===Level %i===", i))
tinsert(ret, parse_tower_full(frame, data["_" .. i], my_data, "<h4>%s</h4>", false))
end
return table.concat(ret, "\n")
end
-- get "last updated" header
function p.last_updated(frame)
local data = mw.loadJsonData(string.format("Module:BTD6 stats/%s/new", frame.args[1]))
return last_updated(data["_last_modified"])
end
return p