// // whackAMole/mole.js // // Created by Thijs Wenker on 5/8/17. // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /* global module */ // @module mole var constants = Script.require('./constants.js'); var helpers = Script.require('./helpers.js'); var MOLE_STATE = constants.MOLE_STATE; // Frame numbers for popping animation var POPUP_FRAME_START = 1; var POPUP_FRAME_END = 28; var DROPDOWN_FRAME_START = 29; var DROPDOWN_FRAME_END = 55; // Frame numbers for attract animation var ATTRACT_FRAME_START = 56; var ATTRACT_FRAME_END = 340; var DEFAULT_FPS = 30; // Make the mole go down faster on hit: var HIT_FPS = 60; var SFX_HIT = SoundCache.getSound(helpers.getSoundURL('whackaMole_hit.wav')); var SFX_LIFT = SoundCache.getSound(helpers.getSoundURL('moleLiftMechanism.wav')); var SFX_LONG_LAUGH = SoundCache.getSound(helpers.getSoundURL('whackaMole_longLaugh.wav')); var SFX_RANDOM_SQUEAKS = [ SoundCache.getSound(helpers.getSoundURL('whackaMole_randomSqueak_1.wav')), SoundCache.getSound(helpers.getSoundURL('whackaMole_randomSqueak_2.wav')), SoundCache.getSound(helpers.getSoundURL('whackaMole_randomSqueak_3.wav')), SoundCache.getSound(helpers.getSoundURL('whackaMole_randomSqueak_4.wav')) ]; var DEFAULT_SFX_VOLUME = 0.2; var Mole = function(entityID, column, row, defaultIdleTime, masterVolume) { this.defaultIdleTime = defaultIdleTime; this.moleEntityID = entityID; this.column = column; this.row = row; this.state = MOLE_STATE.DOWN; this.lastFPS = 0; this.speed = 1.0; this.idleTime = 2000; this.nextMoveAt = -1; this.masterVolume = masterVolume; try { var userData = JSON.parse(Entities.getEntityProperties(entityID, ['userData']).userData); this.colliderID = userData.whackAMole.colliderID; } catch (e) { // e } }; // TODO: make a sub-mole (more physical, so it can really pop out of the hole with the spring action) Mole.prototype = { moleEntityID: null, colliderID: null, column: null, row: null, speed: null, idleTime: null, nextMoveAt: null, _animate: function(firstFrame, lastFrame, fps) { if (fps === undefined) { fps = DEFAULT_FPS; } var entityProperties = { animation: { currentFrame: firstFrame, firstFrame: firstFrame, lastFrame: lastFrame, running: true, url: helpers.getModelURL('moleRig_Opt.fbx'), loop: false, fps: fps, hold: true } }; helpers.debugPrint('playing animation -> ' + JSON.stringify(entityProperties)); Entities.editEntity(this.moleEntityID, entityProperties); return (Math.abs(firstFrame - lastFrame) / fps) * constants.MILLISECONDS_PER_SECOND; }, _playSound: function(sound, properties) { if (properties === undefined) { properties = {}; } if (properties.position === undefined) { properties.position = Entities.getEntityProperties(this.moleEntityID, 'position').position; } if (properties.volume === undefined) { properties.volume = DEFAULT_SFX_VOLUME; } return Audio.playSound(sound, properties); }, // makes the mole spin spin: function() { Entities.editEntity(this.moleEntityID, { angularVelocity: Quat.fromVec3Degrees({ x: 0, y: 20, z: 0 }), angularDamping: 0.0 }); }, reset: function() { this._animate(0, 0); }, reactAngry: function() { var position = Entities.getEntityProperties(this.moleEntityID, 'position').position; position.y += 0.3; Entities.addEntity({ azimuthStart: 2.5, dimensions: { x: 0.26400002837181091, y: 0.26400002837181091, z: 0.26400002837181091 }, emitAcceleration: { x: 0, y: 0, z: 0 }, emitOrientation: { w: 0.7070610523223877, x: -0.70715254545211792, y: -1.5258869098033756e-05, z: -1.5258869098033756e-05 }, emitRate: 3, emitSpeed: 0.20000000298023224, lifespan: 0.40000000596046448, maxParticles: 5, particleRadius: 0.045000001788139343, radiusFinish: 0.045000001788139343, radiusStart: 0.045000001788139343, speedSpread: 0.10000000149011612, textures: Script.resolvePath('particles/cartoonSwear_wide.png'), type: "ParticleEffect", lifetime: 0.6, position: position }); }, hit: function() { var currentMilliseconds = Date.now(); helpers.debugPrint('HIT!!'); this.reactAngry(); this._playSound(SFX_HIT); if (this.state === MOLE_STATE.POPUP) { var startFrame = DROPDOWN_FRAME_START; try { var animation = Entities.getEntityProperties(this.moleEntityID, 'animation').animation; var maxDropDownFrames = DROPDOWN_FRAME_END - DROPDOWN_FRAME_START; startFrame = DROPDOWN_FRAME_END - Math.min(animation.currentFrame - POPUP_FRAME_START, maxDropDownFrames); } catch (e) { // e } this.nextMoveAt = currentMilliseconds + this.dropDown(HIT_FPS, startFrame); } else if (this.state === MOLE_STATE.UP) { this.nextMoveAt = currentMilliseconds + this.dropDown(HIT_FPS); } }, beginMove: function(currentMilliseconds, speed) { this.speed = speed; this.nextMoveAt = currentMilliseconds + this.popUp(); }, nextMove: function(currentMilliseconds) { if (this.state === MOLE_STATE.POPUP) { this.idle(); this.nextMoveAt = currentMilliseconds + (this.idleTime / this.speed); } else if (this.state === MOLE_STATE.UP) { this.nextMoveAt = currentMilliseconds + this.dropDown(); } else if (this.state === MOLE_STATE.DROPDOWN) { this.state = MOLE_STATE.DOWN; } }, popUp: function(fps) { this._playSound(SFX_LIFT); this.state = MOLE_STATE.POPUP; var runtimeMilliseconds = this._animate(POPUP_FRAME_START, POPUP_FRAME_END, fps); helpers.debugPrint('popUp Mole -> ' + this.moleEntityID); return runtimeMilliseconds; }, idle: function() { this.state = MOLE_STATE.UP; var runtimeMilliseconds = this._animate(POPUP_FRAME_END, POPUP_FRAME_END); helpers.debugPrint('idle Mole -> ' + this.moleEntityID); return runtimeMilliseconds; }, dropDown: function(fps, startFrame) { if (startFrame === undefined) { startFrame = DROPDOWN_FRAME_START; } this.state = MOLE_STATE.DROPDOWN; var randomSqueak = SFX_RANDOM_SQUEAKS[Math.floor(Math.random() * SFX_RANDOM_SQUEAKS.length)]; this._playSound(randomSqueak); var runtimeMilliseconds = this._animate(startFrame, DROPDOWN_FRAME_END, fps); helpers.debugPrint('dropDown Mole -> ' + this.moleEntityID); return runtimeMilliseconds; }, attract: function() { if (Math.random() < 0.20) { this._playSound(SFX_LONG_LAUGH); } this.state = MOLE_STATE.LURE; return this._animate(ATTRACT_FRAME_START, ATTRACT_FRAME_END); } }; module.exports = Mole;