Module:AchievementSystem: Difference between revisions
Appearance
// via Wikitext Extension for VSCode |
// via Wikitext Extension for VSCode |
||
| Line 1: | Line 1: | ||
-- Module:AchievementSystem | -- Module:AchievementSystem | ||
-- Achievement system that loads data from MediaWiki:AchievementData.json | -- Achievement system that loads data from MediaWiki:AchievementData.json. | ||
-- STYLING NOTE: All achievement styling is defined in CSS/Templates.css, not in the JSON. | -- STYLING NOTE: All achievement styling is defined in CSS/Templates.css, not in the JSON. | ||
-- This module only assigns CSS classes based on achievement IDs in the format: | -- This module only assigns CSS classes based on achievement IDs in the format: | ||
-- .person-template .template-title.achievement-{id}::after {} | -- .person-template .template-title.achievement-{id}::after {} | ||
-- | -- | ||
-- The module does not use any styling information from the JSON data structure. | -- The module does not use any styling information from the JSON data structure. | ||
| Line 9: | Line 9: | ||
local Achievements = {} | local Achievements = {} | ||
-- Debug configuration | -- Debug configuration | ||
local DEBUG_MODE = true | local DEBUG_MODE = true | ||
local function debugLog(message) | local function debugLog(message) | ||
if not DEBUG_MODE then return end | if not DEBUG_MODE then return end | ||
pcall(function() | pcall(function() | ||
mw.logObject({ | mw.logObject({ | ||
| Line 22: | Line 20: | ||
}) | }) | ||
end) | end) | ||
mw.log("ACHIEVEMENT-DEBUG: " .. message) | mw.log("ACHIEVEMENT-DEBUG: " .. message) | ||
end | end | ||
-- | -- Attempt to load JSON handling | ||
local json | local json | ||
local jsonLoaded = pcall(function() | local jsonLoaded = pcall(function() | ||
| Line 33: | Line 29: | ||
end) | end) | ||
if not jsonLoaded or not json then | if not jsonLoaded or not json then | ||
json = { decode = function() return nil end } | json = { decode = function() return nil end } | ||
| Line 39: | Line 34: | ||
end | end | ||
-- | -- Simple HTML encode fallback | ||
local htmlEncode | local function htmlEncode(str) | ||
if mw.text and mw.text.htmlEncode then | if mw.text and mw.text.htmlEncode then | ||
return mw.text.htmlEncode(str or '') | return mw.text.htmlEncode(str or '') | ||
else | else | ||
return (str or ''):gsub('&', '&'):gsub('<', '<'):gsub('>', '>'):gsub('"', '"') | return (str or '') | ||
:gsub('&', '&') | |||
:gsub('<', '<') | |||
:gsub('>', '>') | |||
:gsub('"', '"') | |||
end | end | ||
end | end | ||
-- | -- The page storing JSON data | ||
local ACHIEVEMENT_DATA_PAGE = 'MediaWiki:AchievementData.json' | local ACHIEVEMENT_DATA_PAGE = 'MediaWiki:AchievementData.json' | ||
-- Cache for achievement data (within one request) | |||
-- Cache for achievement data (within request) | |||
local dataCache = nil | local dataCache = nil | ||
-- Default data | -- Default data if JSON load fails | ||
local DEFAULT_DATA = { | local DEFAULT_DATA = { | ||
schema_version = 1, | schema_version = 1, | ||
last_updated = os.date('!%Y-%m-%dT%H:%M:%SZ'), | last_updated = os.date('!%Y-%m-%dT%H:%M:%SZ'), | ||
achievement_types = {}, | achievement_types = {}, | ||
user_achievements = {}, | user_achievements = {}, | ||
cache_control = { version = 0 } | cache_control = { version = 0 } | ||
} | } | ||
-- | -------------------------------------------------------------------------------- | ||
-- Internal: load achievement data from the page, or from cache | |||
-------------------------------------------------------------------------------- | |||
function Achievements.loadData() | function Achievements.loadData() | ||
mw.log("JSON-DEBUG: Starting to load achievement data") | mw.log("JSON-DEBUG: Starting to load achievement data") | ||
if dataCache then | if dataCache then | ||
mw.log("JSON-DEBUG: Using request-level cached data") | mw.log("JSON-DEBUG: Using request-level cached data") | ||
return dataCache | return dataCache | ||
end | end | ||
local success, data = pcall(function() | local success, data = pcall(function() | ||
-- First try to load from parser cache | -- First try to load from parser cache | ||
| Line 118: | Line 78: | ||
return mw.loadData('Module:AchievementSystem') | return mw.loadData('Module:AchievementSystem') | ||
end) | end) | ||
if loadDataSuccess and cachedData then | if loadDataSuccess and cachedData then | ||
mw.log("JSON-DEBUG: Using mw.loadData cached data") | mw.log("JSON-DEBUG: Using mw.loadData cached data") | ||
| Line 125: | Line 85: | ||
mw.log("JSON-DEBUG: mw.loadData failed or returned empty, using direct page load") | mw.log("JSON-DEBUG: mw.loadData failed or returned empty, using direct page load") | ||
end | end | ||
local pageTitle = mw.title.new(ACHIEVEMENT_DATA_PAGE) | |||
if not pageTitle or not pageTitle.exists then | |||
local | mw.log("JSON-DEBUG: " .. ACHIEVEMENT_DATA_PAGE .. " does not exist or title creation failed") | ||
if not | |||
mw.log("JSON-DEBUG: | |||
return DEFAULT_DATA | return DEFAULT_DATA | ||
end | end | ||
local content = pageTitle:getContent() | |||
content = | |||
if not content or content == '' then | if not content or content == '' then | ||
mw.log("JSON-DEBUG: Page content is empty") | mw.log("JSON-DEBUG: Page content is empty") | ||
return DEFAULT_DATA | return DEFAULT_DATA | ||
end | end | ||
mw.log("JSON-DEBUG: Raw JSON content length: " .. #content) | mw.log("JSON-DEBUG: Raw JSON content length: " .. #content) | ||
local parseSuccess, parsedData = pcall(function() | |||
return json.decode(content) | |||
local parseSuccess, parsedData = pcall(function() | |||
return json.decode(content) | |||
end) | end) | ||
if not parseSuccess or not parsedData then | if not parseSuccess or not parsedData then | ||
mw.log("JSON-DEBUG: JSON parse failed: " .. tostring(parsedData or | mw.log("JSON-DEBUG: JSON parse failed: " .. tostring(parsedData or 'unknown error')) | ||
return DEFAULT_DATA | return DEFAULT_DATA | ||
end | end | ||
mw.log("JSON-DEBUG: Successfully loaded achievement data") | mw.log("JSON-DEBUG: Successfully loaded achievement data") | ||
return parsedData | return parsedData | ||
end) | end) | ||
if not success or not data then | if not success or not data then | ||
mw.log("JSON-DEBUG: Critical error in load process: " .. tostring(data or 'unknown error')) | mw.log("JSON-DEBUG: Critical error in load process: " .. tostring(data or 'unknown error')) | ||
data = DEFAULT_DATA | data = DEFAULT_DATA | ||
end | end | ||
dataCache = data | dataCache = data | ||
return data | return data | ||
end | end | ||
-- | -------------------------------------------------------------------------------- | ||
Checks if a user has any achievements | -- Checks if a user has any achievements | ||
-------------------------------------------------------------------------------- | |||
function Achievements.hasAchievements(pageId) | |||
if not pageId or pageId == '' then | |||
return false | |||
end | |||
local data = Achievements.loadData() | local data = Achievements.loadData() | ||
if not data or not data.user_achievements then return false | if not data or not data.user_achievements then | ||
return false | |||
end | |||
local key = tostring(pageId) | local key = tostring(pageId) | ||
if data.user_achievements[key] and #data.user_achievements[key] > 0 then | if data.user_achievements[key] and #data.user_achievements[key] > 0 then | ||
return true | return true | ||
end | end | ||
-- Check for achievements under n-prefixed key ( | -- Check for achievements under n-prefixed key (legacy) | ||
if key:match("^%d+$") | if key:match("^%d+$") then | ||
local alt = "n" .. key | |||
if data.user_achievements[alt] and #data.user_achievements[alt] > 0 then | |||
return true | |||
end | |||
end | end | ||
return false | return false | ||
end | end | ||
-- | -------------------------------------------------------------------------------- | ||
-- Retrieves the display name for a given achievement type | |||
-------------------------------------------------------------------------------- | |||
function Achievements.getAchievementName(achievementType) | function Achievements.getAchievementName(achievementType) | ||
if not achievementType or achievementType == '' then | |||
if not achievementType or achievementType == '' then | |||
debugLog("Empty achievement type provided to getAchievementName") | debugLog("Empty achievement type provided to getAchievementName") | ||
mw.log("ACHIEVEMENT-NAME-ERROR: Received empty achievementType") | |||
mw.log("ACHIEVEMENT-NAME-ERROR: | return 'Unknown' | ||
return 'Unknown' | |||
end | end | ||
debugLog("Looking up achievement name for type: '" .. tostring(achievementType) .. "'") | debugLog("Looking up achievement name for type: '" .. tostring(achievementType) .. "'") | ||
local data = Achievements.loadData() | local data = Achievements.loadData() | ||
if not data or not data.achievement_types then | |||
mw.log("ACHIEVEMENT-NAME-ERROR: No achievement data or achievement_types missing") | |||
mw.log("ACHIEVEMENT-NAME-ERROR: No achievement data | |||
return achievementType | return achievementType | ||
end | end | ||
for i, typeData in ipairs(data.achievement_types) do | for i, typeData in ipairs(data.achievement_types) do | ||
if typeData.id == achievementType then | |||
if typeData.name and typeData.name ~= "" then | |||
debugLog("Found achievement: " .. typeData.id .. " with name: " .. typeData.name) | |||
return typeData.name | |||
else | |||
mw.log("ACHIEVEMENT-NAME-WARNING: '" .. typeData.id .. "' has no name") | |||
return achievementType | |||
end | end | ||
end | end | ||
end | end | ||
mw.log("ACHIEVEMENT-NAME-ERROR: No achievement found with type: '" .. achievementType .. "'") | |||
return achievementType | |||
mw.log("ACHIEVEMENT-NAME-ERROR: No achievement found with type: '" .. | |||
return achievementType | |||
end | end | ||
-- | -------------------------------------------------------------------------------- | ||
-- Finds the top-tier achievement for a user | |||
-- Returns the CSS class and the achievement name | |||
-------------------------------------------------------------------------------- | |||
function Achievements.getTitleClass(pageId) | function Achievements.getTitleClass(pageId) | ||
if not pageId or pageId == '' then | if not pageId or pageId == '' then | ||
debugLog("Empty page ID provided to getTitleClass") | debugLog("Empty page ID provided to getTitleClass") | ||
return '', '' | return '', '' | ||
end | end | ||
local data = Achievements.loadData() | local data = Achievements.loadData() | ||
if not data or not data.user_achievements then | if not data or not data.user_achievements then | ||
debugLog("No achievement data available") | debugLog("No achievement data available in getTitleClass") | ||
return '', '' | return '', '' | ||
end | end | ||
local key = tostring(pageId) | local key = tostring(pageId) | ||
debugLog("Looking up achievements for ID: " .. key) | debugLog("Looking up achievements for ID: " .. key) | ||
local userAchievements = data.user_achievements[key] or {} | local userAchievements = data.user_achievements[key] or {} | ||
if #userAchievements == 0 and key:match("^%d+$") then | if #userAchievements == 0 and key:match("^%d+$") then | ||
local | local altKey = "n" .. key | ||
userAchievements = data.user_achievements[altKey] or {} | |||
userAchievements = data.user_achievements[ | |||
end | end | ||
if #userAchievements == 0 then | if #userAchievements == 0 then | ||
debugLog("No achievements found") | debugLog("No achievements found for user " .. key) | ||
return '', '' | return '', '' | ||
end | end | ||
local highestTier = 999 | |||
local highestAchievement = nil | local highestAchievement = nil | ||
for _, achievement in ipairs(userAchievements) do | for _, achievement in ipairs(userAchievements) do | ||
local | local achType = achievement.type | ||
for _, typeData in ipairs(data.achievement_types) do | |||
if typeData.id == achType then | |||
for _, typeData in ipairs(data.achievement_types | local tier = typeData.tier or 999 | ||
if typeData.id == | if tier < highestTier then | ||
highestTier = tier | |||
highestAchievement = typeData | |||
end | |||
end | end | ||
end | end | ||
end | end | ||
if not highestAchievement or not highestAchievement.id then | if not highestAchievement or not highestAchievement.id then | ||
debugLog("No valid achievement | debugLog("No valid top-tier achievement found for user " .. key) | ||
return '', '' | return '', '' | ||
end | end | ||
local | local cssClass = "achievement-" .. highestAchievement.id | ||
local | local displayName = highestAchievement.name or highestAchievement.id or "Award" | ||
debugLog("Using top-tier achievement: " .. cssClass .. " with name: " .. displayName) | |||
debugLog("Using achievement | return cssClass, displayName | ||
return | |||
end | end | ||
-- | -------------------------------------------------------------------------------- | ||
-- Renders an achievement box showing the top-tier achievement | |||
-------------------------------------------------------------------------------- | |||
function Achievements.renderAchievementBox(pageId) | function Achievements.renderAchievementBox(pageId) | ||
if not pageId or pageId == '' then | |||
if | return '' | ||
return ' | |||
end | end | ||
local data = Achievements.loadData() | local data = Achievements.loadData() | ||
if not data or not data.user_achievements then return '' | if not data or not data.user_achievements then | ||
return '' | |||
end | |||
local key = tostring(pageId) | local key = tostring(pageId) | ||
local | local userAchievements = data.user_achievements[key] | ||
if (not userAchievements or #userAchievements == 0) and key:match("^%d+$") then | |||
userAchievements = data.user_achievements["n" .. key] | userAchievements = data.user_achievements["n" .. key] | ||
end | |||
if not userAchievements or #userAchievements == 0 then | |||
return '' | return '' | ||
end | end | ||
local highestTier = 999 | local highestTier = 999 | ||
local topAch = nil | |||
for _, achievement in ipairs(userAchievements) do | for _, achievement in ipairs(userAchievements) do | ||
local | local achType = achievement.type | ||
for _, typeData in ipairs(data.achievement_types) do | |||
for _, typeData in ipairs(data.achievement_types | if typeData.id == achType then | ||
if typeData.id == | local tier = typeData.tier or 999 | ||
if tier < highestTier then | |||
highestTier = tier | |||
topAch = typeData | |||
end | |||
end | end | ||
end | end | ||
end | end | ||
if topAch then | |||
if | return string.format( | ||
return '<div class="achievement-box-simple" data-achievement-type="' | '<div class="achievement-box-simple" data-achievement-type="%s">%s</div>', | ||
topAch.id, | |||
htmlEncode(topAch.name or topAch.id or "") | |||
) | |||
end | end | ||
return '' | return '' | ||
end | end | ||
-- | -------------------------------------------------------------------------------- | ||
-- Marks a page for cache purging | |||
-------------------------------------------------------------------------------- | |||
function Achievements.trackPage(pageId, pageName) | function Achievements.trackPage(pageId, pageName) | ||
return true | return true | ||
end | end | ||
-- | -------------------------------------------------------------------------------- | ||
Retrieves a specific achievement | -- Retrieves a specific achievement from a user if present | ||
-------------------------------------------------------------------------------- | |||
function Achievements.getSpecificAchievement(pageId, achievementType) | function Achievements.getSpecificAchievement(pageId, achievementType) | ||
debugLog("ACHIEVEMENT-DEBUG: Looking for '" .. tostring(achievementType) .. | |||
debugLog("ACHIEVEMENT-DEBUG: Looking for '" .. achievementType .. "' | "' in page ID: " .. tostring(pageId)) | ||
if not pageId or pageId == '' | if not pageId or not achievementType or pageId == '' then | ||
debugLog("ACHIEVEMENT-DEBUG: Invalid | debugLog("ACHIEVEMENT-DEBUG: Invalid arguments for getSpecificAchievement") | ||
return nil | return nil | ||
end | end | ||
local data = Achievements.loadData() | local data = Achievements.loadData() | ||
if not data or not data.user_achievements then | if not data or not data.user_achievements then | ||
debugLog("ACHIEVEMENT-DEBUG: No achievement data | debugLog("ACHIEVEMENT-DEBUG: No achievement data loaded") | ||
return nil | return nil | ||
end | end | ||
local key = tostring(pageId) | local key = tostring(pageId) | ||
local userAchievements = data.user_achievements[key] or {} | |||
if #userAchievements == 0 and key:match("^%d+$") then | |||
userAchievements = data.user_achievements["n" .. key] or {} | |||
end | end | ||
for _, achievement in ipairs(userAchievements) do | |||
if achievement.type == achievementType then | |||
return achievement | |||
end | end | ||
end | end | ||
return nil | return nil | ||
end | end | ||
return Achievements | return Achievements | ||
Revision as of 01:30, 1 April 2025
Documentation for this module may be created at Module:AchievementSystem/doc
-- Module:AchievementSystem
-- Achievement system that loads data from MediaWiki:AchievementData.json.
-- STYLING NOTE: All achievement styling is defined in CSS/Templates.css, not in the JSON.
-- This module only assigns CSS classes based on achievement IDs in the format:
-- .person-template .template-title.achievement-{id}::after {}
--
-- The module does not use any styling information from the JSON data structure.
local Achievements = {}
-- Debug configuration
local DEBUG_MODE = true
local function debugLog(message)
if not DEBUG_MODE then return end
pcall(function()
mw.logObject({
system = "achievement_simple",
message = message,
timestamp = os.date('%H:%M:%S')
})
end)
mw.log("ACHIEVEMENT-DEBUG: " .. message)
end
-- Attempt to load JSON handling
local json
local jsonLoaded = pcall(function()
json = require('Module:JSON')
end)
if not jsonLoaded or not json then
json = { decode = function() return nil end }
debugLog('WARNING: Module:JSON not available, achievement features will be limited')
end
-- Simple HTML encode fallback
local function htmlEncode(str)
if mw.text and mw.text.htmlEncode then
return mw.text.htmlEncode(str or '')
else
return (str or '')
:gsub('&', '&')
:gsub('<', '<')
:gsub('>', '>')
:gsub('"', '"')
end
end
-- The page storing JSON data
local ACHIEVEMENT_DATA_PAGE = 'MediaWiki:AchievementData.json'
-- Cache for achievement data (within one request)
local dataCache = nil
-- Default data if JSON load fails
local DEFAULT_DATA = {
schema_version = 1,
last_updated = os.date('!%Y-%m-%dT%H:%M:%SZ'),
achievement_types = {},
user_achievements = {},
cache_control = { version = 0 }
}
--------------------------------------------------------------------------------
-- Internal: load achievement data from the page, or from cache
--------------------------------------------------------------------------------
function Achievements.loadData()
mw.log("JSON-DEBUG: Starting to load achievement data")
if dataCache then
mw.log("JSON-DEBUG: Using request-level cached data")
return dataCache
end
local success, data = pcall(function()
-- First try to load from parser cache
local loadDataSuccess, cachedData = pcall(function()
return mw.loadData('Module:AchievementSystem')
end)
if loadDataSuccess and cachedData then
mw.log("JSON-DEBUG: Using mw.loadData cached data")
return cachedData
else
mw.log("JSON-DEBUG: mw.loadData failed or returned empty, using direct page load")
end
local pageTitle = mw.title.new(ACHIEVEMENT_DATA_PAGE)
if not pageTitle or not pageTitle.exists then
mw.log("JSON-DEBUG: " .. ACHIEVEMENT_DATA_PAGE .. " does not exist or title creation failed")
return DEFAULT_DATA
end
local content = pageTitle:getContent()
if not content or content == '' then
mw.log("JSON-DEBUG: Page content is empty")
return DEFAULT_DATA
end
mw.log("JSON-DEBUG: Raw JSON content length: " .. #content)
local parseSuccess, parsedData = pcall(function()
return json.decode(content)
end)
if not parseSuccess or not parsedData then
mw.log("JSON-DEBUG: JSON parse failed: " .. tostring(parsedData or 'unknown error'))
return DEFAULT_DATA
end
mw.log("JSON-DEBUG: Successfully loaded achievement data")
return parsedData
end)
if not success or not data then
mw.log("JSON-DEBUG: Critical error in load process: " .. tostring(data or 'unknown error'))
data = DEFAULT_DATA
end
dataCache = data
return data
end
--------------------------------------------------------------------------------
-- Checks if a user has any achievements
--------------------------------------------------------------------------------
function Achievements.hasAchievements(pageId)
if not pageId or pageId == '' then
return false
end
local data = Achievements.loadData()
if not data or not data.user_achievements then
return false
end
local key = tostring(pageId)
if data.user_achievements[key] and #data.user_achievements[key] > 0 then
return true
end
-- Check for achievements under n-prefixed key (legacy)
if key:match("^%d+$") then
local alt = "n" .. key
if data.user_achievements[alt] and #data.user_achievements[alt] > 0 then
return true
end
end
return false
end
--------------------------------------------------------------------------------
-- Retrieves the display name for a given achievement type
--------------------------------------------------------------------------------
function Achievements.getAchievementName(achievementType)
if not achievementType or achievementType == '' then
debugLog("Empty achievement type provided to getAchievementName")
mw.log("ACHIEVEMENT-NAME-ERROR: Received empty achievementType")
return 'Unknown'
end
debugLog("Looking up achievement name for type: '" .. tostring(achievementType) .. "'")
local data = Achievements.loadData()
if not data or not data.achievement_types then
mw.log("ACHIEVEMENT-NAME-ERROR: No achievement data or achievement_types missing")
return achievementType
end
for i, typeData in ipairs(data.achievement_types) do
if typeData.id == achievementType then
if typeData.name and typeData.name ~= "" then
debugLog("Found achievement: " .. typeData.id .. " with name: " .. typeData.name)
return typeData.name
else
mw.log("ACHIEVEMENT-NAME-WARNING: '" .. typeData.id .. "' has no name")
return achievementType
end
end
end
mw.log("ACHIEVEMENT-NAME-ERROR: No achievement found with type: '" .. achievementType .. "'")
return achievementType
end
--------------------------------------------------------------------------------
-- Finds the top-tier achievement for a user
-- Returns the CSS class and the achievement name
--------------------------------------------------------------------------------
function Achievements.getTitleClass(pageId)
if not pageId or pageId == '' then
debugLog("Empty page ID provided to getTitleClass")
return '', ''
end
local data = Achievements.loadData()
if not data or not data.user_achievements then
debugLog("No achievement data available in getTitleClass")
return '', ''
end
local key = tostring(pageId)
debugLog("Looking up achievements for ID: " .. key)
local userAchievements = data.user_achievements[key] or {}
if #userAchievements == 0 and key:match("^%d+$") then
local altKey = "n" .. key
userAchievements = data.user_achievements[altKey] or {}
end
if #userAchievements == 0 then
debugLog("No achievements found for user " .. key)
return '', ''
end
local highestTier = 999
local highestAchievement = nil
for _, achievement in ipairs(userAchievements) do
local achType = achievement.type
for _, typeData in ipairs(data.achievement_types) do
if typeData.id == achType then
local tier = typeData.tier or 999
if tier < highestTier then
highestTier = tier
highestAchievement = typeData
end
end
end
end
if not highestAchievement or not highestAchievement.id then
debugLog("No valid top-tier achievement found for user " .. key)
return '', ''
end
local cssClass = "achievement-" .. highestAchievement.id
local displayName = highestAchievement.name or highestAchievement.id or "Award"
debugLog("Using top-tier achievement: " .. cssClass .. " with name: " .. displayName)
return cssClass, displayName
end
--------------------------------------------------------------------------------
-- Renders an achievement box showing the top-tier achievement
--------------------------------------------------------------------------------
function Achievements.renderAchievementBox(pageId)
if not pageId or pageId == '' then
return ''
end
local data = Achievements.loadData()
if not data or not data.user_achievements then
return ''
end
local key = tostring(pageId)
local userAchievements = data.user_achievements[key]
if (not userAchievements or #userAchievements == 0) and key:match("^%d+$") then
userAchievements = data.user_achievements["n" .. key]
end
if not userAchievements or #userAchievements == 0 then
return ''
end
local highestTier = 999
local topAch = nil
for _, achievement in ipairs(userAchievements) do
local achType = achievement.type
for _, typeData in ipairs(data.achievement_types) do
if typeData.id == achType then
local tier = typeData.tier or 999
if tier < highestTier then
highestTier = tier
topAch = typeData
end
end
end
end
if topAch then
return string.format(
'<div class="achievement-box-simple" data-achievement-type="%s">%s</div>',
topAch.id,
htmlEncode(topAch.name or topAch.id or "")
)
end
return ''
end
--------------------------------------------------------------------------------
-- Marks a page for cache purging
--------------------------------------------------------------------------------
function Achievements.trackPage(pageId, pageName)
return true
end
--------------------------------------------------------------------------------
-- Retrieves a specific achievement from a user if present
--------------------------------------------------------------------------------
function Achievements.getSpecificAchievement(pageId, achievementType)
debugLog("ACHIEVEMENT-DEBUG: Looking for '" .. tostring(achievementType) ..
"' in page ID: " .. tostring(pageId))
if not pageId or not achievementType or pageId == '' then
debugLog("ACHIEVEMENT-DEBUG: Invalid arguments for getSpecificAchievement")
return nil
end
local data = Achievements.loadData()
if not data or not data.user_achievements then
debugLog("ACHIEVEMENT-DEBUG: No achievement data loaded")
return nil
end
local key = tostring(pageId)
local userAchievements = data.user_achievements[key] or {}
if #userAchievements == 0 and key:match("^%d+$") then
userAchievements = data.user_achievements["n" .. key] or {}
end
for _, achievement in ipairs(userAchievements) do
if achievement.type == achievementType then
return achievement
end
end
return nil
end
return Achievements