12,855
edits
(Initial implementation) |
(Amend to use alternative data pages) |
||
(13 intermediate revisions by the same user not shown) | |||
Line 4: | Line 4: | ||
local p = {} | local p = {} | ||
local GameData = mw. | local GameData1 = mw.loadJsonData('Module:GameData/data') | ||
local GameData2 = mw.loadJsonData('Module: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 = {} | local indexCache = {} | ||
-- Expose underlying data should other modules require it | -- Expose underlying data should other modules require it | ||
p.rawData = GameData | 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. | -- If the entity ID is within the cache for the given entity type, then return it. | ||
-- Otherwise, returns nil | -- Otherwise, returns nil | ||
function getCache(entityType, ID) | local function getCache(entityType, ID) | ||
if type(entityType) == 'string' and type(ID) == 'string' then | if type(entityType) == 'string' and type(ID) == 'string' then | ||
local cacheCat = indexCache[entityType] | local cacheCat = indexCache[entityType] | ||
Line 22: | Line 75: | ||
-- Sets the cache for entity ID within the given entity type to idx. | -- Sets the cache for entity ID within the given entity type to idx. | ||
function setCache(entityType, ID, idx) | local function setCache(entityType, ID, idx) | ||
if type(entityType) == 'string' and type(ID) == 'string' and type(idx) == 'number' | if type(entityType) == 'string' and type(ID) == 'string' and type(idx) == 'number' then | ||
if indexCache[entityType] == nil then | if indexCache[entityType] == nil then | ||
indexCache[entityType] = {} | indexCache[entityType] = {} | ||
end | end | ||
if indexCache[entityType][ID] == nil then | if indexCache[entityType][ID] == nil then | ||
indexCache[entityType][ID] = idx | indexCache[entityType][ID] = math.floor(idx) | ||
end | end | ||
end | end | ||
end | end | ||
Line 43: | Line 92: | ||
for idx, skill in ipairs(GameData.skillData) do | for idx, skill in ipairs(GameData.skillData) do | ||
if skill.skillID == skillID then | if skill.skillID == skillID then | ||
return skill | return skill.data | ||
end | end | ||
end | end | ||
end | end | ||
-- Takes an entity type & property name/value, returning an object representing the | -- 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. | -- entity with the given property (if found). If not found, then nil is returned. | ||
function p.getEntityByProperty(entityType, propName, propValue) | function p.getEntityByProperty(entityType, propName, propValue) | ||
if type(entityType) ~= 'string' then | if type(entityType) ~= 'string' and type(entityType) ~= 'table' then | ||
error('Entity type name must be a string', 2) | error('Entity type name must be a string or table', 2) | ||
elseif type(propName) ~= 'string' then | elseif type(propName) ~= 'string' then | ||
error('Property name must be a string', 2) | error('Property name must be a string', 2) | ||
end | end | ||
local entData = GameData[entityType] | 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 | if entData == nil then | ||
error('No such entity type: ' .. entityType, 2) | error('No such entity type: ' .. entityType, 2) | ||
elseif type(entData) ~= 'table' then | elseif type(entData) ~= 'table' then | ||
error('Entity data is not a table: ' .. entityType, 2) | error('Entity data is not a table: ' .. entityType, 2) | ||
elseif type(entData[1]) ~= 'table' or entData[1] | elseif type(entData[1]) ~= 'table' or entData[1][propName] == nil then | ||
error('Entity data is not composed of entities: ' .. entityType, 2) | error('Entity data is not composed of entities: ' .. entityType, 2) | ||
end | end | ||
-- Check if this ID is already cached | -- Check if this ID is already cached | ||
if propName == 'id' then | if propName == 'id' and useCache then | ||
local cacheIdx = getCache(entityType, propValue) | local cacheIdx = getCache(entityType, propValue) | ||
if cacheIdx ~= nil then | if cacheIdx ~= nil then | ||
Line 74: | Line 130: | ||
-- Cache miss or property isn't ID, so scan the entity data sequentially | -- Cache miss or property isn't ID, so scan the entity data sequentially | ||
for idx, entity in ipairs(entData) do | for idx, entity in ipairs(entData) do | ||
setCache(entityType, entity.id, idx) | if useCache then | ||
setCache(entityType, entity.id, idx) | |||
end | |||
if entity[propName] == propValue then | if entity[propName] == propValue then | ||
return entity | return entity | ||
Line 96: | Line 154: | ||
local result = {} | local result = {} | ||
local entityCount = 0 | local entityCount = 0 | ||
if type(entityType) ~= 'string' then | if type(entityType) ~= 'string' and type(entityType) ~= 'table' then | ||
error('Entity type name must be a string', 2) | error('Entity type name must be a string or table', 2) | ||
elseif type(checkFunc) ~= 'function' then | elseif type(checkFunc) ~= 'function' then | ||
error('Check function name must be a function', 2) | error('Check function name must be a function', 2) | ||
end | end | ||
local entData = GameData[entityType] | local entData, useCache = nil, false | ||
if entData == nil then | 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) | error('No such entity type: ' .. entityType, 2) | ||
elseif type(entData) ~= 'table' then | elseif type(entData) ~= 'table' then | ||
error('Entity data is not a table: ' .. entityType, 2) | error('Entity data is not a table: ' .. entityType, 2) | ||
elseif type(entData[1]) ~= 'table' | elseif type(entData[1]) ~= 'table' then | ||
error('Entity data is not composed of entities: ' .. entityType, 2) | error('Entity data is not composed of entities: ' .. entityType, 2) | ||
end | end | ||
for idx, entity in ipairs(entData) do | for idx, entity in ipairs(entData) do | ||
setCache(entityType, entity.id, idx) | if useCache then | ||
setCache(entityType, entity.id, idx) | |||
end | |||
if checkFunc(entity) then | if checkFunc(entity) then | ||
entityCount = entityCount + 1 | entityCount = entityCount + 1 | ||
Line 119: | Line 186: | ||
end | end | ||
-- | -- Sorts the given dataTable by ID into the same order as orderTable | ||
function p. | -- keepUnsorted specifies whether unsorted elements are included within the output. Default: true | ||
local | -- unsorted elements will be at the end of the array, order is not guaranteed | ||
return | -- 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 | end | ||
return p | return p |