reldens
Version:
Reldens - MMORPG Platform
613 lines (582 loc) • 22.6 kB
JavaScript
/**
*
* Reldens - InventoryPlugin
*
* Client-side plugin for the inventory system.
* Manages UI, event listeners, trade actions, and player inventory instances.
*
*/
const { InventoryUi } = require('./inventory-ui');
const { InventoryReceiver } = require('./inventory-receiver');
const { TradeTargetAction } = require('./exchange/trade-target-action');
const { TradeMessageListener } = require('./trade-message-listener');
const { UserInterface } = require('../../game/client/user-interface');
const { PluginInterface } = require('../../features/plugin-interface');
const { TemplatesHandler } = require('./templates-handler');
const { TranslationsMapper } = require('../../snippets/client/translations-mapper');
const Translations = require('./snippets/en_US');
const { InventoryConst } = require('../constants');
const { ItemsEvents, ItemsConst } = require('@reldens/items-system');
const { GameConst } = require('../../game/constants');
const { Logger, sc } = require('@reldens/utils');
/**
* @typedef {import('@reldens/utils').EventsManager} EventsManager
* @typedef {import('../../game/client/game-manager').GameManager} GameManager
*/
class InventoryPlugin extends PluginInterface
{
/**
* @param {Object} props
*/
async setup(props)
{
// @TODO - BETA - Refactor plugin, extract all the methods into new classes.
/** @type {GameManager|boolean} */
this.gameManager = sc.get(props, 'gameManager', false);
if(!this.gameManager){
Logger.error('Game Manager undefined in InventoryPlugin.');
}
/** @type {EventsManager|boolean} */
this.events = sc.get(props, 'events', false);
if(!this.events){
Logger.error('EventsManager undefined in InventoryPlugin.');
}
this.tradeTargetAction = new TradeTargetAction();
this.setTradeUi();
this.listenEvents();
this.setListener();
this.setTranslations();
}
/**
* @returns {boolean|void}
*/
setTranslations()
{
if(!this.gameManager){
return false;
}
TranslationsMapper.forConfig(this.gameManager.config.client, Translations, InventoryConst.MESSAGE.DATA_VALUES);
return true;
}
/**
* @returns {boolean|void}
*/
setTradeUi()
{
if(!this.gameManager){
return false;
}
// @TODO - BETA - Make the dialogBox template load on it's own so we can reuse the same object from cache.
// @NOTE: the tradeUi works as preload for the trade template which at the end is an dialog-box.
this.tradeUi = new UserInterface(this.gameManager, {id: 'trade', type: 'trade'});
return true;
}
/**
* @returns {boolean|void}
*/
setListener()
{
if(!this.gameManager){
return false;
}
this.gameManager.config.client.message.listeners['trade'] = new TradeMessageListener();
return true;
}
/**
* @returns {boolean|void}
*/
listenEvents()
{
if(!this.events){
return false;
}
this.events.on('reldens.playersOnAdd', (player, key, previousScene, roomEvents) => {
this.onPlayerAdd(key, roomEvents, player);
});
this.events.on('reldens.preloadUiScene', (preloadScene) => {
TemplatesHandler.preloadTemplates(preloadScene);
});
this.events.on('reldens.createUiScene', (preloadScene) => {
return this.onPreloadUiScene(preloadScene);
});
this.events.on('reldens.gameEngineShowTarget', (gameEngine, target, previousTarget, targetName) => {
this.tradeTargetAction.showTargetExchangeAction(this.gameManager, target, previousTarget, targetName);
});
return true;
}
/**
* @param {Object} preloadScene
*/
onPreloadUiScene(preloadScene)
{
this.uiManager = new InventoryUi(preloadScene);
this.uiManager.createUi();
let manager = preloadScene.gameManager.inventory.manager;
let equipmentPanel = this.activateGroupAndEquipmentUi(preloadScene, manager);
let inventoryPanel = this.activateInventoryUi(preloadScene, manager, equipmentPanel);
this.listenInventoryEvents(preloadScene, inventoryPanel, equipmentPanel);
}
/**
* @param {Object} preloadScene
* @param {Object} manager
* @returns {Object|boolean}
*/
activateGroupAndEquipmentUi(preloadScene, manager)
{
let equipmentElement = preloadScene.getUiElement('equipment');
if(!equipmentElement){
Logger.warning('EquipmentElement not found.', equipmentElement);
return false;
}
let equipmentPanel = equipmentElement.getChildByProperty(
'id',
InventoryConst.EQUIPMENT_ITEMS
);
if(!equipmentPanel){
Logger.warning('Equipment UI not found.', equipmentPanel);
return false;
}
let inventoryGroups = sc.get(manager, 'groups', {});
if(Object.keys(inventoryGroups).length){
let equipmentItemsElement = preloadScene.gameManager.gameDom.getElement(
'#' + InventoryConst.EQUIPMENT_ITEMS
);
if(!equipmentItemsElement){
Logger.warning('Element "equipmentItemsElement" not found.');
return false;
}
equipmentItemsElement.innerHTML = '';
let orderedGroups = this.sortGroups(inventoryGroups);
for (let i of orderedGroups){
let output = this.createGroupBox(inventoryGroups[i], preloadScene.gameManager, preloadScene);
preloadScene.gameManager.gameDom.appendToElement('#' + InventoryConst.EQUIPMENT_ITEMS, output);
}
}
return equipmentPanel;
}
/**
* @param {Object} preloadScene
* @param {Object} manager
* @param {Object} equipmentPanel
* @returns {Object|boolean}
*/
activateInventoryUi(preloadScene, manager, equipmentPanel)
{
let inventoryElement = preloadScene.getUiElement('inventory');
if(!inventoryElement){
Logger.warning('InventoryElement not found.', inventoryElement);
return false;
}
let inventoryPanel = inventoryElement.getChildByProperty(
'id',
InventoryConst.INVENTORY_ITEMS
);
if(!inventoryPanel){
Logger.warning('Inventory UI not found.', inventoryPanel);
return false;
}
let itemsElements = sc.get(manager, 'items', {});
let itemsKeys = Object.keys(itemsElements);
if(0 < itemsKeys.length){
for (let i of itemsKeys){
let item = itemsElements[i];
this.displayItem(item, preloadScene, equipmentPanel, inventoryPanel, i);
}
}
return inventoryPanel;
}
/**
* @param {string} key
* @param {Object} roomEvents
* @param {Object} player
* @returns {boolean|void}
*/
onPlayerAdd(key, roomEvents, player)
{
if(key !== roomEvents.room.sessionId){
return false;
}
if(!roomEvents.gameManager.inventory){
this.createInventoryInstance(player, roomEvents);
}
roomEvents.room.onMessage('*', (message) => {
roomEvents.gameManager.inventory.processMessage(message);
});
return true;
}
/**
* @param {Object} player
* @param {Object} roomEvents
*/
createInventoryInstance(player, roomEvents)
{
let receiverProps = {
owner: player,
ownerIdProperty: 'sessionId',
gameManager: roomEvents.gameManager
};
let inventoryClasses = roomEvents.gameManager.config.getWithoutLogs('client/customClasses/inventory/items', {});
if(inventoryClasses && 0 < Object.keys(inventoryClasses).length){
receiverProps.itemClasses = inventoryClasses;
}
let groupClasses = roomEvents.gameManager.config.getWithoutLogs('client/customClasses/inventory/groups', {});
if(groupClasses && Object.keys(groupClasses).length){
receiverProps.groupClasses = groupClasses;
}
roomEvents.gameManager.inventory = new InventoryReceiver(receiverProps);
}
/**
* @param {Object} uiScene
* @param {Object} inventoryPanel
* @param {Object} equipmentPanel
*/
listenInventoryEvents(uiScene, inventoryPanel, equipmentPanel)
{
let gameManager = uiScene.gameManager;
let masterKey = gameManager.inventory.manager.getOwnerEventKey();
gameManager.inventory.manager.listenEvent(
ItemsEvents.ADD_ITEM,
(inventory, item) => {
let output = this.createItemBox(item, 'inventoryItem', gameManager, uiScene);
gameManager.gameDom.appendToElement('#'+InventoryConst.INVENTORY_ITEMS, output);
this.setupButtonsActions(inventoryPanel, item.getInventoryId(), item, uiScene);
},
gameManager.inventory.manager.getOwnerUniqueEventKey('addItemPack'),
masterKey
);
gameManager.inventory.manager.listenEvent(
ItemsEvents.SET_ITEMS,
(props) => {
inventoryPanel.innerHTML = '';
for(let i of Object.keys(props.items)){
let item = props.items[i];
this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, i);
}
},
gameManager.inventory.manager.getOwnerUniqueEventKey('setItemsPack'),
masterKey
);
gameManager.inventory.manager.listenEvent(
ItemsEvents.MODIFY_ITEM_QTY,
(item) => {
let qtyBox = uiScene.getUiElement('inventory').getChildByID('item-qty-'+item.getInventoryId());
qtyBox.innerHTML = item.qty;
},
gameManager.inventory.manager.getOwnerUniqueEventKey('modifyItemQtyPack'),
masterKey
);
gameManager.inventory.manager.listenEvent(
ItemsEvents.REMOVE_ITEM,
(inventory, itemKey) => {
uiScene.getUiElement('inventory').getChildByID('item-'+itemKey).remove();
},
gameManager.inventory.manager.getOwnerUniqueEventKey('removeItemPack'),
masterKey
);
gameManager.inventory.manager.listenEvent(
ItemsEvents.SET_GROUPS,
(props) => {
// @TODO - BETA - If groups are re-set or updated we will need to update the items as well.
let reEquipItems = false;
let equipmentItemsGroups = gameManager.gameDom.getElement('#'+InventoryConst.EQUIPMENT_ITEMS);
if(equipmentItemsGroups.innerHTML !== ''){
reEquipItems = true;
}
equipmentItemsGroups.innerHTML = '';
let orderedGroups = this.sortGroups(props.groups);
for(let i of orderedGroups){
let output = this.createGroupBox(props.groups[i], gameManager, uiScene);
gameManager.gameDom.appendToElement('#'+InventoryConst.EQUIPMENT_ITEMS, output);
}
if(reEquipItems){
this.resetEquippedItemsDisplay(gameManager, uiScene, equipmentPanel, inventoryPanel);
}
},
gameManager.inventory.manager.getOwnerUniqueEventKey('setGroupsPack'),
masterKey
);
gameManager.inventory.manager.listenEvent(
ItemsEvents.EQUIP_ITEM,
(item) => {
this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, item.getInventoryId());
},
gameManager.inventory.manager.getOwnerUniqueEventKey('equipItemPack'),
masterKey
);
gameManager.inventory.manager.listenEvent(
ItemsEvents.UNEQUIP_ITEM,
(item) => {
this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, item.getInventoryId());
},
gameManager.inventory.manager.getOwnerUniqueEventKey('unequipItemPack'),
masterKey
);
}
/**
* @param {GameManager} gameManager
* @param {Object} uiScene
* @param {Object} equipmentPanel
* @param {Object} inventoryPanel
* @returns {boolean|void}
*/
resetEquippedItemsDisplay(gameManager, uiScene, equipmentPanel, inventoryPanel)
{
let items = Object.keys(gameManager.inventory.manager.items);
if(0 === items.length){
return false;
}
for(let i of items){
let item = gameManager.inventory.manager.items[i];
if(!this.isEquipped(item)){
continue;
}
this.displayItem(item, uiScene, equipmentPanel, inventoryPanel, item.getInventoryId());
}
return true;
}
/**
* @param {Object} item
* @param {Object} uiScene
* @param {Object} equipmentPanel
* @param {Object} inventoryPanel
* @param {string} itemIdx
*/
displayItem(item, uiScene, equipmentPanel, inventoryPanel, itemIdx)
{
let output = this.createItemBox(item, 'inventoryItem', uiScene.gameManager, uiScene);
let existentElement = uiScene.gameManager.gameDom.getElement('#item-'+item.getInventoryId());
if(existentElement){
existentElement.remove();
}
if(!this.isEquipped(item)){
uiScene.gameManager.gameDom.appendToElement('#' + InventoryConst.INVENTORY_ITEMS, output);
this.setupButtonsActions(inventoryPanel, itemIdx, item, uiScene);
return;
}
this.displayItemInGroups(item, uiScene, output);
if(!equipmentPanel){
return;
}
this.setupButtonsActions(equipmentPanel, itemIdx, item, uiScene);
}
/**
* @param {Object} item
* @param {Object} uiScene
* @param {string} output
*/
displayItemInGroups(item, uiScene, output)
{
let group = this.getGroupById(item.group_id, uiScene.gameManager.inventory.manager.groups);
if(group && uiScene.gameManager.gameDom.getElement('#group-item-' + group.key + ' .equipped-item')){
uiScene.gameManager.gameDom.updateContent('#group-item-' + group.key + ' .equipped-item', output);
return;
}
// @TODO - BETA - Make this append optional for now we will leave it to make the equipment action
// visible.
// Logger.error('Group element not found. Group ID: '+item.group_id);
uiScene.gameManager.gameDom.appendToElement('#' + InventoryConst.EQUIPMENT_ITEMS, output);
}
/**
* @param {Object} item
* @param {GameManager} gameManager
*/
updateEquipmentStatus(item, gameManager)
{
let currentItemElement = gameManager.gameDom.getElement('#item-equip-'+item.idx);
let equipState = item.equipped ? 'equipped' : 'unequipped';
// @TODO - BETA - Replace fixed image type.
currentItemElement.src = '/assets/features/inventory/assets/'+ equipState+GameConst.FILES.EXTENSIONS.PNG;
}
/**
* @param {Object} item
* @param {string} templateKey
* @param {GameManager} gameManager
* @param {Object} uiScene
* @returns {string}
*/
createItemBox(item, templateKey, gameManager, uiScene)
{
let messageTemplate = uiScene.cache.html.get(templateKey);
return gameManager.gameEngine.parseTemplate(messageTemplate, {
key: item.key,
label: item.label,
description: item.description,
id: item.getInventoryId(),
qty: item.qty,
usable: this.isUsable(item) ? this.getUsableContent(item, gameManager, uiScene) : '',
equipment: this.isEquipment(item) ? this.getEquipContent(item, gameManager, uiScene) : ''
});
}
/**
* @param {Object} item
* @returns {boolean}
*/
isEquipment(item)
{
return item.isType(ItemsConst.TYPES.EQUIPMENT) || item.isType(ItemsConst.TYPES.SINGLE_EQUIPMENT);
}
/**
* @param {Object} item
* @returns {boolean}
*/
isEquipped(item)
{
return this.isEquipment(item) && true === item.equipped;
}
/**
* @param {Object} item
* @returns {boolean}
*/
isUsable(item)
{
return item.isType(ItemsConst.TYPES.USABLE) || item.isType(ItemsConst.TYPES.SINGLE_USABLE);
}
/**
* @param {Object<string, Object>} groups
* @returns {Array<string>}
*/
sortGroups(groups)
{
return Object.keys(groups).sort((a,b) => {
return (groups[a].sort > groups[b].sort) ? 1 : -1;
});
}
/**
* @param {Object} group
* @param {GameManager} gameManager
* @param {Object} uiScene
* @returns {string}
*/
createGroupBox(group, gameManager, uiScene)
{
let messageTemplate = uiScene.cache.html.get('inventoryGroup');
return gameManager.gameEngine.parseTemplate(messageTemplate, {
key: group.key,
label: group.label,
description: group.description,
fileName: group.files_name
});
}
/**
* @param {Object} inventoryPanel
* @param {string} idx
* @param {Object} item
* @param {Object} preloadScene
* @returns {boolean|void}
*/
setupButtonsActions(inventoryPanel, idx, item, preloadScene)
{
// @TODO - BETA - Improve and move all the styles into an external class, and make it configurable.
let domMan = preloadScene.gameManager.gameDom;
// show item data:
let itemImage = inventoryPanel.querySelector('#item-' + idx + ' .image-container img');
if(!itemImage){
Logger.error(['Missing image element.', '#item-' + idx]);
return false;
}
itemImage.addEventListener('click', () => {
let details = inventoryPanel.querySelector('#item-' + idx + ' .item-data-container');
let show = false;
if(details.style.display !== 'block'){
show = true;
}
inventoryPanel.querySelectorAll('.item-box .image-container img').forEach(function(element){
element.style.border = 'none';
});
inventoryPanel.querySelectorAll('.item-data-container').forEach(function(element){
element.style.display = 'none';
});
if(show){
itemImage.style.border = '1px solid #fff';
details.style.display = 'block';
}
});
let buttonElement = inventoryPanel.querySelector('#item-trash-' + idx + ' img');
if(!buttonElement){
Logger.error(['Missing button.', buttonElement]);
return false;
}
buttonElement.addEventListener('click', () => {
inventoryPanel.querySelector('#trash-confirm-' + idx).style.display = 'block';
});
inventoryPanel.querySelector('#trash-cancel-' + idx).addEventListener('click', () => {
inventoryPanel.querySelector('#trash-confirm-' + idx).style.display = 'none';
});
inventoryPanel.querySelector('#trash-confirmed-' + idx).addEventListener('click', () => {
let optionSend = {
idx: idx,
act: InventoryConst.ACTIONS.REMOVE
};
preloadScene.gameManager.activeRoomEvents.send(optionSend);
});
if(this.isUsable(item)){
let useBtn = domMan.getElement('#item-use-'+idx);
useBtn.addEventListener(
'click',
this.clickedBox.bind(this, idx, InventoryConst.ACTIONS.USE, preloadScene)
);
}
if(this.isEquipment(item)){
let equipBtn = domMan.getElement('#item-equip-'+idx);
equipBtn.addEventListener(
'click',
this.clickedBox.bind(this, idx, InventoryConst.ACTIONS.EQUIP, preloadScene)
);
}
return true;
}
/**
* @param {string} itemId
* @param {string} action
* @param {Object} preloadScene
*/
clickedBox(itemId, action, preloadScene)
{
preloadScene.gameManager.activeRoomEvents.send({act: action, idx: itemId});
}
/**
* @param {Object} item
* @param {GameManager} gameManager
* @param {Object} uiScene
* @returns {string}
*/
getUsableContent(item, gameManager, uiScene)
{
let messageTemplate = uiScene.cache.html.get('inventoryItemUse');
return gameManager.gameEngine.parseTemplate(messageTemplate, {
id: item.getInventoryId()
});
}
/**
* @param {Object} item
* @param {GameManager} gameManager
* @param {Object} uiScene
* @returns {string}
*/
getEquipContent(item, gameManager, uiScene)
{
let messageTemplate = uiScene.cache.html.get('inventoryItemEquip');
return gameManager.gameEngine.parseTemplate(messageTemplate, {
id: item.getInventoryId(),
equipStatus: item.equipped ? 'equipped' : 'unequipped'
});
}
/**
* @param {number} groupId
* @param {Object<string, Object>} groupsList
* @returns {Object|boolean}
*/
getGroupById(groupId, groupsList)
{
let groups = Object.keys(groupsList);
if(0 === groups.length){
return false;
}
for(let i of groups){
if(groupsList[i].id === groupId){
return groupsList[i];
}
}
}
}
module.exports.InventoryPlugin = InventoryPlugin;