Module:Database
From Cassette Beasts
Documentation for this module may be created at Module:Database/doc
local p = {}
local db = mw.loadData("Module:Database/data")
-- Private helpers
function table.empty(t)
for _,_ in pairs(t) do
return false
end
return true
end
function table.clone(t)
local result = {}
for k,v in pairs(t) do
result[k] = v
end
return result
end
function table.values(t)
local result = {}
for _,v in pairs(t) do
table.insert(result, v)
end
return result
end
function string.split(s, delim)
local result = {}
if s == "" then
return result
end
local current = ""
for i = 1, string.len(s) do
if string.sub(s, i, i + string.len(delim) - 1) == delim then
i = i + string.len(delim) - 1
table.insert(result, current)
current = ""
else
current = current .. string.sub(s, i, i)
end
end
table.insert(result, current)
return result
end
function species_sort_index(species)
local i = species.bestiary_index
if i == nil or i < 0 then
return 99999
else
return i
end
end
function cmp_i(a, b)
if type(a) == "string" then
a = string.lower(a)
end
if type(b) == "string" then
b = string.lower(b)
end
return a < b
end
function cmp_species(a, b)
local ai = species_sort_index(a)
local bi = species_sort_index(b)
if ai == bi then
return string.lower(a.name) < string.lower(b.name)
end
return ai < bi
end
function cmp_moves(a, b)
return string.lower(a.name) < string.lower(b.name)
end
function cmp_status_effects(a, b)
return string.lower(a.name) < string.lower(b.name)
end
function get_species(id)
if tonumber(id) ~= nil then
id = tonumber(id)
end
if type(id) == "number" then
return db.species.by_index[id] or db.species.unknown
else
return db.species.by_name[string.lower(id)] or db.species.unknown
end
end
function describe_evolution_branch(evo)
if evo.specialization ~= "" then
return evo.specialization
elseif evo.required_type_override ~= "" then
return "[[" .. evo.required_type_override .. "]] Bootleg"
elseif evo.required_move ~= "" then
return "[[" ..evo.required_move .. "]] Sticker"
elseif evo.min_hour == 18 and evo.max_hour == 6 then
return "Night"
elseif evo.min_hour == 6 and evo.max_hour == 18 then
return "Day"
else
return "Default"
end
end
function get_move(id)
return db.moves.by_name[string.lower(id)] or db.moves.unknown
end
function get_type(id)
return db.types.by_name[string.lower(id)] or db.types.typeless
end
function get_status_effect(id)
return db.status_effects.by_name[string.lower(id)] or db.status_effects.unknown
end
local getters = {
species = get_species,
move = get_move,
["type"] = get_type,
status_effect = get_status_effect
}
function subscript(value, path, path_start)
while value ~= nil and path[path_start] ~= nil do
if type(value) ~= "table" then
return nil
end
local key = path[path_start]
if tonumber(key) ~= nil then
value = value[tonumber(key)]
else
value = value[key]
end
path_start = path_start + 1
end
return value
end
function flatten_template_args(args)
for k,v in pairs(args) do
if type(v) == "table" and v.name then
args[k] = v.name
end
args[k] = tostring(v)
end
end
function textify(frame, value)
if frame.args.sorted and type(value) == "table" then
value = table.values(value)
table.sort(value, cmp_i)
elseif frame.args.sort_by and type(value) == "table" then
value = table.values(value)
table.sort(value, function(a, b)
return cmp_i(a[frame.args.sort_by], b[frame.args.sort_by])
end)
end
if frame.args["if_" .. tostring(value)] ~= nil then
return frame.args["if_" .. tostring(value)]
elseif type(value) == "number" and frame.args.if_number ~= nil then
return string.format(frame.args.if_number, value)
elseif type(value) == "table" and frame.args.if_empty ~= nil and table.empty(value) then
return frame.args.if_empty
elseif type(value) == "table" and (frame.args.delim or frame.args["foreach"]) then
local delim = frame.args.delim or ""
if delim == "," then delim = delim .. " " end
local result = ""
local i = 1
local iter
if value[1] then iter = ipairs else iter = pairs end
for key,elem in iter(value) do
if i > 1 then
result = result .. delim
end
if frame.args.subscript then
elem = subscript(elem, string.split(frame.args.subscript, "."), 1)
end
if frame.args["foreach"] then
local args
if type(elem) == "table" then
args = table.clone(elem)
else
args = { elem, first = i == 1 }
end
if frame.args.key_param then
local s = key
if frame.args.key_param_format then
s = string.format(frame.args.key_param_format, s)
end
args[frame.args.key_param] = s
end
if frame.args.pass_through then
args[frame.args.pass_through] = frame.args[frame.args.pass_through]
end
flatten_template_args(args)
elem = frame:expandTemplate{title = frame.args["foreach"], args = args}
else
elem = textify(frame, elem)
end
result = result .. elem
i = i + 1
end
return result
end
if type(frame.args.format) == "string" then
value = string.format(frame.args.format, value)
end
if type(value) == "table" and value.name ~= nil then
value = value.name
end
if value == nil or value == false then
return ""
elseif type(value) == "string" or type(value) == "number" or type(value) == "boolean" then
return tostring(value)
end
return "<" .. tostring(value) .. ">"
end
-- Public functions for use with #invoke
function p.get_species(frame)
local species = get_species(frame.args[1])
return textify(frame, subscript(species, frame.args, 2))
end
function p.get_prev_species(frame)
local species = get_species(frame.args[1])
if species.bestiary_index ~= nil then
species = get_species(species.bestiary_index - 1)
end
return textify(frame, subscript(species, frame.args, 2))
end
function p.get_next_species(frame)
local species = get_species(frame.args[1])
if species.bestiary_index ~= nil then
species = get_species(species.bestiary_index + 1)
end
return textify(frame, subscript(species, frame.args, 2))
end
function p.species_has_family(frame)
local species = get_species(frame.args[1])
local result = not table.empty(species.evolves_from) or not table.empty(species.evolves_to)
return textify(frame, subscript(result, frame.args, 2))
end
function p.get_species_compatible_moves(frame)
local result_set = {}
local species = get_species(frame.args[1])
local tags = species.moves.tags
if frame.args.always_compatible then
tags = species.moves.core_tags
end
for _,tag in ipairs(tags) do
if db.moves.by_tag[tag] then
for _,move in ipairs(db.moves.by_tag[tag]) do
result_set[move.name] = move
end
end
end
local result = table.values(result_set)
table.sort(result, cmp_moves)
return textify(frame, result)
end
function p.get_tag_moves(frame)
local result = {}
local tag = frame.args[1]
local for_bootleg = frame.args.for_bootleg
if db.moves.by_tag[tag] then
for _,move in ipairs(db.moves.by_tag[tag]) do
if not for_bootleg or move.camouflage ~= tag then
table.insert(result, move)
end
end
end
return textify(frame, result)
end
function p.get_species_move_learn_method(frame)
local species = get_species(frame.args[1])
local move = get_move(frame.args[2])
for _,m in ipairs(species.moves.initial) do
if move.name == m then
return textify(frame, "initial")
end
end
for i,m in ipairs(species.moves.upgrades) do
if move.name == m then
return textify(frame, i)
end
end
if string.lower(species.elemental_type) ~= move.camouflage then
local tags = {}
for _,tag in ipairs(species.moves.tags) do
tags[tag] = true
end
for _,tag in ipairs(move.tags) do
if tags[tag] then
return textify(frame, "compatible")
end
end
end
return textify(frame, "incompatible")
end
function p.get_species_list(frame)
-- If frame.args includes either 'type' as a key, it returns only the
-- species with that type. Otherwise it returns all species.
-- Use this with foreach= and/or delim= in frame.args.
local result = {}
if frame.args["type"] then
local t = get_type(frame.args["type"])
result = table.values(db.species.by_type[string.lower(t.name)] or {})
table.sort(result, cmp_species)
else
for i = db.species.min_index, db.species.max_index do
if db.species.by_index[i] then
table.insert(result, db.species.by_index[i])
else
table.insert(result, db.species.unknown)
end
end
for _,s in ipairs(db.species.unnumbered) do
table.insert(result, s)
end
end
return textify(frame, result)
end
function p.describe_evolution_branch(frame)
local from_species = get_species(frame.args[1])
local to_species = get_species(frame.args[2])
if from_species == db.species.unknown or to_species == db.species.unknown then
return nil
end
for _,evo in ipairs(from_species.evolves_to) do
if evo.evolved_form == to_species.name then
return describe_evolution_branch(evo)
end
end
return nil
end
function p.get_move(frame)
local move = get_move(frame.args[1])
return textify(frame, subscript(move, frame.args, 2))
end
function p.get_move_compatible_species(frame)
local result_set = {}
local move = get_move(frame.args[1])
for _,tag in ipairs(move.tags) do
if db.species.by_tag[tag] then
for _,species in ipairs(db.species.by_tag[tag]) do
if string.lower(species.elemental_type) ~= move.camouflage then
result_set[species.name] = true
end
end
end
end
local result = {}
for species,_ in pairs(result_set) do
table.insert(result, species)
end
table.sort(result)
return textify(frame, result)
end
function p.get_move_type_tags(frame)
local result = {}
local move = get_move(frame.args[1])
for _,tag in ipairs(move.tags) do
if db.types.by_name[tag] and tag ~= move.camouflage then
table.insert(result, db.types.by_name[tag].name)
end
end
table.sort(result)
return textify(frame, result)
end
function p.get_move_list(frame)
-- If frame.args includes either 'type' as a key, it returns only the
-- moves with that type. Otherwise it returns all moves.
-- Use this with foreach= and/or delim= in frame.args.
local result = {}
if frame.args["type"] then
local t = get_type(frame.args["type"])
result = table.values(db.moves.by_type[string.lower(t.name)] or {})
else
result = table.values(db.moves.by_name)
end
table.sort(result, cmp_moves)
return textify(frame, result)
end
function p.get_type(frame)
local t = get_type(frame.args[1])
local result = subscript(t, frame.args, 2)
return textify(frame, result)
end
function p.get_type_list(frame)
return textify(frame, db.types.ordered)
end
function p.get_reaction(frame)
local atype = get_type(frame.args[1])
local dtype = get_type(frame.args[2])
local reaction = atype.inflicts[dtype.name]
return textify(frame, subscript(reaction, frame.args, 3))
end
function p.get_status_effect(frame)
local s = get_status_effect(frame.args[1])
local result = subscript(s, frame.args, 2)
return textify(frame, result)
end
function p.get_status_effect_list(frame)
local list = table.values(db.status_effects.by_name)
table.sort(list, cmp_status_effects)
return textify(frame, list)
end
function p.get_link(frame)
local table_name = frame.args[1]
local getter = getters[table_name]
if getter == nil then
return frame:preprocess("[[INVALID]]")
end
local id = frame.args[2]
local object = getter(id)
for k,g in pairs(getters) do
if k ~= table_name then
local unknown = g("Unknown")
local o = g(id)
if o ~= unknown then
return frame:preprocess("[[" .. object.name .. " (" .. table_name ..")|" .. object.name .. "]]")
end
end
end
return frame:preprocess("[[" .. object.name .. "]]")
end
function p.exists(frame)
local getter = getters[frame.args[1]]
local object = getter(frame.args[2])
local unknown = getter("Unknown")
return textify(frame, object ~= unknown)
end
return p