UNPKG

reldens

Version:
286 lines (270 loc) 10.9 kB
/** * * Reldens - RewardsSubscriber * * Processes reward distribution to players, handling items, modifiers, and experience rewards with configurable split rules. * */ const { TargetDeterminer } = require('../target-determiner'); const { Reward } = require('../reward'); const { RewardsConst } = require('../../constants'); const { Modifier, ModifierConst } = require('@reldens/modifiers'); const { Logger, sc } = require('@reldens/utils'); /** * @typedef {import('@reldens/utils').EventsManagerSingleton} EventsManager * @typedef {import('@reldens/storage').BaseDataServer} BaseDataServer * @typedef {import('../../../teams/server/plugin').TeamsPlugin} TeamsPlugin */ class RewardsSubscriber { /** * @param {Object} props */ constructor(props) { // @TODO - BETA - Handle undefined cases. /** @type {TeamsPlugin|undefined} */ this.teamsPlugin = props?.featuresManager?.featuresList?.teams.package; /** @type {object} */ this.rewardsConfig = props.featuresManager?.config?.getWithoutLogs('client/rewards/general', {}) ?? {}; /** @type {number} */ this.splitExperience = sc.get(this.rewardsConfig, 'splitExperience', 0); /** @type {number} */ this.splitModifier = sc.get(this.rewardsConfig, 'splitModifier', 0); /** @type {TargetDeterminer} */ this.targetDeterminer = new TargetDeterminer(this.teamsPlugin); } /** * @param {Object} playerSchema * @param {Object} targetObject * @param {EventsManager} eventsManager */ async giveRewards(playerSchema, targetObject, eventsManager) { if(!targetObject){ return; } let eventDrop = {playerSchema, targetObject, continueEvent: true}; await eventsManager.emit('reldens.beforeGiveRewards', eventDrop); if(!eventDrop.continueEvent){ return; } let rewards = sc.get(targetObject, 'rewards', false); let winningRewards = this.getWinningRewards(rewards); // Logger.debug('Player won rewards.', rewards, {playerId: playerSchema.player_id, objectId: targetObject.id}); if(0 === winningRewards.length){ return; } let itemRewards = []; for(let winningReward of winningRewards){ let giveRewardErrors = await this.giveReward(winningReward, playerSchema, itemRewards); if(0 < giveRewardErrors.length){ Logger.error( 'There was an error on the reward ID "'+winningReward.id+'".', winningReward, playerSchema.username+' (Player ID: '+playerSchema.player_id+')', giveRewardErrors ); } } // @TODO - BETA - Verify if we can update the unique reward if there was any errors. await this.updateUniqueRewards(winningRewards, targetObject.dataServer); if(0 === itemRewards.length){ return; } await eventsManager.emit('reldens.afterGiveRewards', {itemRewards, playerSchema, targetObject, winningRewards}); } /** * @param {Reward} reward * @param {Object} playerSchema * @param {Array<Object>} itemRewards * @returns {Promise<Array<Object>>} */ async giveReward(reward, playerSchema, itemRewards) { // @TODO - BETA - Make optional give full records or partial ones. // give by record will give a single reward model all the options to the same player (exp, item, modifier): // let giveByRecord = this.config.get('rewards/general/giveByRecord'); let rewardTargets = this.targetDeterminer.forReward(playerSchema); let errors = []; if(reward.isItemType()){ let result = await this.applyItemReward(reward, rewardTargets, itemRewards); if(!result.isSuccess){ errors.push(result); } } if(reward.isModifierType()){ let result = await this.applyModifierReward(reward, rewardTargets); if(!result.isSuccess){ errors.push(result); } } if(reward.hasExperienceSet()){ let result = await this.applyExperienceReward(reward, rewardTargets); if(!result.isSuccess){ errors.push(result); } } return errors; } /** * @param {Reward} reward * @param {Object<number, Object>} rewardTargets * @param {Array<Object>} itemRewards * @returns {Promise<Object>} */ async applyItemReward(reward, rewardTargets, itemRewards) { if(!reward.item){ return this.createRewardResult(false, 'The item with id "'+reward.itemId+'" was not found.'); } let playersKeys = Object.keys(rewardTargets); let randomIndex = sc.randomInteger(0, (playersKeys.length -1)); let randomTarget = rewardTargets[playersKeys[randomIndex]]; if(reward.isDroppable()){ itemRewards.push(reward); return this.createRewardResult(true); } let addItemsResult = await this.addItemToPlayerInventory(randomTarget, reward.item, reward.dropQuantity); if(!addItemsResult){ return this.createRewardResult( false, 'Could not add item to Players Inventory - Reward ' + reward.id + ' - Item Id = ' + reward.itemId ); } return this.createRewardResult(true); } /** * @param {Reward} reward * @param {Object<number, Object>} rewardTargets * @returns {Promise<Object>} */ async applyModifierReward(reward, rewardTargets) { let rewardModifierModel = reward.modifier; if(!rewardModifierModel){ return this.createRewardResult(false, 'The modifier with id ' + reward.modifierId + ' was not found.'); } let playersKeys = Object.keys(rewardTargets); let randomIndex = sc.randomInteger(0, playersKeys.length); let randomTarget = rewardTargets[randomIndex]; let configuredTargets = RewardsConst.SPLIT_MODIFIER.ALL === this.splitModifier ? rewardTargets : {[randomTarget.player_id]: randomTarget}; for(let i of Object.keys(configuredTargets)){ let modifier = new Modifier(rewardModifierModel); modifier.apply(configuredTargets[i]); if(ModifierConst.MOD_APPLIED !== modifier.state){ return this.createRewardResult( false, 'Could not add modifier to Player - Reward ' + reward.id + ' - Modifier Id = ' + reward.modifierId ); } } return this.createRewardResult(true); } /** * @param {Reward} reward * @param {Object<number, Object>} rewardTargets * @returns {Promise<Object>} */ async applyExperienceReward(reward, rewardTargets) { let playersKeys = Object.keys(rewardTargets); if(RewardsConst.SPLIT_EXPERIENCE.ALL === this.splitExperience){ for(let i of playersKeys){ let experiencePerPlayer = Number(reward.experience) / Number(playersKeys.length); let rewardTargetClassPath = rewardTargets[i].skillsServer.classPath; await rewardTargetClassPath.addExperience(experiencePerPlayer); } } if(RewardsConst.SPLIT_EXPERIENCE.PROPORTIONAL_BY_LEVEL === this.splitExperience){ let levelsTotal = this.targetsLevelsTotal(playersKeys, rewardTargets); for(let i of playersKeys){ let rewardTargetClassPath = rewardTargets[i].skillsServer.classPath; let playerLevelProportion = (rewardTargetClassPath.currentLevel * 100) / levelsTotal; let experiencePerPlayer = (playerLevelProportion * Number(reward.experience)) / 100 ; await rewardTargetClassPath.addExperience(experiencePerPlayer); } } return this.createRewardResult(true); } /** * @param {Array<string>} playersKeys * @param {Object<number, Object>} rewardTargets * @returns {number} */ targetsLevelsTotal(playersKeys, rewardTargets) { let levelsTotal = 0; for(let i of playersKeys){ levelsTotal += Number(rewardTargets[i].skillsServer.classPath.currentLevel); } return levelsTotal; } /** * @param {boolean} isSuccess * @param {string} message * @returns {Object} */ createRewardResult(isSuccess, message = '') { return { isSuccess, message }; } /** * @param {Object} playerSchema * @param {Object} item * @param {number} quantity * @returns {Promise<boolean>} */ async addItemToPlayerInventory(playerSchema, item, quantity) { let rewardItem = playerSchema.inventory.manager.createItemInstance(item.key, quantity); if(!rewardItem){ Logger.error(`Couldn't create item instance with key ${item.key}`); return false; } return await playerSchema.inventory.manager.addItem(rewardItem); } /** * @param {Array<Reward>|boolean} rewards * @param {boolean} usesRewardBag * @returns {Array<Reward>} */ getWinningRewards(rewards, usesRewardBag = false) { let rewardAwarded = []; if(!Reward.areValidRewards(rewards)){ return rewardAwarded; } if(usesRewardBag){ let rewardsBag = Reward.getRewardsBag(rewards); let reward = rewardsBag[Math.floor(Math.random() * rewardsBag.length)]; if(reward.isValidReward()){ rewardAwarded.push(reward); } return rewardAwarded; } for(let reward of rewards){ if(!reward.isWinningReward() || !reward.isValidReward()){ continue; } rewardAwarded.push(reward); } return rewardAwarded; } /** * @param {Array<Reward>} rewards * @param {BaseDataServer} dataServer */ async updateUniqueRewards(rewards, dataServer) { let rewardsRepository = dataServer.getEntity('rewards'); for(let reward of rewards){ if(reward.isUnique && !reward.was_given){ await rewardsRepository.updateById(reward.id, { was_given: 1 }); } } } } module.exports.RewardsSubscriber = RewardsSubscriber;