//// tutorial.js//// Created by Ryan Huffman on 9/1/16.// Copyright 2016 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// Script.include("entityData.js");Script.include("lighter/createButaneLighter.js");Script.include("tutorialEntityIDs.js"); if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { if (typeof this !== 'function') { // closest thing possible to the ECMAScript 5 // internal IsCallable function throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function() {}, fBound = function() { return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; if (this.prototype) { // Function.prototype doesn't have a prototype property fNOP.prototype = this.prototype; } fBound.prototype = new fNOP(); return fBound; };} var DEBUG = true;function debug() { if (DEBUG) { var args = Array.prototype.slice.call(arguments); args.unshift("tutorial.js | "); print.apply(this, args); }} var INFO = true;function info() { if (INFO) { var args = Array.prototype.slice.call(arguments); args.unshift("tutorial.js | "); print.apply(this, args); }} var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn";var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn";var GUN_SPAWN_NAME = "tutorial/gun_spawn";var TELEPORT_PAD_NAME = "tutorial/teleport/pad" var successSound = SoundCache.getSound("atp:/tutorial_sounds/good_one.L.wav");var firecrackerSound = SoundCache.getSound("atp:/tutorial_sounds/Pops_Firecracker.wav"); var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable";function setAwayEnabled(value) { var message = value ? 'enable' : 'disable'; Messages.sendLocalMessage(CHANNEL_AWAY_ENABLE, message);} findEntity = function(properties, searchRadius, filterFn) { var entities = findEntities(properties, searchRadius, filterFn); return entities.length > 0 ? entities[0] : null;} // Return all entities with properties `properties` within radius `searchRadius`findEntities = function(properties, searchRadius, filterFn) { if (!filterFn) { filterFn = function(properties, key, value) { return value == properties[key]; } } searchRadius = searchRadius ? searchRadius : 100000; var entities = Entities.findEntities({ x: 0, y: 0, z: 0 }, searchRadius); var matchedEntities = []; var keys = Object.keys(properties); for (var i = 0; i < entities.length; ++i) { var match = true; var candidateProperties = Entities.getEntityProperties(entities[i], keys); for (var key in properties) { if (!filterFn(properties, key, candidateProperties[key])) { // This isn't a match, move to next entity match = false; break; } } if (match) { matchedEntities.push(entities[i]); } } return matchedEntities;} function findEntitiesWithTag(tag) { return findEntities({ userData: "" }, 10000, function(properties, key, value) { data = parseJSON(value); return data.tag === tag; });} /** * A controller in made up of parts, and each part can have multiple "layers," * which are really just different texures. For example, the "trigger" part * has "normal" and "highlight" layers. */function setControllerPartLayer(part, layer) { data = {}; data[part] = layer; Messages.sendLocalMessage('Controller-Set-Part-Layer', JSON.stringify(data));} /** * @param {object[]} entityPropertiesList A list of properties to */function spawn(entityPropertiesList, transform, modifyFn) { debug("Creating: ", entityPropertiesList); if (!transform) { transform = { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0, w: 1 } } } var ids = []; for (var i = 0; i < entityPropertiesList.length; ++i) { var data = entityPropertiesList[i]; debug("Creating: ", data.name); data.position = Vec3.sum(transform.position, data.position); data.rotation = Quat.multiply(data.rotation, transform.rotation); if (modifyFn) { data = modifyFn(data); } var id = Entities.addEntity(data); ids.push(id); debug(id, "data:", JSON.stringify(data)); } return ids;} /** * @function parseJSON * @param {string} jsonString The string to parse. * @return {object} Return an empty if the string was not valid JSON, otherwise * the parsed object is returned. */function parseJSON(jsonString) { var data; try { data = JSON.parse(jsonString); } catch(e) { data = {}; } return data;} /** * Spawn entities with `tag` in the userData. * @function spawnWithTag */function spawnWithTag(entityData, transform, tag) { function modifyFn(data) { var userData = parseJSON(data.userData); userData.tag = tag; data.userData = JSON.stringify(userData); debug("In modify", tag, userData, data.userData); return data; } return spawn(entityData, transform, modifyFn);} /** * Delete all entities with the tag `tag` in their userData. * @function deleteEntitiesWithTag */function deleteEntitiesWithTag(tag) { debug("searching for...:", tag); var entityIDs = findEntitiesWithTag(tag); for (var i = 0; i < entityIDs.length; ++i) { Entities.deleteEntity(entityIDs[i]); }} function editEntitiesWithTag(tag, propertiesOrFn) { var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; debug("Editing tag: ", tag); if (entities) { for (entityID in entities) { debug("Editing: ", entityID, ", ", propertiesOrFn, ", Is in local tree: ", isEntityInLocalTree(entityID)); if (isFunction(propertiesOrFn)) { Entities.editEntity(entityID, propertiesOrFn(entityIDs[i])); } else { Entities.editEntity(entityID, propertiesOrFn); } } }} // From http://stackoverflow.com/questions/5999998/how-can-i-check-if-a-javascript-variable-is-function-typefunction isFunction(functionToCheck) { var getType = {}; return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]';} /** * Return `true` if `entityID` can be found in the local entity tree, otherwise `false`. */function isEntityInLocalTree(entityID) { return Entities.getEntityProperties(entityID, 'visible').visible !== undefined;} /** * */function showEntitiesWithTag(tag) { var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; if (entities) { for (entityID in entities) { var data = entities[entityID]; var collisionless = data.visible === false ? true : false; if (data.collidable !== undefined) { collisionless = data.collidable === true ? false : true; } if (data.soundKey) { data.soundKey.playing = true; } var newProperties = { visible: data.visible == false ? false : true, collisionless: collisionless, userData: JSON.stringify(data), }; debug("Showing: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); Entities.editEntity(entityID, newProperties); } } // Dynamic method, suppressed for now return; editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; var data = parseJSON(userData); var collisionless = data.visible === false ? true : false; if (data.collidable !== undefined) { collisionless = data.collidable === true ? false : true; } if (data.soundKey) { data.soundKey.playing = true; } var newProperties = { visible: data.visible == false ? false : true, collisionless: collisionless, userData: JSON.stringify(data), }; Entities.editEntity(entityID, newProperties); });} /** * @function hideEntitiesWithTag */function hideEntitiesWithTag(tag) { var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; if (entities) { for (entityID in entities) { var data = entities[entityID]; if (data.soundKey) { data.soundKey.playing = false; } var newProperties = { visible: false, collisionless: 1, ignoreForCollisions: 1, userData: JSON.stringify(data), }; debug("Hiding: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); Entities.editEntity(entityID, newProperties); } } // Dynamic method, suppressed for now return; editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; var data = parseJSON(userData); if (data.soundKey) { data.soundKey.playing = false; } var newProperties = { visible: false, collisionless: 1, ignoreForCollisions: 1, userData: JSON.stringify(data), }; Entities.editEntity(entityID, newProperties); });} // Return the entity properties for an entity with a given name if it is in our// cached list of entities. Otherwise, return undefined.function getEntityWithName(name) { debug("Getting entity with name:", name); var entityID = TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP[name]; debug("Entity id: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); return entityID;} function playSuccessSound() { Audio.playSound(successSound, { position: MyAvatar.position, volume: 0.7, loop: false });} function playFirecrackerSound(position) { Audio.playSound(firecrackerSound, { position: position, volume: 0.5, loop: false });} //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //// STEP: DISABLE CONTROLLERS //// /////////////////////////////////////////////////////////////////////////////////var stepStart = function(name) { this.tag = name;}stepStart.prototype = { start: function(onFinish) { disableEverything(); HMD.requestShowHandControllers(); onFinish(); }, cleanup: function() { }}; function disableEverything() { editEntitiesWithTag('door', { visible: true, collisionless: false }); Menu.setIsOptionChecked("Overlays", false); Controller.disableMapping('handControllerPointer-click'); Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'disable'); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ nearGrabEnabled: true, holdEnabled: false, farGrabEnabled: false, })); setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('tips', 'blank'); hideEntitiesWithTag('finish'); setAwayEnabled(false);} function reenableEverything() { editEntitiesWithTag('door', { visible: false, collisionless: true }); Menu.setIsOptionChecked("Overlays", true); Controller.enableMapping('handControllerPointer-click'); Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'enable'); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ nearGrabEnabled: true, holdEnabled: true, farGrabEnabled: true, })); setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('tips', 'blank'); MyAvatar.shouldRenderLocally = true; setAwayEnabled(true);} //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //// STEP: ENABLE CONTROLLERS //// ///////////////////////////////////////////////////////////////////////////////// var stepEnableControllers = function(name) { this.tag = name; this.shouldLog = false;}stepEnableControllers.prototype = { start: function(onFinish) { reenableEverything(); HMD.requestHideHandControllers(); onFinish(); }, cleanup: function() { }}; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //// STEP: Welcome //// /////////////////////////////////////////////////////////////////////////////////var stepWelcome = function(name) { this.tag = name;}stepWelcome.prototype = { start: function(onFinish) { this.timerID = Script.setTimeout(onFinish, 8000); showEntitiesWithTag(this.tag); }, cleanup: function() { if (this.timerID) { Script.clearTimeout(this.timerID); this.timerID = null; } hideEntitiesWithTag(this.tag); }}; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //// STEP: Orient and raise hands above head //// /////////////////////////////////////////////////////////////////////////////////var stepOrient = function(name) { this.tag = name; this.tempTag = name + "-temporary";}stepOrient.prototype = { start: function(onFinish) { this.active = true; var tag = this.tag; // Spawn content set editEntitiesWithTag(this.tag, { visible: true }); this.checkIntervalID = null; function checkForHandsAboveHead() { debug("Orient | Checking for hands above head"); if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; location = "/tutorial"; Script.setTimeout(playSuccessSound, 150); this.active = false; onFinish(); } } this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); }, cleanup: function() { debug("Orient | Cleanup"); if (this.active) { this.active = false; } if (this.overlay) { this.overlay.destroy(); this.overlay = null; } if (this.checkIntervalID) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; } editEntitiesWithTag(this.tag, { visible: false, collisionless: 1 }); deleteEntitiesWithTag(this.tempTag); }}; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //// STEP: Near Grab //// //Lines 1–500 of 1127. Load more
// // tutorial.js // // Created by Ryan Huffman on 9/1/16. // Copyright 2016 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 // Script.include("entityData.js"); Script.include("lighter/createButaneLighter.js"); Script.include("tutorialEntityIDs.js"); if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { if (typeof this !== 'function') { // closest thing possible to the ECMAScript 5 // internal IsCallable function throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function() {}, fBound = function() { return fToBind.apply(this instanceof fNOP ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; if (this.prototype) { // Function.prototype doesn't have a prototype property fNOP.prototype = this.prototype; } fBound.prototype = new fNOP(); return fBound; }; } var DEBUG = true; function debug() { if (DEBUG) { var args = Array.prototype.slice.call(arguments); args.unshift("tutorial.js | "); print.apply(this, args); } } var INFO = true; function info() { if (INFO) { var args = Array.prototype.slice.call(arguments); args.unshift("tutorial.js | "); print.apply(this, args); } } var NEAR_BOX_SPAWN_NAME = "tutorial/nearGrab/box_spawn"; var FAR_BOX_SPAWN_NAME = "tutorial/farGrab/box_spawn"; var GUN_SPAWN_NAME = "tutorial/gun_spawn"; var TELEPORT_PAD_NAME = "tutorial/teleport/pad" var successSound = SoundCache.getSound("atp:/tutorial_sounds/good_one.L.wav"); var firecrackerSound = SoundCache.getSound("atp:/tutorial_sounds/Pops_Firecracker.wav"); var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable"; function setAwayEnabled(value) { var message = value ? 'enable' : 'disable'; Messages.sendLocalMessage(CHANNEL_AWAY_ENABLE, message); } findEntity = function(properties, searchRadius, filterFn) { var entities = findEntities(properties, searchRadius, filterFn); return entities.length > 0 ? entities[0] : null; } // Return all entities with properties `properties` within radius `searchRadius` findEntities = function(properties, searchRadius, filterFn) { if (!filterFn) { filterFn = function(properties, key, value) { return value == properties[key]; } } searchRadius = searchRadius ? searchRadius : 100000; var entities = Entities.findEntities({ x: 0, y: 0, z: 0 }, searchRadius); var matchedEntities = []; var keys = Object.keys(properties); for (var i = 0; i < entities.length; ++i) { var match = true; var candidateProperties = Entities.getEntityProperties(entities[i], keys); for (var key in properties) { if (!filterFn(properties, key, candidateProperties[key])) { // This isn't a match, move to next entity match = false; break; } } if (match) { matchedEntities.push(entities[i]); } } return matchedEntities; } function findEntitiesWithTag(tag) { return findEntities({ userData: "" }, 10000, function(properties, key, value) { data = parseJSON(value); return data.tag === tag; }); } /** * A controller in made up of parts, and each part can have multiple "layers," * which are really just different texures. For example, the "trigger" part * has "normal" and "highlight" layers. */ function setControllerPartLayer(part, layer) { data = {}; data[part] = layer; Messages.sendLocalMessage('Controller-Set-Part-Layer', JSON.stringify(data)); } /** * @param {object[]} entityPropertiesList A list of properties to */ function spawn(entityPropertiesList, transform, modifyFn) { debug("Creating: ", entityPropertiesList); if (!transform) { transform = { position: { x: 0, y: 0, z: 0 }, rotation: { x: 0, y: 0, z: 0, w: 1 } } } var ids = []; for (var i = 0; i < entityPropertiesList.length; ++i) { var data = entityPropertiesList[i]; debug("Creating: ", data.name); data.position = Vec3.sum(transform.position, data.position); data.rotation = Quat.multiply(data.rotation, transform.rotation); if (modifyFn) { data = modifyFn(data); } var id = Entities.addEntity(data); ids.push(id); debug(id, "data:", JSON.stringify(data)); } return ids; } /** * @function parseJSON * @param {string} jsonString The string to parse. * @return {object} Return an empty if the string was not valid JSON, otherwise * the parsed object is returned. */ function parseJSON(jsonString) { var data; try { data = JSON.parse(jsonString); } catch(e) { data = {}; } return data; } /** * Spawn entities with `tag` in the userData. * @function spawnWithTag */ function spawnWithTag(entityData, transform, tag) { function modifyFn(data) { var userData = parseJSON(data.userData); userData.tag = tag; data.userData = JSON.stringify(userData); debug("In modify", tag, userData, data.userData); return data; } return spawn(entityData, transform, modifyFn); } /** * Delete all entities with the tag `tag` in their userData. * @function deleteEntitiesWithTag */ function deleteEntitiesWithTag(tag) { debug("searching for...:", tag); var entityIDs = findEntitiesWithTag(tag); for (var i = 0; i < entityIDs.length; ++i) { Entities.deleteEntity(entityIDs[i]); } } function editEntitiesWithTag(tag, propertiesOrFn) { var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; debug("Editing tag: ", tag); if (entities) { for (entityID in entities) { debug("Editing: ", entityID, ", ", propertiesOrFn, ", Is in local tree: ", isEntityInLocalTree(entityID)); if (isFunction(propertiesOrFn)) { Entities.editEntity(entityID, propertiesOrFn(entityIDs[i])); } else { Entities.editEntity(entityID, propertiesOrFn); } } } } // From http://stackoverflow.com/questions/5999998/how-can-i-check-if-a-javascript-variable-is-function-type function isFunction(functionToCheck) { var getType = {}; return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]'; } /** * Return `true` if `entityID` can be found in the local entity tree, otherwise `false`. */ function isEntityInLocalTree(entityID) { return Entities.getEntityProperties(entityID, 'visible').visible !== undefined; } /** * */ function showEntitiesWithTag(tag) { var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; if (entities) { for (entityID in entities) { var data = entities[entityID]; var collisionless = data.visible === false ? true : false; if (data.collidable !== undefined) { collisionless = data.collidable === true ? false : true; } if (data.soundKey) { data.soundKey.playing = true; } var newProperties = { visible: data.visible == false ? false : true, collisionless: collisionless, userData: JSON.stringify(data), }; debug("Showing: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); Entities.editEntity(entityID, newProperties); } } // Dynamic method, suppressed for now return; editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; var data = parseJSON(userData); var collisionless = data.visible === false ? true : false; if (data.collidable !== undefined) { collisionless = data.collidable === true ? false : true; } if (data.soundKey) { data.soundKey.playing = true; } var newProperties = { visible: data.visible == false ? false : true, collisionless: collisionless, userData: JSON.stringify(data), }; Entities.editEntity(entityID, newProperties); }); } /** * @function hideEntitiesWithTag */ function hideEntitiesWithTag(tag) { var entities = TUTORIAL_TAG_TO_ENTITY_IDS_MAP[tag]; if (entities) { for (entityID in entities) { var data = entities[entityID]; if (data.soundKey) { data.soundKey.playing = false; } var newProperties = { visible: false, collisionless: 1, ignoreForCollisions: 1, userData: JSON.stringify(data), }; debug("Hiding: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); Entities.editEntity(entityID, newProperties); } } // Dynamic method, suppressed for now return; editEntitiesWithTag(tag, function(entityID) { var userData = Entities.getEntityProperties(entityID, "userData").userData; var data = parseJSON(userData); if (data.soundKey) { data.soundKey.playing = false; } var newProperties = { visible: false, collisionless: 1, ignoreForCollisions: 1, userData: JSON.stringify(data), }; Entities.editEntity(entityID, newProperties); }); } // Return the entity properties for an entity with a given name if it is in our // cached list of entities. Otherwise, return undefined. function getEntityWithName(name) { debug("Getting entity with name:", name); var entityID = TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP[name]; debug("Entity id: ", entityID, ", Is in local tree: ", isEntityInLocalTree(entityID)); return entityID; } function playSuccessSound() { Audio.playSound(successSound, { position: MyAvatar.position, volume: 0.7, loop: false }); } function playFirecrackerSound(position) { Audio.playSound(firecrackerSound, { position: position, volume: 0.5, loop: false }); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // // STEP: DISABLE CONTROLLERS // // // /////////////////////////////////////////////////////////////////////////////// var stepStart = function(name) { this.tag = name; } stepStart.prototype = { start: function(onFinish) { disableEverything(); HMD.requestShowHandControllers(); onFinish(); }, cleanup: function() { } }; function disableEverything() { editEntitiesWithTag('door', { visible: true, collisionless: false }); Menu.setIsOptionChecked("Overlays", false); Controller.disableMapping('handControllerPointer-click'); Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'disable'); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'both'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ nearGrabEnabled: true, holdEnabled: false, farGrabEnabled: false, })); setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('tips', 'blank'); hideEntitiesWithTag('finish'); setAwayEnabled(false); } function reenableEverything() { editEntitiesWithTag('door', { visible: false, collisionless: true }); Menu.setIsOptionChecked("Overlays", true); Controller.enableMapping('handControllerPointer-click'); Messages.sendLocalMessage('Hifi-Advanced-Movement-Disabler', 'enable'); Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ nearGrabEnabled: true, holdEnabled: true, farGrabEnabled: true, })); setControllerPartLayer('touchpad', 'blank'); setControllerPartLayer('tips', 'blank'); MyAvatar.shouldRenderLocally = true; setAwayEnabled(true); } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // // STEP: ENABLE CONTROLLERS // // // /////////////////////////////////////////////////////////////////////////////// var stepEnableControllers = function(name) { this.tag = name; this.shouldLog = false; } stepEnableControllers.prototype = { start: function(onFinish) { reenableEverything(); HMD.requestHideHandControllers(); onFinish(); }, cleanup: function() { } }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // // STEP: Welcome // // // /////////////////////////////////////////////////////////////////////////////// var stepWelcome = function(name) { this.tag = name; } stepWelcome.prototype = { start: function(onFinish) { this.timerID = Script.setTimeout(onFinish, 8000); showEntitiesWithTag(this.tag); }, cleanup: function() { if (this.timerID) { Script.clearTimeout(this.timerID); this.timerID = null; } hideEntitiesWithTag(this.tag); } }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // // STEP: Orient and raise hands above head // // // /////////////////////////////////////////////////////////////////////////////// var stepOrient = function(name) { this.tag = name; this.tempTag = name + "-temporary"; } stepOrient.prototype = { start: function(onFinish) { this.active = true; var tag = this.tag; // Spawn content set editEntitiesWithTag(this.tag, { visible: true }); this.checkIntervalID = null; function checkForHandsAboveHead() { debug("Orient | Checking for hands above head"); if (MyAvatar.getLeftPalmPosition().y > (MyAvatar.getHeadPosition().y + 0.1)) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; location = "/tutorial"; Script.setTimeout(playSuccessSound, 150); this.active = false; onFinish(); } } this.checkIntervalID = Script.setInterval(checkForHandsAboveHead.bind(this), 500); }, cleanup: function() { debug("Orient | Cleanup"); if (this.active) { this.active = false; } if (this.overlay) { this.overlay.destroy(); this.overlay = null; } if (this.checkIntervalID) { Script.clearInterval(this.checkIntervalID); this.checkIntervalID = null; } editEntitiesWithTag(this.tag, { visible: false, collisionless: 1 }); deleteEntitiesWithTag(this.tempTag); } }; /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // // STEP: Near Grab // // //