Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Join the Playtest on Steam Now: SpiritVale

Revision as of 05:06, 16 December 2025 by Eviand (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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:

  1. require("Module:GameData").
  2. Call the appropriate load* function.
  3. Use .byId for fast lookups by internal ID.
  4. Use .records when 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