UNPKG

reldens

Version:
343 lines (323 loc) 12.8 kB
/** * * Reldens - PreloaderHandler. * * Manages preloading and creation of animations for skills and class paths. * */ const { GameConst } = require('../../game/constants'); const { Logger, sc } = require('@reldens/utils'); /** * @typedef {import('../../game/client/game-manager').GameManager} GameManager * @typedef {import('@reldens/utils').EventsManager} EventsManager * @typedef {import('phaser').Scene} PhaserScene * * @typedef {Object} PreloaderHandlerProps * @property {GameManager} [gameManager] * @property {EventsManager} [events] * @property {string} [assetsCustomActionsSpritesPath] * * @typedef {Object} AnimationData * @property {string} key * @property {string} [skillKey] * @property {Object} animationData * @property {Object} [classKey] */ class PreloaderHandler { /** * @param {PreloaderHandlerProps} props */ constructor(props) { /** @type {GameManager|false} */ this.gameManager = sc.get(props, 'gameManager', false); if(!this.gameManager){ Logger.error('Game Manager undefined in ActionsPlugin PreloaderHandler.'); } /** @type {EventsManager|false} */ this.events = sc.get(props, 'events', false); if(!this.events){ Logger.error('EventsManager undefined in ActionsPlugin PreloaderHandler.'); } this.setProperties(props); } /** * @param {PreloaderHandlerProps} props * @returns {boolean|void} */ setProperties(props) { if(!this.gameManager){ return false; } this.gameDom = this.gameManager.gameDom; this.initialGameData = this.gameManager.initialGameData; this.levelsAnimConfig = this.gameManager.config.get('client/levels/animations'); this.skillsAnimConfig = this.gameManager.config.get('client/skills/animations'); this.assetsCustomActionsSpritesPath = sc.get( props, 'assetsCustomActionsSpritesPath', 'assets/custom/actions/sprites/' ); if(!this.gameManager.loadedAssets){ this.gameManager.loadedAssets = {}; } if(!this.gameManager.createdAnimations){ this.gameManager.createdAnimations = {}; } } /** * @param {PhaserScene} uiScene */ loadContents(uiScene) { uiScene.load.html('skillsClassPath', '/assets/features/skills/templates/ui-class-path.html'); uiScene.load.html('skillsLevel', '/assets/features/skills/templates/ui-level.html'); uiScene.load.html('skillsExperience', '/assets/features/skills/templates/ui-experience.html'); uiScene.load.html('skills', '/assets/features/skills/templates/ui-skills.html'); uiScene.load.html('skillBox', '/assets/features/skills/templates/ui-skill-box.html'); uiScene.load.html('actionBox', '/assets/html/ui-action-box.html'); this.preloadClassPaths(uiScene); this.loopAnimationsAnd(this.levelsAnimConfig, 'preload', uiScene); this.loopAnimationsAnd(this.skillsAnimConfig, 'preload', uiScene); } /** * @param {PhaserScene} uiScene * @returns {boolean|void} */ preloadClassPaths(uiScene) { let classesData = sc.get(this.initialGameData, 'classesData', false); if(!classesData){ return false; } for(let i of Object.keys(classesData)){ let avatarKey = classesData[i].key; let spriteSheetLoader = uiScene.load.spritesheet( avatarKey, '/assets/custom/sprites/'+avatarKey+GameConst.FILES.EXTENSIONS.PNG, uiScene.playerSpriteSize ); spriteSheetLoader.on('filecomplete', async (completedKey) => { this.gameManager.loadedAssets[completedKey] = completedKey; }); } } /** * @param {PhaserScene} preloadScene */ createAnimations(preloadScene) { let levelsAnimations = this.levelsAnimConfig; this.loopAnimationsAnd(levelsAnimations, 'create', preloadScene); let skillsAnimations = this.skillsAnimConfig; this.loopAnimationsAnd(skillsAnimations, 'create', preloadScene); this.createAvatarsAnimations(preloadScene); } /** * @param {PhaserScene} preloadScene * @returns {Object<string, string>|boolean} */ createAvatarsAnimations(preloadScene) { let classesData = sc.get(this.initialGameData, 'classesData', false); if(!classesData){ Logger.debug('Classes data not found. Fallback to player avatar.'); return false; } if(!this.gameManager.mappedAvatars){ this.gameManager.mappedAvatars = {}; } Logger.debug({availableClassesData: classesData}); for(let i of Object.keys(classesData)){ let avatarKey = classesData[i].key; if(!this.gameManager.loadedAssets[avatarKey]){ avatarKey = GameConst.IMAGE_PLAYER; Logger.info('Avatar for class path "'+avatarKey+'" not found in assets. Fallback to player avatar.'); } this.gameManager.mappedAvatars[avatarKey] = avatarKey; preloadScene.createPlayerAnimations(avatarKey); } return this.gameManager.mappedAvatars; } /** * @param {Object<string, AnimationData>} animations * @param {string} command * @param {PhaserScene} uiScene * @returns {boolean|void} */ loopAnimationsAnd(animations, command, uiScene) { if(!animations){ Logger.warning('Animations not found.', animations); return false; } for(let i of Object.keys(animations)){ let data = animations[i]; if(!data.animationData.enabled){ Logger.debug('Animation "'+i+'" not enabled, skipping.', data); continue; } Logger.debug({[command+'Animation']: data}); this[command+'Animation'](data, uiScene); } } /** * @param {AnimationData} data * @param {PhaserScene} uiScene */ preloadAnimation(data, uiScene) { // @TODO - BETA - Remove the hardcoded file extensions. // @NOTE: here we use have two keys, the animation key and the animationData.img, this is because we could have // a single sprite with multiple attacks, and use the start and end frame to run the required one. if( sc.hasOwn(data.animationData, ['type', 'img']) && GameConst.ANIMATIONS_TYPE.SPRITESHEET === data.animationData.type ){ this.preloadAnimationsInDirections(data, uiScene); } if(data.classKey && sc.isFunction(data.classKey['prepareAnimation'])){ data.classKey['prepareAnimation']({data, uiScene, pack: this}); } } /** * @param {AnimationData} data * @param {PhaserScene} uiScene */ preloadAnimationsInDirections(data, uiScene) { // try load directions: // - 1: both (this is to include diagonals) // - 2: up/down // - 3: left/right let animDir = sc.get(data.animationData, 'dir', 0); if(0 === animDir){ uiScene.load.spritesheet( this.getAnimationKey(data), this.assetsCustomActionsSpritesPath + data.animationData.img+GameConst.FILES.EXTENSIONS.PNG, data.animationData ); return; } // @TODO - BETA - Refactor and implement animDir = 1 (both): up_right, up_left, down_right, down_left. if(1 === animDir || 2 === animDir){ this.preloadSpriteInDirection(uiScene, data, GameConst.UP); this.preloadSpriteInDirection(uiScene, data, GameConst.DOWN); } if(1 === animDir || 3 === animDir){ this.preloadSpriteInDirection(uiScene, data, GameConst.LEFT); this.preloadSpriteInDirection(uiScene, data, GameConst.RIGHT); } } /** * @param {PhaserScene} uiScene * @param {AnimationData} data * @param {string} direction */ preloadSpriteInDirection(uiScene, data, direction) { uiScene.load.spritesheet( this.getAnimationKey(data, direction), this.assetsCustomActionsSpritesPath+data.animationData.img+'_'+direction+GameConst.FILES.EXTENSIONS.PNG, data.animationData ); } /** * @param {AnimationData} data * @param {PhaserScene} uiScene */ createAnimation(data, uiScene) { if( sc.hasOwn(data.animationData, ['type', 'img']) && data.animationData.type === GameConst.ANIMATIONS_TYPE.SPRITESHEET ){ let animDir = sc.get(data.animationData, 'dir', 0); 0 < animDir ? this.createWithMultipleDirections(uiScene, data, animDir) : this.createWithDirection(data, uiScene); } if(data.classKey && sc.isFunction(data.classKey['createAnimation'])){ data.classKey['createAnimation']({data, uiScene, pack: this}); } } /** * @param {PhaserScene} uiScene * @param {AnimationData} data * @param {number} animDir */ createWithMultipleDirections(uiScene, data, animDir) { // @TODO - BETA - Refactor and implement animDir = 1 (both): up_right, up_left, down_right, // down_left. uiScene.directionalAnimations[this.getAnimationKey(data)] = data.animationData.dir; if(1 === animDir || 2 === animDir){ this.createWithDirection(data, uiScene, GameConst.UP); this.createWithDirection(data, uiScene, GameConst.DOWN); } if(1 === animDir || 3 === animDir){ this.createWithDirection(data, uiScene, GameConst.LEFT); this.createWithDirection(data, uiScene, GameConst.RIGHT); } } /** * @param {AnimationData} data * @param {PhaserScene} uiScene * @param {string} [direction] * @returns {Object} */ createWithDirection(data, uiScene, direction = '') { let animationCreateData = this.prepareAnimationData(data, uiScene, direction); if(this.gameManager.createdAnimations[animationCreateData.key]){ return this.gameManager.createdAnimations[animationCreateData.key]; } let newAnimation = uiScene.anims.create(animationCreateData); if(sc.hasOwn(data.animationData, 'destroyTime')){ newAnimation.destroyTime = data.animationData.destroyTime; } if(sc.hasOwn(data.animationData, 'depthByPlayer')){ newAnimation.depthByPlayer = data.animationData.depthByPlayer; } this.gameManager.createdAnimations[animationCreateData.key] = newAnimation; return this.gameManager.createdAnimations[animationCreateData.key]; } /** * @param {AnimationData} data * @param {PhaserScene} uiScene * @param {string} [direction] * @returns {Object} */ prepareAnimationData(data, uiScene, direction = '') { // @NOTE: here we use have two keys, the animation key and the animationData.img, this is because we could have // a single sprite with multiple attacks, and use the start and end frame to run the required one. let imageKey = this.getAnimationKey(data, direction); let animationCreateData = { key: imageKey, frames: uiScene.anims.generateFrameNumbers(imageKey, data.animationData), hideOnComplete: sc.get(data.animationData, 'hide', true), }; if(sc.hasOwn(data.animationData, 'duration')){ animationCreateData.duration = data.animationData.duration; } else { animationCreateData.frameRate = sc.get(data.animationData, 'frameRate', uiScene.configuredFrameRate); } if(sc.hasOwn(data.animationData, 'repeat')){ animationCreateData.repeat = data.animationData.repeat; } return animationCreateData; } /** * @param {AnimationData} data * @param {string} [direction] * @returns {string} */ getAnimationKey(data, direction = '') { return (data.skillKey ? data.skillKey+'_' : '')+data.key+(direction && '' !== direction ? '_'+direction : ''); } } module.exports.PreloaderHandler = PreloaderHandler;