'use strict'
const Discord = require('discord.js')
const { BaseClass } = require('./BaseClass')
const { ErrorCodes } = require('../Errors/ErrorCodes')
const { MissingValue, InvalidValue } = require('../Errors/LME')
const { AchievementStatus } = require('../Interfaces')
const { isNotPositiveInteger } = require('../Utils/ValidateInteger')
/** @typedef {import('../../typings').Rank} Rank */
/** @typedef {import('../../typings').Achievement} Achievement */
/** @typedef {import('../../typings').UserEntry} UserEntry */
/**
* Represents a user in the database/cache.
* @extends BaseClass
*/
class UserCard extends BaseClass {
/** @type {Discord.Client} - Discord Client */
client
/** @type {Discord.Snowflake} - Users Id */
id
/** @type {String} - Users username */
username
/** @type {Number} - Users actual xp */
xp
/** @type {Number} Users level */
level
/** @type {Number} Users xp needed to level up */
maxXpToLevelUp
/** @type {Rank} - Users rank */
rank
/** @type {Achievement[]} - Users achievements */
achievements
/**
* @param {Discord.Client} client - Discord client
* @param {UserEntry} data - Data extracted from the database/cache
*/
constructor (client, data) {
super(client)
this.#checkData(data)
this.client = client
this.id = data.id
this.username = data.username
this.xp = data.xp
this.level = data.level
this.maxXpToLevelUp = data.maxXpToLevelUp
this.rank = data.rank
this.achievements = data.achievements
}
/**
* Evaluates if the user has all the required data.
* @param {UserEntry} data - Data extracted from the database/cache
* @throws {MissingValue} - If a property is missing.
* @throws {InvalidValue} - If a property is not valid.
* @private
*/
#checkData (data) {
if (!data) throw new MissingValue(ErrorCodes.MissingArgument, 'data')
if (typeof data !== 'object') throw new InvalidValue(ErrorCodes.InvalidValue, 'data', 'an object')
if (!data.id) throw new MissingValue(ErrorCodes.MissingProperty, 'id')
if (typeof data.id !== 'string') throw new InvalidValue(ErrorCodes.InvalidValue, 'id', 'a string')
if (!data.username) throw new MissingValue(ErrorCodes.MissingProperty, 'username')
if (typeof data.username !== 'string') throw new InvalidValue(ErrorCodes.InvalidValue, 'username', 'a string')
if (!data.xp && data.xp !== 0) throw new MissingValue(ErrorCodes.MissingProperty, 'xp')
if (typeof data.xp !== 'number') throw new InvalidValue(ErrorCodes.InvalidValue, 'xp', 'a positive integer')
if (!data.level && data.level !== 0) throw new MissingValue(ErrorCodes.MissingProperty, 'level')
if (typeof data.level !== 'number') throw new InvalidValue(ErrorCodes.InvalidValue, 'level', 'a positive integer')
if (!data.maxXpToLevelUp) throw new MissingValue(ErrorCodes.MissingProperty, 'maxXpToLevelUp')
if (typeof data.maxXpToLevelUp !== 'number') throw new InvalidValue(ErrorCodes.InvalidValue, 'maxXpToLevelUp', 'a positive integer')
if (!data.rank) throw new MissingValue(ErrorCodes.MissingProperty, 'rank')
if (typeof data.rank !== 'object') throw new InvalidValue(ErrorCodes.InvalidValue, 'rank', 'an object')
if (!data.achievements) throw new MissingValue(ErrorCodes.MissingProperty, 'achievements')
if (!Array.isArray(data.achievements)) throw new InvalidValue(ErrorCodes.InvalidValue, 'achievements', 'an array')
}
/**
* Updates a achievement progress and unlocks it if the progress is equal or greater than the achievement's max progress.
*
* The new progress will overwrite the old progress so if you want add more progress to the achievement you need to add the old progress to the new progress.
*
* @example
* user.achievementProgress(achievement, achievement.progress[0] + 10)
*
* @param {Achievement} achievement - Achievement to update.
* @param {Number} progress - Progress to update.
* @returns {Boolean} - If the achievement was unlocked.
*/
achievementProgress (achievement, progress) {
// Check params
if (!achievement) throw new MissingValue(ErrorCodes.MissingArgument, 'achievement')
if (achievement === null || achievement === undefined || typeof achievement !== 'object') throw new InvalidValue(ErrorCodes.InvalidValue, 'achievement', 'an object')
if (progress === null || progress === undefined || typeof progress !== 'number' || isNotPositiveInteger(progress)) throw new InvalidValue(ErrorCodes.InvalidValue, 'progress', 'a positive integer')
if (achievement.progress[1] < progress) throw new InvalidValue(ErrorCodes.InvalidValue, 'progress', `a positive integer less than ${achievement.progress[1]}`)
// Check if the achievement is already unlocked
if (achievement.progress[0] === achievement.progress[1]) return false
else achievement.status = AchievementStatus.Locked
// Update progress
achievement.progress[0] = progress
// Check if the achievement was unlocked
if (achievement.progress[0] >= achievement.progress[1]) {
achievement.status = AchievementStatus.Unlocked
achievement.progress[0] = achievement.progress[1]
return true
}
return false
}
/**
* Returns the user as a JSON object.
* @returns {UserEntry}
*/
toJSON () {
return {
id: this.id,
username: this.username,
xp: this.xp,
level: this.level,
maxXpToLevelUp: this.maxXpToLevelUp,
rank: this.rank,
achievements: this.achievements
}
}
}
module.exports = { UserCard }