Module:GameData: Difference between revisions
More actions
m Protected "Module:GameData" ([Edit=Allow only administrators] (indefinite) [Move=Allow only administrators] (indefinite)) [cascading] |
No edit summary |
||
| Line 3: | Line 3: | ||
-- In-memory cache so we only parse each JSON page once | -- In-memory cache so we only parse each JSON page once | ||
local cache = {} | local cache = {} | ||
local function newEmptyResult() | |||
return { meta = {}, records = {}, byId = {} } | |||
end | |||
local function getInternalKey(rec) | |||
if type(rec) ~= "table" then | |||
return nil | |||
end | |||
-- Prefer the new Structured / Wiki keys, but tolerate legacy variants. | |||
local internal = | |||
rec["Internal Name"] or | |||
rec["InternalName"] or | |||
rec["Internal ID"] or | |||
rec["InternalID"] or | |||
rec["internal_name"] or | |||
rec["Id"] or | |||
rec["ID"] or | |||
rec["Name"] | |||
if type(internal) == "string" and internal ~= "" then | |||
return internal | |||
end | |||
return nil | |||
end | |||
local function decodeJsonPage(titleText) | local function decodeJsonPage(titleText) | ||
if cache[titleText] then | |||
return cache[titleText] | |||
end | |||
local function finish(result) | |||
cache[titleText] = result | |||
return result | |||
end | |||
local title = mw.title.new(titleText) | |||
if not title then | |||
return finish(newEmptyResult()) | |||
end | |||
local content = title:getContent() | |||
if type(content) ~= "string" or content == "" then | |||
return finish(newEmptyResult()) | |||
end | |||
local ok, data = pcall(mw.text.jsonDecode, content) | |||
if not ok or type(data) ~= "table" then | |||
return finish(newEmptyResult()) | |||
end | |||
-- Your files use: version, schema_version, generated_at, records | |||
local records = data.records | |||
if type(records) ~= "table" then | |||
records = {} | |||
end | |||
local byId = {} | |||
for _, rec in ipairs(records) do | |||
local internal = getInternalKey(rec) | |||
if internal then | |||
byId[internal] = rec | |||
end | |||
end | |||
local result = { | |||
meta = { | |||
version = data.version, | |||
schema_version = data.schema_version, | |||
generated_at = data.generated_at, | |||
}, | |||
records = records, | |||
byId = byId, | |||
} | |||
return finish(result) | |||
end | end | ||
| Line 67: | Line 86: | ||
function p.loadSkills() | function p.loadSkills() | ||
return decodeJsonPage("Data:skills.json") | |||
end | end | ||
function p.loadPassives() | function p.loadPassives() | ||
return decodeJsonPage("Data:passives.json") | |||
end | end | ||
function p.loadSummons() | function p.loadSummons() | ||
return decodeJsonPage("Data:summons.json") | |||
end | end | ||
function p.loadEffects() | function p.loadEffects() | ||
return decodeJsonPage("Data:effects.json") | |||
end | end | ||
return p | return p | ||
Latest revision as of 05:06, 16 December 2025
Module:GameData
Module:GameData is the central JSON loader for SpiritVale’s game data.
It reads four JSON pages:
and turns each one into a Lua dataset that other modules (like Module:GameSkills, Module:GamePassives, Module:GameSummons, and Module:GameEffects) can use.
This module is not meant to be called directly from templates with #invoke.
Instead, other Lua modules should require it and use the load* helper functions described below.
JSON format
Each JSON page is expected to have the following top-level structure:
{
"version": "SpiritVale-0.8.2",
"schema_version": 1,
"generated_at": "2025-12-12T17:24:05.807675+00:00",
"records": [
{
"Name": "Some Skill",
"Internal Name": "SomeSkillInternalId",
"...": "other fields specific to this type"
},
{
"Name": "Another Skill",
"Internal Name": "AnotherSkill",
"...": "more data"
}
]
}
Key points:
version– game / patch version string.schema_version– JSON schema version number.generated_at– timestamp when the file was generated by the external tool.records– array of objects (skills, passives, summons, effects, etc.).- Each record must have an
"Internal Name"field, used as the stable ID.
The exact fields inside each record depend on the data type and are handled by the type-specific modules (Module:GameSkills, Module:GamePassives, Module:GameSummons, Module:GameEffects).
Return value
Every load* function returns the same dataset structure:
local dataset = {
meta = {
version = "SpiritVale-0.8.2",
schema_version = 1,
generated_at = "2025-12-12T17:24:05.807675+00:00",
},
records = { ... }, -- array of all records from the JSON
byId = { -- lookup table by "Internal Name"
["SomeSkillInternalId"] = { ...record... },
["AnotherSkill"] = { ...record... },
-- etc.
},
}
This makes it easy to either:
- Iterate over all records via
dataset.records, or - Grab a single record by its internal ID via
dataset.byId["Internal Name"].
Public functions
loadSkills()
Reads Data:skills.json and returns a dataset for all active skills.
local GameData = require("Module:GameData")
local skills = GameData.loadSkills()
-- Access metadata
local meta = skills.meta
-- Iterate all skills
for _, skill in ipairs(skills.records) do
-- do something
end
-- Lookup by Internal Name
local bash = skills.byId["Bash"]
loadPassives()
Reads Data:passives.json and returns a dataset for all passive skills.
local GameData = require("Module:GameData")
local passives = GameData.loadPassives()
loadSummons()
Reads Data:summons.json and returns a dataset for all summons.
local GameData = require("Module:GameData")
local summons = GameData.loadSummons()
loadEffects()
Reads Data:effects.json and returns a dataset for all status effects / effects.
local GameData = require("Module:GameData")
local effects = GameData.loadEffects()
Usage pattern
Other modules should:
require("Module:GameData").- Call the appropriate
load*function. - Use
.byIdfor fast lookups by internal ID. - Use
.recordswhen they need to iterate over all entries.
Example (skills):
local GameData = require("Module:GameData")
local p = {}
function p.infobox(frame)
local id = frame.args.id
local skills = GameData.loadSkills()
local rec = skills.byId[id]
-- build and return HTML using 'rec'
end
return p
This keeps all JSON loading and parsing logic in one place, and makes it easy to update the data files each patch without changing the Lua code.
local p = {}
-- In-memory cache so we only parse each JSON page once
local cache = {}
local function newEmptyResult()
return { meta = {}, records = {}, byId = {} }
end
local function getInternalKey(rec)
if type(rec) ~= "table" then
return nil
end
-- Prefer the new Structured / Wiki keys, but tolerate legacy variants.
local internal =
rec["Internal Name"] or
rec["InternalName"] or
rec["Internal ID"] or
rec["InternalID"] or
rec["internal_name"] or
rec["Id"] or
rec["ID"] or
rec["Name"]
if type(internal) == "string" and internal ~= "" then
return internal
end
return nil
end
local function decodeJsonPage(titleText)
if cache[titleText] then
return cache[titleText]
end
local function finish(result)
cache[titleText] = result
return result
end
local title = mw.title.new(titleText)
if not title then
return finish(newEmptyResult())
end
local content = title:getContent()
if type(content) ~= "string" or content == "" then
return finish(newEmptyResult())
end
local ok, data = pcall(mw.text.jsonDecode, content)
if not ok or type(data) ~= "table" then
return finish(newEmptyResult())
end
-- Your files use: version, schema_version, generated_at, records
local records = data.records
if type(records) ~= "table" then
records = {}
end
local byId = {}
for _, rec in ipairs(records) do
local internal = getInternalKey(rec)
if internal then
byId[internal] = rec
end
end
local result = {
meta = {
version = data.version,
schema_version = data.schema_version,
generated_at = data.generated_at,
},
records = records,
byId = byId,
}
return finish(result)
end
-- Public helpers for each data file ------------------------------
function p.loadSkills()
return decodeJsonPage("Data:skills.json")
end
function p.loadPassives()
return decodeJsonPage("Data:passives.json")
end
function p.loadSummons()
return decodeJsonPage("Data:summons.json")
end
function p.loadEffects()
return decodeJsonPage("Data:effects.json")
end
return p