Module:Sandbox/GameData
From Melvor Idle
Documentation for this module may be created at Module:Sandbox/GameData/doc
-- This module is responsible for managing all game data, and provides various
-- functions so that other modules may interact with the data.
local p = {}
local GameData1 = mw.loadData('Module:Sandbox/GameData/data')
local GameData2 = mw.loadData('Module:Sandbox/GameData/data2')
-- Combine data into a single object
local GameData = {}
for _, data in ipairs({GameData1, GameData2}) do
for entityType, entityData in pairs(data) do
GameData[entityType] = entityData
end
end
local indexCache = {}
-- Expose underlying data should other modules require it
p.rawData = GameData
-- Given a namespace & local ID, returns a namespaced ID
function p.getNamespacedID(namespace, localID)
if string.find(localID, ':') == nil then
return namespace .. ':' .. localID
else
-- ID already appears to be namespaced
return localID
end
end
-- Given a namespaced ID, returns both the namespace & local ID
function p.getLocalID(ID)
local namespace, localID = nil, nil
local sepIdx = string.find(ID, ':')
if sepIdx == nil then
-- Provided ID doesn't appear to be namespaced
localID = ID
else
namespace = string.sub(ID, 1, sepIdx - 1)
localID = string.sub(ID, sepIdx + 1, string.len(ID))
end
return namespace, localID
end
local function populateSkillData()
local skillData = {}
for i, skillObj in ipairs(GameData.skillData) do
local _, localID = p.getLocalID(skillObj.skillID)
if localID ~= nil then
skillData[localID] = skillObj.data
end
end
return skillData
end
-- Expose an easy way to reference skill data by skill local ID
p.skillData = populateSkillData()
function p.skillDataTest()
for localID, data in pairs(p.skillData) do
mw.log(localID)
end
end
-- If the entity ID is within the cache for the given entity type, then return it.
-- Otherwise, returns nil
local function getCache(entityType, ID)
if type(entityType) == 'string' and type(ID) == 'string' then
local cacheCat = indexCache[entityType]
if cacheCat ~= nil then
return cacheCat[ID]
end
end
end
-- Sets the cache for entity ID within the given entity type to idx.
local function setCache(entityType, ID, idx)
if type(entityType) == 'string' and type(ID) == 'string' and type(idx) == 'number' then
if indexCache[entityType] == nil then
indexCache[entityType] = {}
end
if indexCache[entityType][ID] == nil then
indexCache[entityType][ID] = math.floor(idx)
end
end
end
function p.getSkillData(skillID)
if type(skillID) ~= 'string' then
error('Skill ID must be a string', 2)
end
for idx, skill in ipairs(GameData.skillData) do
if skill.skillID == skillID then
return skill.data
end
end
end
-- Takes an entity type (or entity list) & property name/value, returning an object representing the
-- entity with the given property (if found). If not found, then nil is returned.
function p.getEntityByProperty(entityType, propName, propValue)
if type(entityType) ~= 'string' and type(entityType) ~= 'table' then
error('Entity type name must be a string or table', 2)
elseif type(propName) ~= 'string' then
error('Property name must be a string', 2)
end
local entData, useCache = nil, false
if type(entityType) == 'string' then
entData = GameData[entityType]
useCache = true
else
-- Function was passed a table of entities rather than a entity type
entData = entityType
end
if entData == nil then
error('No such entity type: ' .. entityType, 2)
elseif type(entData) ~= 'table' then
error('Entity data is not a table: ' .. entityType, 2)
elseif type(entData[1]) ~= 'table' or entData[1][propName] == nil then
error('Entity data is not composed of entities: ' .. entityType, 2)
end
-- Check if this ID is already cached
if propName == 'id' and useCache then
local cacheIdx = getCache(entityType, propValue)
if cacheIdx ~= nil then
-- Cache hit
return entData[cacheIdx]
end
end
-- Cache miss or property isn't ID, so scan the entity data sequentially
for idx, entity in ipairs(entData) do
if useCache then
setCache(entityType, entity.id, idx)
end
if entity[propName] == propValue then
return entity
end
end
end
function p.getEntityByID(entityType, entityID)
return p.getEntityByProperty(entityType, 'id', entityID)
end
function p.getEntityByName(entityType, entityName)
return p.getEntityByProperty(entityType, 'name', entityName)
end
-- Takes an entity type and a function, returning a list of entities for which
-- the function evaluates to true.
-- The function must accept one parameter (the entity being checked), and must
-- return either true or false
function p.getEntities(entityType, checkFunc)
local result = {}
local entityCount = 0
if type(entityType) ~= 'string' and type(entityType) ~= 'table' then
error('Entity type name must be a string or table', 2)
elseif type(checkFunc) ~= 'function' then
error('Check function name must be a function', 2)
end
local entData, useCache = nil, false
if type(entityType) == 'string' then
entData = GameData[entityType]
useCache = true
else
-- Function was passed a table of entities rather than a entity type
entData = entityType
end
if entData == nil and type(entityType) == 'string' then
error('No such entity type: ' .. entityType, 2)
elseif type(entData) ~= 'table' then
error('Entity data is not a table: ' .. entityType, 2)
elseif type(entData[1]) ~= 'table' then
error('Entity data is not composed of entities: ' .. entityType, 2)
end
for idx, entity in ipairs(entData) do
if useCache then
setCache(entityType, entity.id, idx)
end
if checkFunc(entity) then
entityCount = entityCount + 1
result[entityCount] = entity
end
end
return result
end
-- Sorts the given dataTable by ID into the same order as orderTable
-- keepUnsorted specifies whether unsorted elements are included within the output. Default: true
-- unsorted elements will be at the end of the array, order is not guaranteed
-- idKey specifies the ID key to sort upon. Default: id
-- orderFunc specifies a custom order function if the default behaviour is not desired
-- Example - Sorts combat areas into the same order as displayed in game:
-- p.sortByOrderTable(p.rawData.combatAreas, p.rawData.combatAreaDisplayOrder)
function p.sortByOrderTable(dataTable, orderTable, keepUnsorted, idKey, orderFunc)
-- Create index table from orderTable
local orderIdx = {}
for idx, v in ipairs(orderTable) do
orderIdx[v] = idx
end
-- Determine if user-specified or default paramater values are to be used
if type(keepUnsorted) ~= 'boolean' then
keepUnsorted = true
end
if type(idKey) ~= 'string' then
idKey = 'id'
end
if type(orderFunc) ~= 'function' then
orderFunc = function(k1, k2)
local o1, o2 = orderIdx[k1[idKey]], orderIdx[k2[idKey]]
if o1 == nil or o2 == nil then
return false
else
return orderIdx[k1[idKey]] < orderIdx[k2[idKey]]
end
end
end
-- Build unsorted result table, removing unsorted elements if requested
local resultTable = {}
local resultItemCount = 0
for idx, v in ipairs(dataTable) do
local keyVal = v[idKey]
if keyVal ~= nil then
if keepUnsorted or orderIdx[keyVal] ~= nil then
resultItemCount = resultItemCount + 1
resultTable[resultItemCount] = v
end
end
end
-- Sort table
table.sort(resultTable, orderFunc)
return resultTable
end
return p