// // boppoServer.js // // Created by Thijs Wenker on 3/15/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 SFX_PREFIX = 'https://hifi-content.s3-us-west-1.amazonaws.com/caitlyn/production/elBoppo/sfx/'; var CLOWN_LAUGHS = [ 'clown_laugh_1.wav', 'clown_laugh_2.wav', 'clown_laugh_3.wav', 'clown_laugh_4.wav' ]; var TICK_TOCK_SOUND = 'ticktock%20-%20tock.wav'; var BOXING_RING_BELL_START = 'boxingRingBell.wav'; var BOXING_RING_BELL_END = 'boxingRingBell-end.wav'; var BOPPO_MUSIC = 'boppoMusic.wav'; var CHANNEL_PREFIX = 'io.highfidelity.boppo_server_'; var MESSAGE_HIT = 'hit'; var MESSAGE_ENTER_ZONE = 'enter-zone'; var MESSAGE_UNLOAD_FIX = 'unload-fix'; var DEFAULT_SOUND_VOLUME = 0.6; // don't set the search radius too high, it might remove boppo's from other nearby instances var BOPPO_SEARCH_RADIUS = 4.0; var MILLISECONDS_PER_SECOND = 1000; // Make sure the entities are loaded at startup (TODO: more solid fix) var LOAD_TIMEOUT = 5000; var SECONDS_PER_MINUTE = 60; var DEFAULT_PLAYTIME = 30; // seconds var BASE_TEN = 10; var TICK_TOCK_FROM = 3; // seconds var COOLDOWN_TIME_MS = MILLISECONDS_PER_SECOND * 3; var createBoppoServer = function() { var _this, _isInitialized = false, _clownLaughs = [], _musicInjector, _music, _laughingInjector, _tickTockSound, _boxingBellRingStart, _boxingBellRingEnd, _entityID, _boppoClownID, _channel, _boppoEntities, _isGameRunning, _updateInterval, _timeLeft, _hits, _coolDown; var getOwnBoppoUserData = function() { try { return JSON.parse(Entities.getEntityProperties(_entityID, ['userData']).userData).Boppo; } catch (e) { // e } return {}; }; var updateBoppoEntities = function() { Entities.getChildrenIDs(_entityID).forEach(function(entityID) { try { var userData = JSON.parse(Entities.getEntityProperties(entityID, ['userData']).userData); if (userData.Boppo.type !== undefined) { _boppoEntities[userData.Boppo.type] = entityID; } } catch (e) { // e } }); }; var clearUntrackedBoppos = function() { var position = Entities.getEntityProperties(_entityID, ['position']).position; Entities.findEntities(position, BOPPO_SEARCH_RADIUS).forEach(function(entityID) { try { if (JSON.parse(Entities.getEntityProperties(entityID, ['userData']).userData).Boppo.type === 'boppo') { Entities.deleteEntity(entityID); } } catch (e) { // e } }); }; var updateTimerDisplay = function() { if (_boppoEntities['timer']) { var secondsString = _timeLeft % SECONDS_PER_MINUTE; if (secondsString < BASE_TEN) { secondsString = '0' + secondsString; } var minutesString = Math.floor(_timeLeft / SECONDS_PER_MINUTE); Entities.editEntity(_boppoEntities['timer'], { text: minutesString + ':' + secondsString }); } }; var updateScoreDisplay = function() { if (_boppoEntities['score']) { Entities.editEntity(_boppoEntities['score'], { text: 'SCORE: ' + _hits }); } }; var playSoundAtBoxingRing = function(sound, properties) { var _properties = properties ? properties : {}; if (_properties['volume'] === undefined) { _properties['volume'] = DEFAULT_SOUND_VOLUME; } _properties['position'] = Entities.getEntityProperties(_entityID, ['position']).position; // play beep return Audio.playSound(sound, _properties); }; var onUpdate = function() { _timeLeft--; if (_timeLeft > 0 && _timeLeft <= TICK_TOCK_FROM) { // play beep playSoundAtBoxingRing(_tickTockSound); } if (_timeLeft === 0) { if (_musicInjector !== undefined && _musicInjector.isPlaying()) { _musicInjector.stop(); _musicInjector = undefined; } playSoundAtBoxingRing(_boxingBellRingEnd); _isGameRunning = false; Script.clearInterval(_updateInterval); _updateInterval = null; _coolDown = true; Script.setTimeout(function() { _coolDown = false; _this.resetBoppo(); }, COOLDOWN_TIME_MS); } updateTimerDisplay(); }; var onMessage = function(channel, message, sender) { if (channel === _channel) { if (message === MESSAGE_HIT) { _this.hit(); } else if (message === MESSAGE_ENTER_ZONE && !_isGameRunning) { _this.resetBoppo(); } else if (message === MESSAGE_UNLOAD_FIX && _isInitialized) { _this.unload(); } } }; var BoppoServer = function () { _this = this; _hits = 0; _boppoClownID = null; _coolDown = false; CLOWN_LAUGHS.forEach(function(clownLaugh) { _clownLaughs.push(SoundCache.getSound(SFX_PREFIX + clownLaugh)); }); _tickTockSound = SoundCache.getSound(SFX_PREFIX + TICK_TOCK_SOUND); _boxingBellRingStart = SoundCache.getSound(SFX_PREFIX + BOXING_RING_BELL_START); _boxingBellRingEnd = SoundCache.getSound(SFX_PREFIX + BOXING_RING_BELL_END); _music = SoundCache.getSound(SFX_PREFIX + BOPPO_MUSIC); _boppoEntities = {}; }; BoppoServer.prototype = { preload: function(entityID) { _entityID = entityID; _channel = CHANNEL_PREFIX + entityID; Messages.sendLocalMessage(_channel, MESSAGE_UNLOAD_FIX); Script.setTimeout(function() { clearUntrackedBoppos(); updateBoppoEntities(); Messages.subscribe(_channel); Messages.messageReceived.connect(onMessage); _this.resetBoppo(); _isInitialized = true; }, LOAD_TIMEOUT); }, resetBoppo: function() { if (_boppoClownID !== null) { print('deleting boppo: ' + _boppoClownID); Entities.deleteEntity(_boppoClownID); } var boppoBaseProperties = Entities.getEntityProperties(_entityID, ['position', 'rotation']); _boppoClownID = Entities.addEntity({ angularDamping: 0.0, collisionSoundURL: 'https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/51460__andre-rocha-nascimento__basket-ball-01-bounce.wav', collisionsWillMove: true, compoundShapeURL: 'https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/bopo_phys.obj', damping: 1.0, density: 10000, dimensions: { x: 1.2668079137802124, y: 2.0568051338195801, z: 0.88563752174377441 }, dynamic: 1.0, friction: 1.0, gravity: { x: 0, y: -25, z: 0 }, modelURL: 'https://hifi-content.s3.amazonaws.com/caitlyn/production/elBoppo/elBoppo3_VR.fbx', name: 'El Boppo the Punching Bag Clown', registrationPoint: { x: 0.5, y: 0, z: 0.3 }, restitution: 0.99, rotation: boppoBaseProperties.rotation, position: Vec3.sum(boppoBaseProperties.position, Vec3.multiplyQbyV(boppoBaseProperties.rotation, { x: 0.08666179329156876, y: -1.5698202848434448, z: 0.1847127377986908 })), script: Script.resolvePath('boppoClownEntity.js'), shapeType: 'compound', type: 'Model', userData: JSON.stringify({ lookAt: { targetID: _boppoEntities['lookAtThis'], disablePitch: true, disableYaw: false, disableRoll: true, clearDisabledAxis: true, rotationOffset: { x: 0.0, y: 180.0, z: 0.0} }, Boppo: { type: 'boppo', gameParentID: _entityID }, grabbableKey: { grabbable: false } }) }); updateBoppoEntities(); _boppoEntities['boppo'] = _boppoClownID; }, laugh: function() { if (_laughingInjector !== undefined && _laughingInjector.isPlaying()) { return; } var randomLaughIndex = Math.floor(Math.random() * _clownLaughs.length); _laughingInjector = Audio.playSound(_clownLaughs[randomLaughIndex], { position: Entities.getEntityProperties(_boppoClownID, ['position']).position }); }, hit: function() { if (_coolDown) { return; } if (!_isGameRunning) { var boxingRingBoppoData = getOwnBoppoUserData(); _updateInterval = Script.setInterval(onUpdate, MILLISECONDS_PER_SECOND); _timeLeft = boxingRingBoppoData.playTimeSeconds ? parseInt(boxingRingBoppoData.playTimeSeconds) : DEFAULT_PLAYTIME; _isGameRunning = true; _hits = 0; playSoundAtBoxingRing(_boxingBellRingStart); _musicInjector = playSoundAtBoxingRing(_music, {loop: true, volume: 0.6}); } _hits++; updateTimerDisplay(); updateScoreDisplay(); _this.laugh(); }, unload: function() { print('unload called'); if (_updateInterval) { Script.clearInterval(_updateInterval); } Messages.messageReceived.disconnect(onMessage); Messages.unsubscribe(_channel); Entities.deleteEntity(_boppoClownID); print('endOfUnload'); } }; return new BoppoServer(); }; return createBoppoServer(); });