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 | 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 | 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 | for _, i in ipairs(v["_order"]) do | ||
if prev_data[k][ | 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 | ||
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 | for _, i in ipairs(v["_order"]) do | ||
if prev_data[k][ | 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, | 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 | |||
-- | -- search through tables | ||
for k, v in pairs(new_data) do | for k, v in pairs(new_data) do | ||
if | 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 | for _, i in ipairs(v["_order"]) do | ||
if prev_data[k][ | 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, 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 | 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 | 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 | ||
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 | for _, i in ipairs(v["_order"]) do | ||
if prev_data[k][ | 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, | 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 | for _, i in ipairs(v["_order"]) do | ||
if prev_data[k][ | 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, | 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"])) | |||
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 | ||