UNPKG

reldens

Version:
568 lines (543 loc) 21.9 kB
/** * * Reldens - TraderObjectUi * * Manages trader object UI rendering and interaction on the client side. * */ const { TradeItemsHelper } = require('../../inventory/client/trade-items-helper'); const { ObjectsConst } = require('../constants'); const { ItemsConst } = require('@reldens/items-system'); const { Logger, sc } = require('@reldens/utils'); /** * @typedef {import('../../rooms/client/room-events').RoomEvents} RoomEvents * @typedef {import('../../game/client/game-manager').GameManager} GameManager - CUSTOM DYNAMIC * @typedef {import('../../game/client/game-dom').GameDom} GameDom * @typedef {import('@reldens/items-system').ItemsManager} ItemsManager * @typedef {import('../../game/client/user-interface').UserInterface} UserInterface * * @typedef {Object} TraderObjectUiProps * @property {RoomEvents} roomEvents * @property {Object} message */ class TraderObjectUi { /** * @param {TraderObjectUiProps} props */ constructor(props) { // @TODO - BETA - Rename class to TraderObjectMessageHandler, split in several services. /** @type {RoomEvents|false} */ this.roomEvents = sc.get(props, 'roomEvents', false); /** @type {Object|false} */ this.message = sc.get(props, 'message', false); /** @type {GameManager|undefined} */ this.gameManager = this.roomEvents?.gameManager; /** @type {GameDom|undefined} */ this.gameDom = this.gameManager?.gameDom; /** @type {Object|undefined} */ this.uiScene = this.gameManager?.gameEngine?.uiScene; /** @type {ItemsManager|undefined} */ this.itemsManager = this.gameManager?.inventory?.manager; /** @type {UserInterface|undefined} */ this.objectUi = this.roomEvents?.objectsUi[this.message?.id]; this.setConfirmMessages(); } /** * @returns {boolean} */ validate() { if(!this.roomEvents){ Logger.error('Missing RoomEvents on TraderObjectUi.'); return false; } if(!this.message){ Logger.error('Missing message on TraderObjectUi.'); return false; } if(!this.gameManager){ Logger.error('Missing GameManager on TraderObjectUi.'); return false; } if(!this.uiScene){ Logger.error('Missing UiScene on TraderObjectUi.'); return false; } if(!this.itemsManager){ Logger.error('Missing ItemsManager on TraderObjectUi.'); return false; } if(!this.objectUi){ Logger.error('Missing objectUi on TraderObjectUi.'); return false; } return true; } /** * @returns {boolean} */ setConfirmMessages() { if(!this.gameManager){ return false; } this.confirmMessages = { buy: this.gameManager.services.translator.t('objects.trader.buyConfirmed'), sell: this.gameManager.services.translator.t('objects.trader.sellConfirmed') }; return true; } /** * @returns {boolean} */ updateContents() { let container = this.gameManager.gameDom.getElement('#box-'+this.objectUi.id+' .box-content'); if(!container){ Logger.error('Missing container: "#box-'+this.objectUi.id+' .box-content".'); return false; } let tradeAction = this.message.result.action; if(ObjectsConst.TRADE_ACTIONS_FUNCTION_NAME.CONFIRM === this.message.result.subAction){ container.innerHTML = this.confirmMessages[tradeAction]; return true; } let items = sc.get(this.message.result, 'items', false); let inventoryKey = this.mapInventoryKeyFromAction(tradeAction); let exchangeData = sc.get(this.message.result, 'exchangeData', false); let exchangeItems = sc.get(exchangeData, inventoryKey, false); let exchangeRequirementsA = this.message.result.exchangeRequirementsA || []; let exchangeRewardsB = this.message.result.exchangeRewardsB || []; this.updateItemsList(items, tradeAction, exchangeRequirementsA, exchangeRewardsB, container, exchangeItems); this.updateExchangeData(exchangeItems, tradeAction, exchangeRequirementsA, exchangeRewardsB, items); return true; } /** * @param {Object} items * @param {string} tradeAction * @param {Array<Object>} exchangeRequirementsA * @param {Array<Object>} exchangeRewardsB * @param {HTMLElement} container * @param {Object|false} exchangeData */ updateItemsList(items, tradeAction, exchangeRequirementsA, exchangeRewardsB, container, exchangeData) { if(!items){ //Logger.debug('Missing items for updateItemsList method.'); return false; } let tradeItems = ''; let tempItemsList = {}; for(let i of Object.keys(items)){ tempItemsList[i] = TradeItemsHelper.createItemInstance(items, i, this.itemsManager); tempItemsList[i].tradeAction = tradeAction; tempItemsList[i].exchangeRequirements = this.fetchItemRequirements(items[i].key, exchangeRequirementsA); tempItemsList[i].exchangeRewards = this.fetchItemRewards(items[i].key, exchangeRewardsB); tradeItems += this.createTradeItemBox(tempItemsList[i], sc.get(exchangeData, tempItemsList[i].uid, false)); } container.innerHTML = this.createTradeContainer(tradeAction, tradeItems); this.activateItemsBoxActions(tempItemsList); this.activateConfirmButtonAction(tradeAction); } /** * @param {string} tradeAction */ activateConfirmButtonAction(tradeAction) { let confirmButton = this.gameManager.gameDom.getElement('.confirm-'+tradeAction); let dataSend = { act: ObjectsConst.OBJECT_INTERACTION, id: this.message.id, value: tradeAction, sub: ObjectsConst.TRADE_ACTIONS.CONFIRM }; confirmButton?.addEventListener('click', () => { this.gameManager.activeRoomEvents.send(dataSend); }); } /** * @param {Object|false} exchangeData * @param {string} tradeAction * @param {Array<Object>} exchangeRequirementsA * @param {Array<Object>} exchangeRewardsB * @param {Object} items * @returns {boolean} */ updateExchangeData(exchangeData, tradeAction, exchangeRequirementsA, exchangeRewardsB, items) { if(false === exchangeData){ return false; } let content = this.createConfirmItemsBox(exchangeData, items, tradeAction); let itemsContainer = null; if('buy' === tradeAction){ itemsContainer = this.gameDom.getElement('.trade-container-buy .trade-col.trade-col-2'); } if('sell' === tradeAction){ itemsContainer = this.gameDom.getElement('.trade-container-sell .trade-col.trade-col-1'); } if(null === itemsContainer){ Logger.error('Missing "'+tradeAction+'" items container.', {message: this.message}); return false; } itemsContainer.innerHTML = content; this.assignRemoveActions(exchangeData, items, tradeAction); return true; } /** * @param {Object} exchangeItems * @param {Object} items * @param {string} tradeAction * @returns {string} */ createConfirmItemsBox(exchangeItems, items, tradeAction) { // @TODO - BETA - Since we are using one template "inventoryTradeItem", use only one "createConfirmItemsBox". let exchangeItemsUids = Object.keys(exchangeItems); if(0 === exchangeItemsUids.length){ // @NOTE: this will be the case if you don't have a required item. if(!this.message.lastErrorMessage){ Logger.info('Undefined exchange items on confirmation trader-object-ui.', {message: this.message}); } return ''; } let content = ''; // @TODO - BETA - Move the template load from cache as part of the engine driver. let messageTemplate = this.uiScene.cache.html.get('inventoryTradeItem'); if(!messageTemplate){ Logger.error('Missing template "inventoryTradeItem".'); return ''; } for(let itemUid of exchangeItemsUids){ let qty = exchangeItems[itemUid]; let item = items[itemUid]; let isBuy = ItemsConst.TRADE_ACTIONS.BUY === tradeAction; let isSell = ItemsConst.TRADE_ACTIONS.SELL === tradeAction; content += this.gameManager.gameEngine.parseTemplate(messageTemplate, { key: item.key, label: item.label, description: item.description, id: itemUid, qty: item.qty, hiddenClass: '', tradeRequirements: isBuy ? this.createTradeRequirementsContent(item) : '', tradeRewards: isSell ? this.createTradeRewardsContent(item) : '', tradeAction: this.createTradeActionRemove(item), tradeActionKey: tradeAction, tradeQuantityContent: qty }); } return content; } /** * @param {Object} exchangeItems * @param {Object} items * @param {string} tradeAction * @returns {boolean} */ assignRemoveActions(exchangeItems, items, tradeAction) { let exchangeItemsUids = Object.keys(exchangeItems); if(0 === exchangeItemsUids.length){ // @NOTE: this will be the case if you don't have a required item. if(!this.message.lastErrorMessage){ Logger.info('Undefined exchange items on remove trader-object-ui.', {message: this.message}); } return false; } for(let itemUid of exchangeItemsUids){ let itemContainerSelector = '.trade-item-to-be-'+tradeAction+'.trade-item-'+itemUid; let itemContainer = this.gameDom.getElement(itemContainerSelector); if(!itemContainer){ Logger.error('Assign trade item "'+itemUid+'" container not found.', {message: this.message}); continue; } let itemActionButton = this.gameDom.getElement( '.trade-item-'+tradeAction+'.trade-item-'+itemUid+' .trade-action-remove' ); if(!itemActionButton){ Logger.error('Assign trade item "'+itemUid+'" remove button not found.', {message: this.message}); continue; } let item = items[itemUid]; itemActionButton.addEventListener('click', () => { itemContainer.classList.remove('hidden'); let dataSend = { act: ObjectsConst.OBJECT_INTERACTION, id: this.message.id, value: tradeAction, itemId: itemUid, itemKey: item.key, }; dataSend[ObjectsConst.TRADE_ACTIONS.SUB_ACTION] = ObjectsConst.TRADE_ACTIONS.REMOVE; this.gameManager.activeRoomEvents.send(dataSend); }); } return true; } /** * @param {string} tradeActionKey * @param {string} tradeItems * @returns {string} */ createTradeContainer(tradeActionKey, tradeItems) { // @TODO - BETA - Move the template load from cache as part of the engine driver. let messageTemplate = this.uiScene.cache.html.get('inventoryTradeContainer'); if(!messageTemplate){ Logger.error('Missing template "inventoryTradeContainer".'); return ''; } // @TODO - BETA - Populate requirements totals. let confirmRequirements = ''; let lastErrorData = this.message.result.lastErrorData; if(lastErrorData?.itemUid){ lastErrorData.item = this.fetchItemLabelByUid(lastErrorData.itemUid); } if(lastErrorData?.requiredItemKey){ lastErrorData.requiredItem = this.fetchItemLabelByUid(lastErrorData.requiredItemKey); } let lastErrorMessage = this.gameManager.services.translator.t( this.message.result.lastErrorMessage, lastErrorData ); let tradeItemsBuy = ItemsConst.TRADE_ACTIONS.BUY === tradeActionKey ? tradeItems : ''; let tradeItemsSell = ItemsConst.TRADE_ACTIONS.SELL === tradeActionKey ? tradeItems : ''; return this.gameManager.gameEngine.parseTemplate( messageTemplate, { tradeActionKey, confirmRequirements, lastErrorMessage, tradeActionLabel: ObjectsConst.TRADE_ACTIONS_FUNCTION_NAME.CONFIRM, tradeItemsBuy, tradeItemsSell } ); } /** * @param {string} itemUid * @returns {string} */ fetchItemLabelByUid(itemUid) { return this.gameManager?.inventory?.manager?.items[itemUid]?.label || this.message?.result?.items[itemUid]?.label || ''; } /** * @param {string} itemKey * @param {Array<Object>} exchangeRequirements * @returns {Object|false} */ fetchItemRequirements(itemKey, exchangeRequirements) { if(0 === exchangeRequirements.length){ return false; } let itemExchangeRequirements = {}; for(let exchangeRequirement of exchangeRequirements){ if(itemKey !== exchangeRequirement.itemKey){ continue; } itemExchangeRequirements[exchangeRequirement.itemKey] = exchangeRequirement; } return itemExchangeRequirements; } /** * @param {string} itemKey * @param {Array<Object>} exchangeRewards * @returns {Object|false} */ fetchItemRewards(itemKey, exchangeRewards) { if(0 === exchangeRewards.length){ return false; } let itemExchangeRewards = {}; for(let exchangeReward of exchangeRewards){ if(itemKey !== exchangeReward.itemKey){ continue; } itemExchangeRewards[exchangeReward.itemKey] = exchangeReward; } return itemExchangeRewards; } /** * @param {Object} item * @param {number|undefined} exchangeDataItem * @returns {string} */ createTradeItemBox(item, exchangeDataItem) { // @TODO - BETA - Move the template load from cache as part of the engine driver. let messageTemplate = this.uiScene.cache.html.get('inventoryTradeItem'); if(!messageTemplate){ Logger.error('Missing template "inventoryTradeItem".'); return ''; } let qtyTemplate = this.uiScene.cache.html.get('inventoryTradeItemQuantity'); if(!qtyTemplate){ Logger.error('Missing template "inventoryTradeItemQuantity".'); return ''; } let isBuy = ItemsConst.TRADE_ACTIONS.BUY === item.tradeAction; let isSell = ItemsConst.TRADE_ACTIONS.SELL === item.tradeAction; let qty = exchangeDataItem || 0; return this.gameManager.gameEngine.parseTemplate(messageTemplate, { key: item.key, label: item.label, description: item.description, id: item.getInventoryId(), qty: item.qty, hiddenClass: 0 < qty && item.qty === qty ? ' hidden' : '', tradeRequirements: isBuy ? this.createTradeRequirementsContent(item) : '', tradeRewards: isSell ? this.createTradeRewardsContent(item) : '', tradeAction: this.createTradeActionContent(item), tradeActionKey: 'to-be-'+item.tradeAction, tradeQuantityContent: this.gameManager.gameEngine.parseTemplate(qtyTemplate, { quantityDisplay: item.quantityDisplay || 1, quantityMaxDisplay: 0 < item.quantityMaxDisplay ? 'max="' + item.quantityMaxDisplay + '"' : '' }) }); } /** * @param {Object} item * @returns {string} */ createTradeRequirementsContent(item) { if(!item.exchangeRequirements){ return ''; } let requirementsKeys = Object.keys(item.exchangeRequirements); if(0 === requirementsKeys.length){ return ''; } // @TODO - BETA - Move the template load from cache as part of the engine driver. let messageTemplate = this.uiScene.cache.html.get('inventoryTradeRequirements'); if(!messageTemplate){ Logger.error('Missing template "inventoryTradeRequirements".'); return ''; } let content = ''; for(let i of requirementsKeys){ let requirement = item.exchangeRequirements[i]; content += this.gameManager.gameEngine.parseTemplate(messageTemplate, { itemKey: item.key, requiredItemKey: requirement.requiredItemKey, requiredQuantity: requirement.requiredQuantity }); } return content; } /** * @param {Object} item * @returns {string} */ createTradeRewardsContent(item) { if(!item.exchangeRewards){ return ''; } let rewardsKeys = Object.keys(item.exchangeRewards); if(0 === rewardsKeys.length){ return ''; } // @TODO - BETA - Move the template load from cache as part of the engine driver. let messageTemplate = this.uiScene.cache.html.get('inventoryTradeRewards'); if(!messageTemplate){ Logger.error('Missing template "inventoryTradeRewards".'); return ''; } let content = ''; for(let i of rewardsKeys){ let reward = item.exchangeRewards[i]; content += this.gameManager.gameEngine.parseTemplate(messageTemplate, { itemKey: item.key, rewardItemKey: reward.rewardItemKey, rewardQuantity: reward.rewardQuantity }); } return content; } /** * @param {Object} item * @param {string} [tradeAction] * @returns {string} */ createTradeActionContent(item, tradeAction) { // @TODO - BETA - Move the template load from cache as part of the engine driver. let messageTemplate = this.uiScene.cache.html.get('inventoryTradeAction'); if(!messageTemplate){ Logger.error('Missing template "inventoryTradeAction".'); return ''; } return this.gameManager.gameEngine.parseTemplate(messageTemplate, { key: item.key, id: item.getInventoryId(), tradeAction: tradeAction || sc.get(item, 'tradeAction', '') }); } /** * @param {Object} item * @returns {string} */ createTradeActionRemove(item) { // @TODO - BETA - Move the template load from cache as part of the engine driver. let messageTemplate = this.uiScene.cache.html.get('inventoryTradeActionRemove'); if(!messageTemplate){ Logger.error('Missing template "inventoryTradeActionRemove".'); return ''; } return this.gameManager.gameEngine.parseTemplate(messageTemplate, { key: item.key, id: item.uid, tradeAction: 'remove' }); } /** * @param {Object} items */ activateItemsBoxActions(items) { for(let i of Object.keys(items)){ let item = items[i]; let itemContainerSelector = '.trade-item-to-be-'+item.tradeAction+'.trade-item-'+item.uid +' .trade-action-'+item.tradeAction; let itemButtonSelector = itemContainerSelector+' button'; let itemActionButton = this.gameDom.getElement(itemButtonSelector); if(!itemActionButton){ Logger.error('Activate trade item "'+item.uid+'" action button not found.'); continue; } itemActionButton.addEventListener('click', () => { let qtySelector = this.gameDom.getElement('.trade-item-'+item.getInventoryId()+' .item-qty input'); let qtySelected = qtySelector?.value || 1; let dataSend = { act: ObjectsConst.OBJECT_INTERACTION, id: this.message.id, value: item.tradeAction, itemId: item.getInventoryId(), itemKey: item.key, qty: Number(qtySelected) }; dataSend[ObjectsConst.TRADE_ACTIONS.SUB_ACTION] = ObjectsConst.TRADE_ACTIONS.ADD; this.gameManager.activeRoomEvents.send(dataSend); }); } } /** * @param {string} action * @returns {string|false} */ mapInventoryKeyFromAction(action) { return sc.get({buy: 'A', sell: 'B'}, action, false); } } module.exports.TraderObjectUi = TraderObjectUi;