// // whackAMole/whackAMoleConsoleES.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 // (function() { var _this; var constants = Script.require('./constants.js'); var helpers = Script.require('./helpers.js'); var Mole = Script.require('./mole.js'); var ScoreBoard = Script.require('./scoreBoard.js'); var MOLE_STATE = constants.MOLE_STATE; var DEFAULT_MOLE_IDLE_TIME = 2000; // seconds var GAME_SPEED_START = 1.0; // Speed at which the game begins before beginning to increases towards the end speed: var GAME_SPEED_END = 3.0; var GAME_FPS = 60; // updater var BACKGROUND_MUSIC_URL = helpers.getSoundURL('223196__rap2h__3-revoltant.wav'); // This is the state in which the machine is when not all entities required are loaded yet var STATE_INITIALIZING = 0; // Mode in which the game is trying to lure a player to join var STATE_LURE = 1; // The mode the game is in while someone plays it var STATE_PLAYING = 2; var WAM_READOUTS_PREFIX = 'wam-readout_'; var WAM_READOUTS_REGEX = new RegExp('^' + WAM_READOUTS_PREFIX + '[0-9]$'); var WAM_READOUTS_ORDER = [[1, 2], [3, 4]]; var WhackAMoleEntityServer = function() { _this = this; }; WhackAMoleEntityServer.prototype = { consoleEntityID: null, moles: null, musicClip: null, musicInjector: null, scoreBoards: null, state: null, channel: null, updateTimer: null, gameSpeed: null, previousMoleIndex: null, currentMole: null, changeMoleAt: null, currentMoleWhacked: null, mainPlayerSessionUUID: null, playerScores: null, gameStartedAt: null, gameEndsAt: null, lastStartupCheck: null, preload: function(entityID) { _this.consoleEntityID = entityID; _this.moles = []; _this.scoreBoards = []; _this.entityID = entityID; _this.musicClip = SoundCache.getSound(BACKGROUND_MUSIC_URL); _this.previousMoleIndex = -1; _this.state = STATE_LURE; _this.currentMole = null; _this.playerScores = [0, 0]; _this.gameStartedAt = -1; _this.gameEndsAt = -1; _this.channel = constants.SERVER_CHANNEL_PREFIX + _this.consoleEntityID; helpers.debugPrint('_this.channel = ' + _this.channel); Messages.messageReceived.connect(_this.onMessage); Messages.subscribe(_this.channel); _this.refreshMoles(); _this.refreshScoreBoard(); _this.updateTimer = Script.setInterval(_this.onUpdate, constants.MILLISECONDS_PER_SECOND / GAME_FPS); _this.setState(STATE_INITIALIZING, true); _this.setState(STATE_LURE, true); }, getMasterVolume: function() { var defaultMasterVolume = 1.0; try { var userData = JSON.parse(Entities.getEntityProperties(_this.consoleEntityID, 'userData').userData); if (userData.whackAMole.masterVolume !== undefined) { return userData.whackAMole.masterVolume; } } catch (e) { // e } return defaultMasterVolume; }, getRandomMole: function() { var moleCount = constants.MOLE_GRID_COLUMNS * constants.MOLE_GRID_ROWS; var freeMoles = []; for (var i = 0; i < moleCount; i++) { if (i !== _this.previousMoleIndex) { freeMoles.push(i); } } var randomMoleIndex = freeMoles[Math.floor(Math.random() * freeMoles.length)]; return { index: randomMoleIndex, column: Math.floor(randomMoleIndex / constants.MOLE_GRID_COLUMNS), row: randomMoleIndex % constants.MOLE_GRID_ROWS }; }, nextRandomMole: function() { var randomMole = _this.getRandomMole(); _this.previousMoleIndex = randomMole.index; _this.currentMole = _this.moles[randomMole.column][randomMole.row]; return _this.currentMole; }, nextAttract: function(nowMilliseconds) { var mole =_this.nextRandomMole(); var runtimeMilliseconds = mole.attract(); _this.changeMoleAt = nowMilliseconds + runtimeMilliseconds; }, nextPopUp: function(nowMilliseconds, gameSpeed) { var mole =_this.nextRandomMole(); var runtimeMilliseconds = mole.beginMove(nowMilliseconds, gameSpeed); _this.changeMoleAt = nowMilliseconds + runtimeMilliseconds; _this.currentMoleWhacked = false; }, resetMoles: function() { for (var column = 0; column < constants.MOLE_GRID_COLUMNS; column++) { for (var row = 0; row < constants.MOLE_GRID_ROWS; row++) { _this.moles[column][row].reset(); } } }, setScore: function(player, score) { _this.playerScores[player] = score; _this.scoreBoards[player].setScore(_this.playerScores[player]); }, incrementScore: function (player) { helpers.debugPrint('Player ' + (player === constants.PLAYER_ONE ? 'one': 'two') + _this.playerScores[player] + ' + ' + '1'); _this.setScore(player, _this.playerScores[player] + 1); }, setState: function(newState, reset) { if (newState === _this.state && !reset) { helpers.debugPrint('State was already set to ' + newState); return; } if (_this.musicInjector !== null && _this.musicInjector.playing) { _this.musicInjector.stop(); } _this.resetMoles(); helpers.debugPrint('Switching state from ' + _this.state + ' to ' + newState); if (newState === STATE_LURE) { _this.nextAttract(Date.now()); } else if (newState === STATE_PLAYING) { var currentMilliseconds = Date.now(); _this.gameSpeed = GAME_SPEED_START; _this.nextPopUp(currentMilliseconds, _this.gameSpeed); _this.setScore(constants.PLAYER_ONE, 0); _this.setScore(constants.PLAYER_TWO, 0); _this.gameStartedAt = currentMilliseconds; _this.gameEndsAt = currentMilliseconds + (constants.SESSION_TIME * constants.MILLISECONDS_PER_SECOND); if (constants.PLAY_SOUND && _this.musicClip.downloaded) { var options = { volume: 0.1 * _this.getMasterVolume(), loop: true, position: Entities.getEntityProperties(_this.consoleEntityID, 'position').position }; _this.musicInjector = Audio.playSound(_this.musicClip, options); } } _this.state = newState; }, onUpdate: function() { var currentMilliseconds = Date.now(); if (_this.state === STATE_PLAYING) { if (currentMilliseconds >= _this.gameEndsAt) { _this.setState(STATE_LURE); return; } var gameProgress = (Date.now() - _this.gameStartedAt) / (_this.gameEndsAt - _this.gameStartedAt); _this.gameSpeed = helpers.mix(GAME_SPEED_START, GAME_SPEED_END, gameProgress); if (_this.currentMole === undefined || _this.currentMole.state === MOLE_STATE.DOWN) { _this.nextPopUp(currentMilliseconds, _this.gameSpeed); } else if (currentMilliseconds >= _this.currentMole.nextMoveAt) { _this.currentMole.nextMove(currentMilliseconds); if (_this.currentMole.state === MOLE_STATE.DOWN) { _this.nextPopUp(currentMilliseconds, _this.gameSpeed); } } } else if (_this.state === STATE_LURE) { if (currentMilliseconds >= _this.changeMoleAt) { _this.nextAttract(currentMilliseconds); } } }, onMessage: function(channel, message, sender) { if (channel !== _this.channel) { return; } var data = JSON.parse(message); if (data.action === 'start') { helpers.debugPrint('Starting mole sequence.'); _this.mainPlayerSessionUUID = sender; _this.refreshMoles(); _this.setState(STATE_PLAYING, true); Script.setTimeout(function () { _this.resetMoles(); }, constants.SESSION_TIME * constants.MILLISECONDS_PER_SECOND); } else if (data.action === 'debugFindMole') { var foundMole = _this.getMoleByEntityID(data.entityID); if (foundMole !== null) { helpers.debugPrint('found mole: ' + JSON.stringify(foundMole)); } } else if (data.action === 'whackMole' && _this.state === STATE_PLAYING && !_this.currentMoleWhacked) { var whackedMole = _this.getMoleByEntityID(data.entityID); if (whackedMole !== null && whackedMole === _this.currentMole) { helpers.debugPrint('found mole: ' + JSON.stringify(whackedMole)); whackedMole.hit(); _this.currentMoleWhacked = true; _this.incrementScore(data.player); } } else { helpers.debugPrint('Could not find action (`' + data.action + '`) with message: ' + message); } }, unload: function() { Script.clearInterval(_this.updateTimer); if (_this.musicInjector !== null && _this.musicInjector.playing) { _this.musicInjector.stop(); } Messages.unsubscribe(_this.channel); }, getMoleByEntityID: function(entityID) { for (var column = 0; column < constants.MOLE_GRID_COLUMNS; column++) { for (var row = 0; row < constants.MOLE_GRID_ROWS; row++) { helpers.debugPrint('column ' + column + ' row ' + row); helpers.debugPrint(JSON.stringify(_this.moles[column][row])); var mole = _this.moles[column][row]; if (mole.moleEntityID === entityID || mole.colliderID === entityID) { return mole; } } } return null; }, refreshScoreBoard: function() { var scoreDigits = {}; Entities.getChildrenIDs(_this.consoleEntityID).forEach(function(entityID) { var name = Entities.getEntityProperties(entityID, ['name']).name; if (WAM_READOUTS_REGEX.test(name)) { var readoutIndex = name.substring(WAM_READOUTS_PREFIX.length); helpers.debugPrint(name + ' ' + readoutIndex); scoreDigits[Number(readoutIndex)] = entityID; } }); var newScoreBoardsList = []; for (var i = 0; i < WAM_READOUTS_ORDER.length; i++) { var board = WAM_READOUTS_ORDER[i]; var digits = []; for (var j = 0; j < board.length; j++) { digits.push(scoreDigits[board[j]]); } if (digits.length === board.length) { newScoreBoardsList.push(new ScoreBoard(digits)); } } if (newScoreBoardsList.length === WAM_READOUTS_ORDER.length) { _this.scoreBoards = newScoreBoardsList; } }, refreshMoles: function() { var newMoleList = []; var masterVolume = _this.getMasterVolume(); Entities.getChildrenIDs(_this.consoleEntityID).forEach(function(entityID) { var properties = Entities.getEntityProperties(entityID, ['name']); if (properties.name.indexOf(constants.MOLE_IDENTIFIER_PREFIX) === 0 && properties.name.length === constants.MOLE_IDENTIFIER_NUM_CHARS && properties.name[constants.MOLE_IDENTIFIER_SEPARATOR] === '_') { var column = parseInt(properties.name[constants.MOLE_IDENTIFIER_COLUMN_INDEX]) - 1; var row = parseInt(properties.name[constants.MOLE_IDENTIFIER_ROW_INDEX]) - 1; helpers.debugPrint(column + ' ' + row); if (column >= 0 && column < constants.MOLE_GRID_COLUMNS && row >= 0 && row < constants.MOLE_GRID_ROWS) { newMoleList.push(new Mole(entityID, column, row, DEFAULT_MOLE_IDLE_TIME, masterVolume)); helpers.debugPrint(properties.name); } } }); if (newMoleList.length !== (constants.MOLE_GRID_COLUMNS * constants.MOLE_GRID_ROWS)) { helpers.debugPrint('Incorrect number of moles. Probably still loading the game.'); return; } _this.moles = []; for (var column = 0; column < constants.MOLE_GRID_COLUMNS; column++) { var rowArray = []; for (var row = 0; row < constants.MOLE_GRID_ROWS; row++) { rowArray.push(null); } _this.moles.push(rowArray); } helpers.debugPrint(JSON.stringify(_this.moles)); for (var i = 0; i < newMoleList.length; i++) { var mole = newMoleList[i]; helpers.debugPrint(mole.column + ', ' + mole.row); _this.moles[mole.column][mole.row] = mole; } helpers.debugPrint(JSON.stringify(_this.moles)); } }; return new WhackAMoleEntityServer(); });