From 154b33bddb384ce046c4eb0a73b0d9e31c0da81d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Jun 2017 15:17:45 -0700 Subject: [PATCH] Remove /tutorial in favor of new repo Tutorial code has been moved to a new hifi-tutorial repo --- tutorial/ACAudioSearchAndInject_tutorial.js | 308 ----- tutorial/Changelog.md | 3 - tutorial/entityData.js | 131 -- tutorial/firePit/fire.js | 181 --- tutorial/firePit/flicker.js | 52 - tutorial/fuse.js | 119 -- tutorial/fuseCollider.js | 18 - tutorial/lighter/butaneLighter.js | 192 --- tutorial/lighter/createButaneLighter.js | 220 ---- tutorial/ownershipToken.js | 188 --- tutorial/spinner.js | 84 -- tutorial/success.wav | Bin 39758 -> 0 bytes tutorial/success48.wav | Bin 39758 -> 0 bytes tutorial/tutorial.js | 1202 ------------------- tutorial/tutorialEntityIDs.js | 360 ------ tutorial/tutorialStartZone.js | 51 - tutorial/tutorialZone.js | 142 --- 17 files changed, 3251 deletions(-) delete mode 100644 tutorial/ACAudioSearchAndInject_tutorial.js delete mode 100644 tutorial/Changelog.md delete mode 100644 tutorial/entityData.js delete mode 100644 tutorial/firePit/fire.js delete mode 100644 tutorial/firePit/flicker.js delete mode 100644 tutorial/fuse.js delete mode 100644 tutorial/fuseCollider.js delete mode 100644 tutorial/lighter/butaneLighter.js delete mode 100644 tutorial/lighter/createButaneLighter.js delete mode 100644 tutorial/ownershipToken.js delete mode 100644 tutorial/spinner.js delete mode 100644 tutorial/success.wav delete mode 100644 tutorial/success48.wav delete mode 100644 tutorial/tutorial.js delete mode 100644 tutorial/tutorialEntityIDs.js delete mode 100644 tutorial/tutorialStartZone.js delete mode 100644 tutorial/tutorialZone.js diff --git a/tutorial/ACAudioSearchAndInject_tutorial.js b/tutorial/ACAudioSearchAndInject_tutorial.js deleted file mode 100644 index 5e2998ff1e..0000000000 --- a/tutorial/ACAudioSearchAndInject_tutorial.js +++ /dev/null @@ -1,308 +0,0 @@ -"use strict"; - -/*jslint nomen: true, plusplus: true, vars: true*/ -/*global AvatarList, Entities, EntityViewer, Script, SoundCache, Audio, print, randFloat*/ -// -// ACAudioSearchAndInject.js -// audio -// -// Created by Eric Levin and Howard Stearns 2/1/2016 -// Copyright 2016 High Fidelity, Inc. -// -// Keeps track of all sounds within QUERY_RADIUS of an avatar, where a "sound" is specified in entity userData. -// Inject as many as practical into the audio mixer. -// See acAudioSearchAndCompatibilityEntitySpawner.js. -// -// This implementation takes some precautions to scale well: -// - It doesn't hastle the entity server because it issues at most one octree query every UPDATE_TIME period, regardless of the number of avatars. -// - It does not load itself because it only gathers entities once every UPDATE_TIME period, and only -// checks entity properties for those small number of entities that are currently playing (plus a RECHECK_TIME period examination of all entities). -// This implementation tries to use all the available injectors. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html - -var MSEC_PER_SEC = 1000; -var SOUND_DATA_KEY = "io.highfidelity.soundKey"; // Sound data is specified in userData under this key. -var old_sound_data_key = "soundKey"; // For backwards compatibility. -var QUERY_RADIUS = 50; // meters -var UPDATE_TIME = 100; // ms. We'll update just one thing on this period. -var EXPIRATION_TIME = 5 * MSEC_PER_SEC; // ms. Remove sounds that have been out of range for this time. -var RECHECK_TIME = 10 * MSEC_PER_SEC; // ms. Check for new userData properties this often when not currently playing. -// (By not checking most of the time when not playing, we can efficiently go through all entities without getEntityProperties.) -var UPDATES_PER_STATS_LOG = RECHECK_TIME / UPDATE_TIME; // (It's nice to smooth out the results by straddling a recheck.) - -var DEFAULT_SOUND_DATA = { - volume: 0.5, // userData cannot specify zero volume with our current method of defaulting. - loop: false, // Default must be false with our current method of defaulting, else there's no way to get a false value. - playbackGap: MSEC_PER_SEC, // in ms - playbackGapRange: 0 // in ms -}; - -//var AGENT_AVATAR_POSITION = { x: -1.5327, y: 0.672515, z: 5.91573 }; -var AGENT_AVATAR_POSITION = { x: -2.83785, y: 1.45243, z: -13.6042 }; - -//var isACScript = this.EntityViewer !== undefined; -var isACScript = true; - -if (isACScript) { - Agent.isAvatar = true; // This puts a robot at 0,0,0, but is currently necessary in order to use AvatarList. - Avatar.skeletonModelURL = "http://hifi-content.s3.amazonaws.com/ozan/dev/avatars/invisible_avatar/invisible_avatar.fst"; - Avatar.position = AGENT_AVATAR_POSITION; - Agent.isListeningToAudioStream = true; -} -function ignore() {} -function debug() { // Display the arguments not just [Object object]. - //print.apply(null, [].map.call(arguments, JSON.stringify)); -} - -function randFloat(low, high) { - return low + Math.random() * (high - low); -} - -if (isACScript) { - EntityViewer.setCenterRadius(QUERY_RADIUS); -} - -// ENTITY DATA CACHE -// -var entityCache = {}; // A dictionary of unexpired EntityData objects. -var entityInvalidUserDataCache = {}; // A cache containing the entity IDs that have - // previously been identified as containing non-JSON userData. - // We use a dictionary here so id lookups are constant time. -var examinationCount = 0; -function EntityDatum(entityIdentifier) { // Just the data of an entity that we need to know about. - // This data is only use for our sound injection. There is no need to store such info in the replicated entity on everyone's computer. - var that = this; - that.lastUserDataUpdate = 0; // new entity is in need of rechecking user data - // State Transitions: - // no data => no data | sound data | expired - // expired => stop => remove - // sound data => downloading - // downloading => downloading | waiting - // waiting => playing | waiting (if too many already playing) - // playing => update position etc | no data - that.stop = function stop() { - if (!that.sound) { - return; - } - print("stopping sound", entityIdentifier, that.url); - delete that.sound; - delete that.url; - if (!that.injector) { - return; - } - that.injector.stop(); - delete that.injector; - }; - this.update = function stateTransitions(expirationCutoff, userDataCutoff, now) { - if (that.timestamp < expirationCutoff) { // EXPIRED => STOP => REMOVE - that.stop(); // Alternatively, we could fade out and then stop... - delete entityCache[entityIdentifier]; - return; - } - var properties, soundData; // Latest data, pulled from local octree. - - // getEntityProperties locks the tree, which competes with the asynchronous processing of queryOctree results. - // Most entity updates are fast and only a very few do getEntityProperties. - function ensureSoundData() { // We only getEntityProperities when we need to. - if (properties) { - return; - } - properties = Entities.getEntityProperties(entityIdentifier, ['userData', 'position']); - examinationCount++; // Collect statistics on how many getEntityProperties we do. - debug("updating", that, properties); - try { - var userData = properties.userData && JSON.parse(properties.userData); - soundData = userData && (userData[SOUND_DATA_KEY] || userData[old_sound_data_key]); // Don't store soundData yet. Let state changes compare. - that.lastUserDataUpdate = now; // But do update these ... - that.url = soundData && soundData.url; - that.playAfter = that.url && now; - } catch (err) { - if (!(entityIdentifier in entityInvalidUserDataCache)) { - print(err, properties.userData); - entityInvalidUserDataCache[entityIdentifier] = true; - } - } - } - - // Stumbling on big new pile of entities will do a lot of getEntityProperties. Once. - if (that.lastUserDataUpdate < userDataCutoff) { // NO DATA => SOUND DATA - ensureSoundData(); - } - - if (!that.url) { // NO DATA => NO DATA - return that.stop(); - } - - if (!that.sound) { // SOUND DATA => DOWNLOADING - that.sound = SoundCache.getSound(soundData.url); // SoundCache can manage duplicates better than we can. - } - - if (!that.sound.downloaded) { // DOWNLOADING => DOWNLOADING - return; - } - - if (that.playAfter > now) { // DOWNLOADING | WAITING => WAITING - return; - } - - ensureSoundData(); // We'll try to play/setOptions and will need position, so we might as well get soundData, too. - if (soundData.url !== that.url) { // WAITING => NO DATA (update next time around) - return that.stop(); - } - - var options = { - position: properties.position, - loop: soundData.loop || DEFAULT_SOUND_DATA.loop, - volume: soundData.volume || DEFAULT_SOUND_DATA.volume - }; - - function repeat() { - return !options.loop && (soundData.playbackGap >= 0); - } - - function randomizedNextPlay() { // time of next play or recheck, randomized to distribute the work - var range = soundData.playbackGapRange || DEFAULT_SOUND_DATA.playbackGapRange, - base = repeat() ? ((that.sound.duration * MSEC_PER_SEC) + (soundData.playbackGap || DEFAULT_SOUND_DATA.playbackGap)) : RECHECK_TIME; - return now + base + randFloat(-Math.min(base, range), range); - } - - if (that.injector && soundData.playing === false) { - that.injector.stop(); - that.injector = null; - } - - if (!that.injector) { - if (soundData.playing === false) { // WAITING => PLAYING | WAITING - return; - } - debug("starting", that, options); - that.injector = Audio.playSound(that.sound, options); // Might be null if at at injector limit. Will try again later. - if (that.injector) { - print("started", entityIdentifier, that.url); - } else { // Don't hammer ensureSoundData or injector manager. - that.playAfter = randomizedNextPlay(); - } - return; - } - - that.injector.setOptions(options); // PLAYING => UPDATE POSITION ETC - if (!that.injector.playing) { // Subtle: a looping sound will not check playbackGap. - if (repeat()) { // WAITING => PLAYING - // Setup next play just once, now. Changes won't be looked at while we wait. - that.playAfter = randomizedNextPlay(); - // Subtle: if the restart fails b/c we're at injector limit, we won't try again until next playAfter. - that.injector.restart(); - } else { // PLAYING => NO DATA - that.playAfter = Infinity; // was one-shot and we're finished - } - } - }; -} - -function internEntityDatum(entityIdentifier, timestamp, avatarPosition, avatar) { - ignore(avatarPosition, avatar); // We could use avatars and/or avatarPositions to prioritize which ones to play. - var entitySound = entityCache[entityIdentifier]; - if (!entitySound) { - entitySound = entityCache[entityIdentifier] = new EntityDatum(entityIdentifier); - } - entitySound.timestamp = timestamp; // Might be updated for multiple avatars. That's fine. -} - -var nUpdates = UPDATES_PER_STATS_LOG, lastStats = Date.now(); - -function updateAllEntityData() { // A fast update of all entities we know about. A few make sounds. - var now = Date.now(), - expirationCutoff = now - EXPIRATION_TIME, - userDataRecheckCutoff = now - RECHECK_TIME; - Object.keys(entityCache).forEach(function (entityIdentifier) { - entityCache[entityIdentifier].update(expirationCutoff, userDataRecheckCutoff, now); - }); - if (nUpdates-- <= 0) { // Report statistics. - // For example, with: - // injector-limit = 40 (in C++ code) - // N_SOUNDS = 1000 (from userData in, e.g., acAudioSearchCompatibleEntitySpawner.js) - // replay-period = 3 + 20 = 23 (seconds, ditto) - // stats-period = UPDATES_PER_STATS_LOG * UPDATE_TIME / MSEC_PER_SEC = 10 seconds - // The log should show between each stats report: - // "start" lines ~= injector-limit * P(finish) = injector-limit * stats-period/replay-period = 17 ? - // total attempts at starting ("start" lines + "could not thread" lines) ~= N_SOUNDS = 1000 ? - // entities > N_SOUNDS * (1+ N_SILENT_ENTITIES_PER_SOUND) = 11000 + whatever was in the scene before running spawner - // sounds = N_SOUNDS = 1000 - // getEntityPropertiesPerUpdate ~= playing + failed-starts/UPDATES_PER_STATS_LOG + other-rechecks-each-update - // = injector-limit + (total attempts - "start" lines)/UPDATES_PER_STATS__LOG - // + (entities - playing - failed-starts/UPDATES_PER_STATS_LOG) * P(recheck-in-update) - // where failed-starts/UPDATES_PER_STATS_LOG = (1000-17)/100 = 10 - // = 40 + 10 + (11000 - 40 - 10)*UPDATE_TIME/RECHECK_TIME - // = 40 + 10 + 10950*0.01 = 159 (mostly proportional to enties/RECHECK_TIME) - // millisecondsPerUpdate ~= UPDATE_TIME = 100 (+ some timer machinery time) - // this assignment client activity monitor < 100% cpu - var stats = { - entities: 0, - sounds: 0, - playing: 0, - getEntityPropertiesPerUpdate: examinationCount / UPDATES_PER_STATS_LOG, - millisecondsPerUpdate: (now - lastStats) / UPDATES_PER_STATS_LOG - }; - nUpdates = UPDATES_PER_STATS_LOG; - lastStats = now; - examinationCount = 0; - Object.keys(entityCache).forEach(function (entityIdentifier) { - var datum = entityCache[entityIdentifier]; - stats.entities++; - if (datum.url) { - stats.sounds++; - if (datum.injector && datum.injector.playing) { - stats.playing++; - } - } - }); - print(JSON.stringify(stats)); - } -} - -// Update the set of which EntityData we know about. -// -function updateEntiesForAvatar(avatarIdentifier) { // Just one piece of update work. - // This does at most: - // one queryOctree request of the entity server, and - // one findEntities geometry query of our own octree, and - // a quick internEntityDatum of each of what may be a large number of entityIdentifiers. - // The idea is that this is a nice bounded piece of work that should not be done too frequently. - // However, it means that we won't learn about new entities until, on average (nAvatars * UPDATE_TIME) + query round trip. - var avatar = AvatarList.getAvatar(avatarIdentifier), avatarPosition = avatar && avatar.position; - if (!avatarPosition) { // No longer here. - return; - } - var timestamp = Date.now(); - if (isACScript) { - EntityViewer.setPosition(avatarPosition); - EntityViewer.queryOctree(); // Requests an update, but there's no telling when we'll actually see different results. - } - var entities = Entities.findEntities(avatarPosition, QUERY_RADIUS); - debug("found", entities.length, "entities near", avatar.name || "unknown", "at", avatarPosition); - entities.forEach(function (entityIdentifier) { - internEntityDatum(entityIdentifier, timestamp, avatarPosition, avatar); - }); -} - -// Slowly update the set of data we have to work with. -// -var workQueue = []; -function updateWorkQueueForAvatarsPresent() { // when nothing else to do, fill queue with individual avatar updates - workQueue = AvatarList.getAvatarIdentifiers().map(function (avatarIdentifier) { - return function () { - updateEntiesForAvatar(avatarIdentifier); - }; - }); -} -Script.setInterval(function () { - // There might be thousands of EntityData known to us, but only a few will require any work to update. - updateAllEntityData(); // i.e., this better be pretty fast. - // Each interval, we do no more than one updateEntitiesforAvatar. - if (!workQueue.length) { - workQueue = [updateWorkQueueForAvatarsPresent]; - } - workQueue.pop()(); // There's always one -}, UPDATE_TIME); diff --git a/tutorial/Changelog.md b/tutorial/Changelog.md deleted file mode 100644 index bd923b6841..0000000000 --- a/tutorial/Changelog.md +++ /dev/null @@ -1,3 +0,0 @@ - * home-tutorial-34 - * Update tutorial to only start if `HMD.active` - * Update builder's grid to use "Good - Sub-meshes" for collision shape type diff --git a/tutorial/entityData.js b/tutorial/entityData.js deleted file mode 100644 index b14185e78f..0000000000 --- a/tutorial/entityData.js +++ /dev/null @@ -1,131 +0,0 @@ -fireworkURLs = [ - "atp:/tutorial_models/bomb1.fbx", - "atp:/tutorial_models/bomb2.fbx", - "atp:/tutorial_models/bomb3.fbx", - "atp:/tutorial_models/bomb4.fbx", - "atp:/tutorial_models/bomb5.fbx", - "atp:/tutorial_models/bomb6.fbx", -]; - -fireworkBaseProps = { - "collisionsWillMove": 1, - velocity: { - x: 0, - y: -0.2, - z: 0 - }, - "dynamic": 1, - "gravity": { - "x": 0, - "y": -10, - "z": 0 - }, - "id": "{1c4061bc-b2e7-4435-bc47-3fcc39ae6624}", - "modelURL": "atp:/tutorial_models/bomb1.fbx", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.11612319946289062, - "y": 0, - "z": 0.21749019622802734 - }, - "queryAACube": { - "scale": 0.24519434571266174, - "x": -0.0064739733934402466, - "y": -0.12259717285633087, - "z": 0.094893023371696472 - }, - "rotation": { - "w": -0.083054840564727783, - "x": 0.93615627288818359, - "y": 0.34154272079467773, - "z": -0.0073701143264770508 - }, - "shapeType": "simple-hull", - "type": "Model", - "userData": "{\n \"hifiHomeKey\": {\n \"reset\": true\n }\n}" -} ; - - -birdFirework2 = { - "collisionsWillMove": 1, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - "dynamic": 1, - "gravity": { - "x": 0, - "y": -10, - "z": 0 - }, - "id": "{ba067084-8d0f-4eeb-a8a1-c6814527c1bb}", - "modelURL": "atp:/tutorial_models/bomb2.fbx", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0, - "y": 0.014694660902023315, - "z": 0 - }, - "queryAACube": { - "scale": 0.24011452496051788, - "x": -0.12005726248025894, - "y": -0.10536260157823563, - "z": -0.12005726248025894 - }, - "rotation": { - "w": 0.55410087108612061, - "x": 0.36000609397888184, - "y": -0.33641564846038818, - "z": -0.67092394828796387 - }, - "shapeType": "simple-compound", - "type": "Model", - "userData": "{\n \"hifiHomeKey\": {\n \"reset\": true\n }\n}" -}; - - -Step1BlockData = { - "clientOnly": 0, - "color": { - "blue": 0, - "green": 0, - "red": 255 - }, - "created": "2016-08-22T22:54:07Z", - "dimensions": { - "x": 0.20000000298023224, - "y": 0.20000000298023224, - "z": 0.20000000298023224 - }, - name: "tutorial/block", - "collisionsWillMove": 1, - velocity: { - x: 0, - y: -0.2, - z: 0 - }, - "dynamic": 1, - "gravity": { - "x": 0, - "y": -10, - "z": 0 - }, - "id": "{5c7223f8-3bc5-4cb4-913c-0e93f5994ca2}", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "queryAACube": { - "scale": 0.34641015529632568, - "x": -0.17320507764816284, - "y": -0.17320507764816284, - "z": -0.17320507764816284 - }, - "rotation": { - "w": 1, - "x": -0.0001373291015625, - "y": -7.62939453125e-05, - "z": -0.0003204345703125 - }, - "shape": "Cube", - "type": "Box", - "userData": JSON.stringify({ hifiHomeKey: { reset: true } }), -}; diff --git a/tutorial/firePit/fire.js b/tutorial/firePit/fire.js deleted file mode 100644 index 4565975351..0000000000 --- a/tutorial/firePit/fire.js +++ /dev/null @@ -1,181 +0,0 @@ -// Originally written by James Pollack, modified by Ryan Huffman for the tutorial -// -// this script turns an entity into an exploder -- anything that collides with it will be vaporized! - -(function() { - - function debug() { - var args = Array.prototype.slice.call(arguments); - args.unshift("fire.js | "); - print.apply(this, args); - } - - var _this = this; - - function Fire() { - _this = this; - } - - var RED = { - red: 255, - green: 0, - blue: 0 - }; - - var ORANGE = { - red: 255, - green: 165, - blue: 0 - }; - - var YELLOW = { - red: 255, - green: 255, - blue: 0 - }; - - var GREEN = { - red: 0, - green: 255, - blue: 0 - }; - - var BLUE = { - red: 0, - green: 0, - blue: 255 - }; - - var INDIGO = { - red: 128, - green: 0, - blue: 128 - }; - - var VIOLET = { - red: 75, - green: 0, - blue: 130 - }; - - var colors = [RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET]; - - var firePitSoundURL = Script.resolvePath("fire_burst.wav"); - debug("Firepit burst sound url is: ", firePitSoundURL); - - var explodeTextureURL = Script.resolvePath("explode.png"); - debug("Firepit explode texture url is: ", explodeTextureURL); - - Fire.prototype = { - preload: function(entityID) { - debug("Preload"); - this.entityID = entityID; - this.EXPLOSION_SOUND = SoundCache.getSound(firePitSoundURL); - }, - collisionWithEntity: function(myID, otherID, collisionInfo) { - debug("Collided with entity: ", myID, otherID); - var otherProps = Entities.getEntityProperties(otherID); - var data = null; - try { - data = JSON.parse(otherProps.userData); - } catch (err) { - debug('ERROR GETTING USERDATA!'); - } - if (data === null || "") { - debug("Data is null or empty", data); - return; - } else { - debug("Got data", data); - if (data.hasOwnProperty('hifiHomeKey')) { - debug("Has hifiHomeKey"); - if (data.hifiHomeKey.reset === true) { - debug("Reset is true"); - _this.playSoundAtCurrentPosition(); - _this.explodeWithColor(); - Entities.deleteEntity(otherID) - debug("Sending local message"); - Messages.sendLocalMessage('Entity-Exploded', JSON.stringify({ - entityID: otherID, - position: Entities.getEntityProperties(this.entityID).position - })); - debug("Done sending local message"); - } - } - } - }, - explodeWithColor: function() { - var myProps = Entities.getEntityProperties(this.entityID); - var color = colors[Math.floor(Math.random() * colors.length)]; - var explosionParticleProperties = { - "color": color, - "isEmitting": 1, - "maxParticles": 1000, - "lifespan": 0.25, - "emitRate": 1, - "emitSpeed": 0.1, - "speedSpread": 1, - "emitOrientation": Quat.getUp(myProps.rotation), - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "polarStart": 0, - "polarFinish": 0, - "azimuthStart": 0, - "azimuthFinish": 0, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.829, - "radiusSpread": 0, - "radiusStart": 0.361, - "radiusFinish": 0.294, - "colorSpread": { - "red": 0, - "green": 0, - "blue": 0 - }, - "colorStart": { - "red": 255, - "green": 255, - "blue": 255 - }, - "colorFinish": { - "red": 255, - "green": 255, - "blue": 255 - }, - "alpha": 1, - "alphaSpread": 0, - "alphaStart": -0.2, - "alphaFinish": 0.5, - "emitterShouldTrail": 0, - "textures": explodeTextureURL, - "type": "ParticleEffect", - lifetime: 1, - position: myProps.position - }; - - var explosion = Entities.addEntity(explosionParticleProperties); - }, - playSoundAtCurrentPosition: function() { - - var audioProperties = { - volume: 0.5, - position: Entities.getEntityProperties(this.entityID).position - }; - - Audio.playSound(this.EXPLOSION_SOUND, audioProperties); - }, - } - - return new Fire(); -}); diff --git a/tutorial/firePit/flicker.js b/tutorial/firePit/flicker.js deleted file mode 100644 index f4406286c4..0000000000 --- a/tutorial/firePit/flicker.js +++ /dev/null @@ -1,52 +0,0 @@ -// Originally written for the Home content set. Pulled into the tutorial by Ryan Huffman -(function() { - - var MINIMUM_LIGHT_INTENSITY = 50.0; - var MAXIMUM_LIGHT_INTENSITY = 200.0; - var LIGHT_FALLOFF_RADIUS = 0.1; - var LIGHT_INTENSITY_RANDOMNESS = 0.1; - - function randFloat(low, high) { - return low + Math.random() * (high - low); - } - - var _this; - - function FlickeringFlame() { - _this = this; - } - - var totalTime = 0; - var spacer = 2; - FlickeringFlame.prototype = { - preload: function(entityID) { - this.entityID = entityID; - Script.update.connect(this.update); - }, - update: function(deltaTime) { - - totalTime += deltaTime; - if (totalTime > spacer) { - var howManyAvatars = AvatarList.getAvatarIdentifiers().length; - var intensity = (MINIMUM_LIGHT_INTENSITY + (MAXIMUM_LIGHT_INTENSITY + (Math.sin(totalTime) * MAXIMUM_LIGHT_INTENSITY))); - intensity += randFloat(-LIGHT_INTENSITY_RANDOMNESS, LIGHT_INTENSITY_RANDOMNESS); - - Entities.editEntity(_this.entityID, { - intensity: intensity - }); - - spacer = Math.random(0, 100) * (2 / howManyAvatars); - totalTime = 0; - } else { - //just keep counting - } - }, - unload: function() { - Script.update.disconnect(this.update) - } - } - - return new FlickeringFlame - - -}); diff --git a/tutorial/fuse.js b/tutorial/fuse.js deleted file mode 100644 index 59306f4113..0000000000 --- a/tutorial/fuse.js +++ /dev/null @@ -1,119 +0,0 @@ -// -// fuse.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 -// - -(function() { - Script.include('utils.js'); - - var DEBUG = true; - function debug() { - if (DEBUG) { - var args = Array.prototype.slice.call(arguments); - args.unshift("fuse.js | "); - print.apply(this, args); - } - } - - var active = false; - - var fuseSound = SoundCache.getSound("atp:/tutorial_sounds/fuse.wav"); - function getChildProperties(entityID, propertyNames) { - var childEntityIDs = Entities.getChildrenIDs(entityID); - var results = {} - for (var i = 0; i < childEntityIDs.length; ++i) { - var childEntityID = childEntityIDs[i]; - var properties = Entities.getEntityProperties(childEntityID, propertyNames); - results[childEntityID] = properties; - } - return results; - } - var Fuse = function() { - }; - Fuse.prototype = { - light: function() { - debug("Received light()", this.entityID); - - var visible = Entities.getEntityProperties(this.entityID, ['visible']).visible; - if (!visible) { - debug("Fuse is not visible, returning"); - return; - } - - if (active) { - debug("Fuse is active, returning"); - return; - } - active = true; - - Entities.editEntity(this.entityID, { - animation: { - currentFrame: 1, - lastFrame: 150, - running: 1, - url: "atp:/tutorial_models/fuse.fbx", - loop: 0 - }, - }); - var injector = Audio.playSound(fuseSound, { - position: Entities.getEntityProperties(this.entityID, 'position').position, - volume: 0.7, - loop: true - }); - - var childrenProps = getChildProperties(this.entityID, ['type']); - for (var childEntityID in childrenProps) { - debug("Updating: ", childEntityID); - var props = childrenProps[childEntityID]; - if (props.type == "ParticleEffect") { - Entities.editEntity(childEntityID, { - emitRate: 140, - }); - } else if (props.type == "Light") { - Entities.editEntity(childEntityID, { - visible: true, - }); - } - } - - var self = this; - Script.setTimeout(function() { - debug("Setting off fireworks"); - var spinnerID = "{dd13fcd5-616f-4749-ab28-2e1e8bc512e9}"; - Entities.callEntityMethod(spinnerID, "onLit"); - injector.stop(); - - var childrenProps = getChildProperties(self.entityID, ['type']); - for (var childEntityID in childrenProps) { - debug("Updating: ", childEntityID); - var props = childrenProps[childEntityID]; - if (props.type == "ParticleEffect") { - Entities.editEntity(childEntityID, { - emitRate: 0, - }); - } else if (props.type == "Light") { - Entities.editEntity(childEntityID, { - visible: false, - }); - } - } - - }, 4900); - - Script.setTimeout(function() { - debug("Setting fuse to inactive"); - active = false; - }, 14000); - }, - preload: function(entityID) { - debug("Preload"); - this.entityID = entityID; - }, - }; - return new Fuse(); -}); diff --git a/tutorial/fuseCollider.js b/tutorial/fuseCollider.js deleted file mode 100644 index 953fcd316d..0000000000 --- a/tutorial/fuseCollider.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - Script.include('utils.js'); - - var Fuse = function() { - }; - Fuse.prototype = { - onLit: function() { - print("fuseCollider.js | Lit", this.entityID); - var fuseID = "{c8944a13-9acb-4d77-b1ee-851845e98357}" - Entities.callEntityMethod(fuseID, "light"); - }, - preload: function(entityID) { - print("fuseCollider.js | preload"); - this.entityID = entityID; - }, - }; - return new Fuse(); -}); diff --git a/tutorial/lighter/butaneLighter.js b/tutorial/lighter/butaneLighter.js deleted file mode 100644 index f37769f867..0000000000 --- a/tutorial/lighter/butaneLighter.js +++ /dev/null @@ -1,192 +0,0 @@ -// -// Created by Thijs Wenker on September 14, 2016. -// 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 -// - -(function() { - var _this; - - function getResourceURL(file) { - return 'atp:/' + file; - }; - - const LIGHTER_ON_SOUND_URL = getResourceURL('tutorial_sounds/lighter_on.wav'); - const BUTANE_SOUND_URL = getResourceURL('tutorial_sounds/butane.wav'); - - // TODO: fix this in the client, changing the sound volume while a sound is playing doesn't seem to work right now - const DYNAMIC_SOUND_VOLUME = false; - const BUTANE_MIN_SOUND_VOLUME = 0.05; - - const FLAME_LENGTH = 0.05; - - const BUTANE_SOUND_SETTINGS = { - volume: 0.4, - loop: true, - playbackGap: 1000, - playbackGapRange: 1000 - }; - - const LIGHTER_ON_SOUND_SETTINGS = { - volume: 0.5, - loop: false - }; - - function RemoteLogSender(channel, identifier) { - this.channel = channel; - this.identifier = identifier; - } - - RemoteLogSender.prototype = { - channel: null, - identifier: null, - debug: function(message) { - Messages.sendLocalMessage(this.channel, JSON.stringify({ - message: '[DEBUG ' + this.identifier + '] ' + message - })); - } - }; - - var remoteLogSender = null; - - function debugPrint(message) { - if (remoteLogSender !== null) { - remoteLogSender.debug(message); - } - } - - ButaneLighter = function() { - _this = this; - _this.triggerValue = 0.0; // be sure to set this value in the constructor, otherwise it will be a shared value - _this.isFiring = false; - } - - ButaneLighter.prototype = { - entityID: null, - lighterOnSound: null, - butaneSound: null, - lighterOnSoundInjector: null, - butaneSoundInjector: null, - butaneSoundInjectorOptions: null, - lighterParticleEntity: null, - buttonBeingPressed: null, - triggerValue: null, - isFiring: null, - getLighterParticleEntity: function() { - var result = null; - Entities.getChildrenIDs(_this.entityID).forEach(function(entity) { - var name = Entities.getEntityProperties(entity, ['name']).name; - if (name === 'lighter_particle') { - result = entity; - } - }); - return result; - }, - preload: function(entityID) { - _this.entityID = entityID; - _this.lighterOnSound = SoundCache.getSound(LIGHTER_ON_SOUND_URL); - _this.butaneSound = SoundCache.getSound(BUTANE_SOUND_URL); - var properties = Entities.getEntityProperties(_this.entityID, ['userData']); - try { - var userData = JSON.parse(properties.userData); - if (userData['debug'] !== undefined && userData['debug']['sessionUUID'] === MyAvatar.sessionUUID && - userData['debug']['channel'] !== undefined) - { - remoteLogSender = new RemoteLogSender(userData['debug']['channel'], _this.entityID); - remoteLogSender.debug('Debugger initialized'); - } - } catch (e) { - //failed to detect if we have a debugger - } - debugPrint(_this.getLighterParticleEntity()); - }, - startEquip: function(entityID, args) { - _this.lighterParticleEntity = _this.getLighterParticleEntity(); - }, - continueEquip: function(entityID, args) { - _this.triggerValue = Controller.getValue(args[0] === 'left' ? Controller.Standard.LT : Controller.Standard.RT); - if (_this.triggerValue > 0.2) { - if (!_this.isFiring) { - _this.startFiring(); - } - _this.tryToIgnite(); - _this.updateButaneSound() - return; - } - _this.stopFiring(); - }, - startFiring: function() { - if (_this.isFiring) { - return; - } - _this.isFiring = true; - if (_this.lighterOnSound.downloaded) { - // We don't want to override the default volume setting, so lets clone the default SETTINGS object - var lighterOnOptions = JSON.parse(JSON.stringify(LIGHTER_ON_SOUND_SETTINGS)); - lighterOnOptions['position'] = Entities.getEntityProperties(_this.entityID, ['position']).position; - _this.lighterOnSoundInjector = Audio.playSound(_this.lighterOnSound, lighterOnOptions); - } - if (_this.butaneSound.downloaded) { - _this.butaneSoundInjectorOptions = JSON.parse(JSON.stringify(BUTANE_SOUND_SETTINGS)); - _this.butaneSoundInjectorOptions['position'] = Entities.getEntityProperties(_this.lighterParticleEntity, ['position']).position; - if (DYNAMIC_SOUND_VOLUME) { - _this.butaneSoundInjectorOptions['volume'] = BUTANE_MIN_SOUND_VOLUME; - } - _this.butaneSoundInjector = Audio.playSound(_this.butaneSound, _this.butaneSoundInjectorOptions); - } - Entities.editEntity(_this.lighterParticleEntity, {isEmitting: _this.isFiring}); - - }, - stopFiring: function() { - if (!_this.isFiring) { - return; - } - _this.isFiring = false; - Entities.editEntity(_this.lighterParticleEntity, {isEmitting: _this.isFiring}); - _this.stopButaneSound(); - }, - tryToIgnite: function() { - var flameProperties = Entities.getEntityProperties(_this.lighterParticleEntity, ['position', 'rotation']); - var pickRay = { - origin: flameProperties.position, - direction: Quat.inverse(Quat.getFront(flameProperties.rotation)) - } - var intersection = Entities.findRayIntersection(pickRay, true, [], [_this.entityID, _this.lighterParticleEntity]); - if (intersection.intersects && intersection.distance <= FLAME_LENGTH) { - var properties = Entities.getEntityProperties(intersection.entityID, 'script'); - if (properties.script !== '') { - Entities.callEntityMethod(intersection.entityID, 'onLit', [_this.triggerValue]); - debugPrint('Light it up! found: ' + intersection.entityID); - } - } - }, - releaseEquip: function(entityID, args) { - _this.stopFiring(); - // reset trigger value; - _this.triggerValue = 0.0; - }, - updateButaneSound: function() { - if (_this.butaneSoundInjector !== null && _this.butaneSoundInjector.isPlaying()) { - _this.butaneSoundInjectorOptions = _this.butaneSoundInjector.options; - _this.butaneSoundInjectorOptions['position'] = Entities.getEntityProperties(_this.entityID, ['position']).position; - if (DYNAMIC_SOUND_VOLUME) { - _this.butaneSoundInjectorOptions['volume'] = ((BUTANE_SOUND_SETTINGS.volume - BUTANE_MIN_SOUND_VOLUME) * - _this.triggerValue) + BUTANE_MIN_SOUND_VOLUME; - } - _this.butaneSoundInjector.options = _this.butaneSoundInjectorOptions; - } - }, - stopButaneSound: function() { - if (_this.butaneSoundInjector !== null && _this.butaneSoundInjector.isPlaying()) { - _this.butaneSoundInjector.stop(); - } - _this.butaneSoundInjector = null; - }, - unload: function() { - _this.stopButaneSound(); - }, - }; - return new ButaneLighter(); -}) diff --git a/tutorial/lighter/createButaneLighter.js b/tutorial/lighter/createButaneLighter.js deleted file mode 100644 index 1a6b94d0f6..0000000000 --- a/tutorial/lighter/createButaneLighter.js +++ /dev/null @@ -1,220 +0,0 @@ -// -// Created by Thijs Wenker on September 14, 2016. -// 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 -// - -const TEST_MODE = false; -const SCRIPT_URL = 'atp:/tutorial/lighter/butaneLighter.js'; - -//Creates an entity and returns a mixed object of the creation properties and the assigned entityID -var createEntity = function(entityProperties, parent) { - if (parent.rotation !== undefined) { - if (entityProperties.rotation !== undefined) { - entityProperties.rotation = Quat.multiply(parent.rotation, entityProperties.rotation); - } else { - entityProperties.rotation = parent.rotation; - } - } - if (parent.position !== undefined) { - var localPosition = (parent.rotation !== undefined) ? Vec3.multiplyQbyV(parent.rotation, entityProperties.position) : entityProperties.position; - entityProperties.position = Vec3.sum(localPosition, parent.position) - } - if (parent.id !== undefined) { - entityProperties.parentID = parent.id; - } - entityProperties.id = Entities.addEntity(entityProperties); - return entityProperties; -}; - -createButaneLighter = function(transform) { - var entityProperties = { - collisionsWillMove: true, - dimensions: { - x: 0.025599999353289604, - y: 0.057399999350309372, - z: 0.37419998645782471 - }, - dynamic: true, - gravity: { - x: 0, - y: -9.8, - z: 0 - }, - velocity: { - x: 0, - y: -0.01, - z: 0 - }, - modelURL: 'atp:/tutorial_models/lighterIceCreamSandwich.fbx', - name: 'BrutaneLighter', - shapeType: 'simple-compound', - type: 'Model', - userData: JSON.stringify({ - "tag": "equip-temporary", - "grabbableKey": { - "invertSolidWhileHeld": true - }, - "wearable": { - "joints": { - "RightHand": [ - { - "x": 0.049671292304992676, - "y": 0.09825992584228516, - "z": 0.03760027885437012 - }, - { - "x": 0.6562752723693848, - "y": 0.27598991990089417, - "z": 0.6638742685317993, - "w": -0.22890058159828186 - } - ], - "LeftHand": [ - { - "x": -0.028073370456695557, - "y": 0.09609812498092651, - "z": 0.039550721645355225 - }, - { - "x": -0.6697965264320374, - "y": 0.22050897777080536, - "z": 0.6544681191444397, - "w": 0.27283111214637756 - } - ] - } - } - }), - script: SCRIPT_URL - }; - return createEntity(entityProperties, transform); -} - -function createFireParticle(butaneLighter) { - var entityProperties = { - userData: JSON.stringify({ tag: "equip-temporary" }), - accelerationSpread: { - x: 0.1, - y: 0, - z: 0.1 - }, - alpha: 0.039999999105930328, - alphaFinish: 0.039999999105930328, - alphaStart: 0.039999999105930328, - azimuthFinish: 0.039999999105930328, - azimuthStart: 0, - dimensions: { - x: 0.49194091558456421, - y: 0.49194091558456421, - z: 0.49194091558456421 - }, - emitAcceleration: { - x: 0, - y: 0, - z: 0 - }, - emitOrientation: { - w: 1, - x: -1.52587890625e-05, - y: -1.52587890625e-05, - z: -1.52587890625e-05 - }, - emitRate: 770, - emitSpeed: 0.014000000432133675, - isEmitting: false, - lifespan: 0.37000000476837158, - maxParticles: 820, - name: 'lighter_particle', - particleRadius: 0.0027000000700354576, - position: { - x: -0.00044769048690795898, - y: 0.016354814171791077, - z: 0.19217036664485931 - }, - radiusFinish: 0.0027000000700354576, - radiusSpread: 3, - radiusStart: 0.0027000000700354576, - rotation: { - w: 1, - x: -0.0001678466796875, - y: -1.52587890625e-05, - z: -1.52587890625e-05 - }, - speedSpread: 0.56999999284744263, - textures: 'atp:/textures/fire3.png', - type: 'ParticleEffect', - - - "color": { - "red": 255, - "green": 255, - "blue": 255 - }, - "isEmitting": 0, - "maxParticles": 820, - "lifespan": 0.28, - "emitRate": 1100, - "emitSpeed": 0.007, - "speedSpread": 0.5699999928474426, - "emitOrientation": { - "x": -0.0000152587890625, - "y": -0.0000152587890625, - "z": -0.0000152587890625, - "w": 1 - }, - "emitDimensions": { - "x": 0, - "y": 0, - "z": 0 - }, - "polarStart": 0, - "polarFinish": 0, - "azimuthStart": 0, - "azimuthFinish": 0.03999999910593033, - "emitAcceleration": { - "x": 0, - "y": 0, - "z": 0 - }, - "accelerationSpread": { - "x": 0, - "y": 0, - "z": 0 - }, - "particleRadius": 0.0037, - "radiusSpread": 3, - "radiusStart": 0.008, - "radiusFinish": 0.0004, - "colorSpread": { - "red": 0, - "green": 0, - "blue": 0 - }, - "colorStart": { - "red": 255, - "green": 255, - "blue": 255 - }, - "colorFinish": { - "red": 255, - "green": 255, - "blue": 255 - }, - "alpha": 0.03999999910593033, - "alphaSpread": 0, - "alphaStart": 0.141, - "alphaFinish": 0.02, - "emitterShouldTrail": 0, - "textures": "atp:/textures/fire3.png" - }; - return createEntity(entityProperties, butaneLighter); -} - -doCreateButaneLighter = function(transform) { - var butaneLighter = createButaneLighter(transform); - createFireParticle(butaneLighter); - return butaneLighter; -} diff --git a/tutorial/ownershipToken.js b/tutorial/ownershipToken.js deleted file mode 100644 index 4a970af66d..0000000000 --- a/tutorial/ownershipToken.js +++ /dev/null @@ -1,188 +0,0 @@ -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; - }; -} - -function getOption(options, key, defaultValue) { - if (options.hasOwnProperty(key)) { - return options[key]; - } - return defaultValue; -} - -var TOKEN_NAME_PREFIX = "ownership_token-"; - -function getOwnershipTokenID(parentEntityID) { - var childEntityIDs = Entities.getChildrenIDs(parentEntityID); - var ownerID = null; - var ownerName = ''; - for (var i = 0; i < childEntityIDs.length; ++i) { - var childID = childEntityIDs[i]; - var properties = Entities.getEntityProperties(childID, ['name', 'userData', 'lifetime', 'age']); - var childName = properties.name; - if (childName.indexOf(TOKEN_NAME_PREFIX) == 0) { - if (ownerID === null || childName < ownerName) { - ownerID = childID; - ownerName = childName; - } - } - } - return ownerID; -} - -function createOwnershipToken(name, parentEntityID) { - return Entities.addEntity({ - type: "Box", - name: TOKEN_NAME_PREFIX + name, - visible: false, - parentID: parentEntityID, - locationPosition: { x: 0, y: 0, z: 0 }, - dimensions: { x: 100, y: 100, z: 100 }, - collisionless: true, - lifetime: 5 - }); -} - -var DEBUG = true; -function debug() { - if (DEBUG) { - var args = Array.prototype.slice.call(arguments); - print.apply(this, args); - } -} - -var TOKEN_STATE_DESTROYED = -1; -var TOKEN_STATE_UNOWNED = 0; -var TOKEN_STATE_REQUESTING_OWNERSHIP = 1; -var TOKEN_STATE_OWNED = 2; - -OwnershipToken = function(name, parentEntityID, options) { - this.name = MyAvatar.sessionUUID + "-" + Math.floor(Math.random() * 10000000); - this.parentEntityID = parentEntityID; - - // How often to check whether the token is available if we don't currently own it - this.checkEverySeconds = getOption(options, 'checkEverySeconds', 1000); - this.updateTokenLifetimeEvery = getOption(options, 'updateTokenLifetimeEvery', 2000); - - this.onGainedOwnership = getOption(options, 'onGainedOwnership', function() { }); - this.onLostOwnership = getOption(options, 'onLostOwnership', function() { }); - - this.ownershipTokenID = null; - this.setState(TOKEN_STATE_UNOWNED); -}; - -OwnershipToken.prototype = { - destroy: function() { - debug(this.name, "Destroying token"); - this.setState(TOKEN_STATE_DESTROYED); - }, - - setState: function(newState) { - if (this.state == newState) { - debug(this.name, "Warning: Trying to set state to the current state"); - return; - } - - if (this.updateLifetimeID) { - debug(this.name, "Clearing update lifetime interval"); - Script.clearInterval(this.updateLifetimeID); - this.updateLifetimeID = null; - } - - if (this.checkOwnershipAvailableID) { - Script.clearInterval(this.checkOwnershipAvailableID); - this.checkOwnershipAvailableID = null; - } - - if (this.state == TOKEN_STATE_OWNED) { - this.onLostOwnership(this); - } - - if (newState == TOKEN_STATE_UNOWNED) { - this.checkOwnershipAvailableID = Script.setInterval( - this.tryRequestingOwnership.bind(this), this.checkEverySeconds); - - } else if (newState == TOKEN_STATE_REQUESTING_OWNERSHIP) { - - } else if (newState == TOKEN_STATE_OWNED) { - this.onGainedOwnership(this); - this.updateLifetimeID = Script.setInterval( - this.updateTokenLifetime.bind(this), this.updateTokenLifetimeEvery); - } else if (newState == TOKEN_STATE_DESTROYED) { - Entities.deleteEntity(this.ownershipTokenID); - } - - debug(this.name, "Info: Switching to state:", newState); - this.state = newState; - }, - updateTokenLifetime: function() { - if (this.state != TOKEN_STATE_OWNED) { - debug(this.name, "Error: Trying to update token while it is unowned"); - return; - } - - debug(this.name, "Updating entity lifetime"); - var age = Entities.getEntityProperties(this.ownershipTokenID, 'age').age; - Entities.editEntity(this.ownershipTokenID, { - lifetime: age + 5 - }); - }, - tryRequestingOwnership: function() { - if (this.state == TOKEN_STATE_REQUESTING_OWNERSHIP || this.state == TOKEN_STATE_OWNED) { - debug(this.name, "We already have or are requesting ownership"); - return; - } - - var ownerID = getOwnershipTokenID(this.parentEntityID); - if (ownerID !== null) { - // Already owned, return - debug(this.name, "Token already owned by another client, returning. Owner: " + owenerID + ", Us: " + this.name); - return; - } - - this.ownershipTokenID = createOwnershipToken(this.name, this.parentEntityID); - this.setState(TOKEN_STATE_REQUESTING_OWNERSHIP); - - function checkOwnershipRequest() { - var ownerID = getOwnershipTokenID(this.parentEntityID); - if (ownerID == this.ownershipTokenID) { - debug(this.name, "Info: Obtained ownership"); - this.setState(TOKEN_STATE_OWNED); - } else { - if (ownerID === null) { - debug(this.name, "Warning: Checked ownership request and no tokens existed"); - } - debug(this.name, "Info: Lost ownership request") - this.ownershipTokenID = null; - this.setState(TOKEN_STATE_UNOWNED); - } - } - - Script.setTimeout(checkOwnershipRequest.bind(this), 2000); - }, -}; - -debug("Returning from ownershipToken"); diff --git a/tutorial/spinner.js b/tutorial/spinner.js deleted file mode 100644 index 2edbb43700..0000000000 --- a/tutorial/spinner.js +++ /dev/null @@ -1,84 +0,0 @@ -// -// spinner.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 -// - -(function() { - var DEBUG = true; - function debug() { - if (DEBUG) { - print.apply(this, arguments); - } - } - - var spinnerSound = SoundCache.getSound("atp:/tutorial_sounds/Pinwheel.L.wav"); - var Spinner = function() { - }; - function getChildProperties(entityID, propertyNames) { - var childEntityIDs = Entities.getChildrenIDs(entityID); - var results = {} - for (var i = 0; i < childEntityIDs.length; ++i) { - var childEntityID = childEntityIDs[i]; - var properties = Entities.getEntityProperties(childEntityID, propertyNames); - results[childEntityID] = properties; - } - return results; - } - Spinner.prototype = { - onLit: function() { - debug("spinner.js | Spinner lit"); - Entities.editEntity(this.entityID, { - "angularDamping": 0.1, - "angularVelocity": { - "x": 20.471975326538086, - "y": 0, - "z": 0 - }, - }); - var injector = Audio.playSound(spinnerSound, { - position: Entities.getEntityProperties(this.entityID, 'position').position, - volume: 1.0, - loop: false - }); - - var childrenProps = getChildProperties(this.entityID, ['type']); - for (var childEntityID in childrenProps) { - var props = childrenProps[childEntityID]; - if (props.type == "ParticleEffect") { - debug("spinner.js | Modifying: ", childEntityID); - Entities.editEntity(childEntityID, { - emitRate: 35, - }); - } - } - Messages.sendLocalMessage("Tutorial-Spinner", "wasLit"); - - var self = this; - Script.setTimeout(function() { - debug("spinner.js | Finishing spinner"); - injector.stop(); - - var childrenProps = getChildProperties(self.entityID, ['type']); - for (var childEntityID in childrenProps) { - var props = childrenProps[childEntityID]; - if (props.type == "ParticleEffect") { - debug("spinner.js | Modifying: ", childEntityID); - Entities.editEntity(childEntityID, { - emitRate: 0, - }); - } - } - }, 4900); - }, - preload: function(entityID) { - debug("spinner.js | Preload"); - this.entityID = entityID; - }, - }; - return new Spinner(); -}); diff --git a/tutorial/success.wav b/tutorial/success.wav deleted file mode 100644 index 597e2b91b66c071fb260675d27f08c235af0a01b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39758 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 Q0WX*d$UJ5) 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 is 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)); -} - -/** - * Spawn entities and return the newly created entity's ids. - * @param {object[]} entityPropertiesList A list of properties of the entities - * to spawn. - */ -function spawn(entityPropertiesList, transform, modifyFn) { - 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]; - 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); - } - 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 showEntitiesWithTags(tags) { - for (var i = 0; i < tags.length; ++i) { - showEntitiesWithTag(tags[i]); - } -} - -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); - } - } else { - debug("ERROR | No entities for tag: ", tag); - } - - return; - // Dynamic method, suppressed for now - //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 hideEntitiesWithTags(tags) { - for (var i = 0; i < tags.length; ++i) { - hideEntitiesWithTag(tags[i]); - } -} - -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); - } - } - - return; - // Dynamic method, suppressed for now - //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 - }); -} - -/** - * This disables everything, including: - * - * - The door to leave the tutorial - * - Overlays - * - Hand controlelrs - * - Teleportation - * - Advanced movement - * - Equip and far grab - * - Away mode - */ -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, - myAvatarScalingEnabled: false, - objectScalingEnabled: false, - })); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('trigger', 'blank'); - setControllerPartLayer('joystick', 'blank'); - setControllerPartLayer('grip', 'blank'); - setControllerPartLayer('button_a', 'blank'); - setControllerPartLayer('button_b', 'blank'); - setControllerPartLayer('tips', 'blank'); - - hideEntitiesWithTag('finish'); - - setAwayEnabled(false); -} - -/** - * This reenables everything that disableEverything() disables. This can be - * used when leaving the tutorial to ensure that nothing is left disabled. - */ -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, - myAvatarScalingEnabled: true, - objectScalingEnabled: true, - })); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('trigger', 'blank'); - setControllerPartLayer('joystick', 'blank'); - setControllerPartLayer('grip', 'blank'); - setControllerPartLayer('button_a', 'blank'); - setControllerPartLayer('button_b', 'blank'); - setControllerPartLayer('tips', 'blank'); - MyAvatar.shouldRenderLocally = true; - setAwayEnabled(true); -} - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: DISABLE CONTROLLERS // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepStart = function() { - this.name = 'start'; -}; -stepStart.prototype = { - start: function(onFinish) { - disableEverything(); - - HMD.requestShowHandControllers(); - - onFinish(); - }, - cleanup: function() { - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: ENABLE CONTROLLERS // -// // -/////////////////////////////////////////////////////////////////////////////// - -var stepEnableControllers = function() { - this.shouldLog = false; -}; -stepEnableControllers.prototype = { - start: function(onFinish) { - reenableEverything(); - HMD.requestHideHandControllers(); - onFinish(); - }, - cleanup: function() { - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Orient and raise hands above head // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepOrient = function(tutorialManager) { - this.name = 'orient'; - this.tags = ["orient", "orient-" + tutorialManager.controllerName]; -} -stepOrient.prototype = { - start: function(onFinish) { - this.active = true; - - var tag = this.tag; - - // Spawn content set - //editEntitiesWithTag(this.tag, { visible: true }); - showEntitiesWithTags(this.tags); - - 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 }); - hideEntitiesWithTags(this.tags); - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Near Grab // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepNearGrab = function(tutorialManager) { - this.name = 'nearGrab'; - this.tags = ["bothGrab", "nearGrab", "nearGrab-" + tutorialManager.controllerName]; - this.tempTag = "nearGrab-temporary"; - this.birdIDs = []; - - this.controllerName = tutorialManager.controllerName; - - Messages.subscribe("Entity-Exploded"); - Messages.messageReceived.connect(this.onMessage.bind(this)); -} -stepNearGrab.prototype = { - start: function(onFinish) { - this.finished = false; - this.onFinish = onFinish; - - if (this.controllerName === CONTROLLER_TOUCH) { - setControllerPartLayer('tips', 'both_triggers'); - setControllerPartLayer('trigger', 'highlight'); - setControllerPartLayer('grip', 'highlight'); - } else { - setControllerPartLayer('tips', 'trigger'); - setControllerPartLayer('trigger', 'highlight'); - } - - // Show content set - showEntitiesWithTags(this.tags); - - var boxSpawnPosition = getEntityWithName(NEAR_BOX_SPAWN_NAME).position; - function createBlock(fireworkNumber) { - fireworkBaseProps.position = boxSpawnPosition; - fireworkBaseProps.modelURL = fireworkURLs[fireworkNumber % fireworkURLs.length]; - debug("Creating firework with url: ", fireworkBaseProps.modelURL); - return spawnWithTag([fireworkBaseProps], null, this.tempTag)[0]; - } - - this.birdIDs = []; - this.birdIDs.push(createBlock.bind(this)(0)); - this.birdIDs.push(createBlock.bind(this)(1)); - this.birdIDs.push(createBlock.bind(this)(2)); - this.positionWatcher = new PositionWatcher(this.birdIDs, boxSpawnPosition, -0.4, 4); - }, - onMessage: function(channel, message, seneder) { - if (this.finished) { - return; - } - if (channel == "Entity-Exploded") { - debug("NearGrab | Got entity-exploded message: ", message); - - var data = parseJSON(message); - if (this.birdIDs.indexOf(data.entityID) >= 0) { - debug("NearGrab | It's one of the firecrackers"); - playFirecrackerSound(data.position); - playSuccessSound(); - this.finished = true; - this.onFinish(); - } - } - }, - cleanup: function() { - debug("NearGrab | Cleanup"); - this.finished = true; - setControllerPartLayer('tips', 'blank'); - setControllerPartLayer('trigger', 'normal'); - setControllerPartLayer('grip', 'normal'); - hideEntitiesWithTags(this.tags); - deleteEntitiesWithTag(this.tempTag); - if (this.positionWatcher) { - this.positionWatcher.destroy(); - this.positionWatcher = null; - } - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Far Grab // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepFarGrab = function() { - this.name = 'farGrab'; - this.tag = "farGrab"; - this.tempTag = "farGrab-temporary"; - this.finished = true; - this.birdIDs = []; - - Messages.subscribe("Entity-Exploded"); - Messages.messageReceived.connect(this.onMessage.bind(this)); -} -stepFarGrab.prototype = { - start: function(onFinish) { - this.finished = false; - this.onFinish = onFinish; - - showEntitiesWithTag('bothGrab', { visible: true }); - - setControllerPartLayer('tips', 'trigger'); - setControllerPartLayer('trigger', 'highlight'); - Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ - farGrabEnabled: true, - })); - var tag = this.tag; - - // Spawn content set - showEntitiesWithTag(this.tag); - - var boxSpawnPosition = getEntityWithName(FAR_BOX_SPAWN_NAME).position; - function createBlock(fireworkNumber) { - fireworkBaseProps.position = boxSpawnPosition; - fireworkBaseProps.modelURL = fireworkURLs[fireworkNumber % fireworkURLs.length]; - debug("Creating firework with url: ", fireworkBaseProps.modelURL); - return spawnWithTag([fireworkBaseProps], null, this.tempTag)[0]; - } - - this.birdIDs = []; - this.birdIDs.push(createBlock.bind(this)(3)); - this.birdIDs.push(createBlock.bind(this)(4)); - this.birdIDs.push(createBlock.bind(this)(5)); - this.positionWatcher = new PositionWatcher(this.birdIDs, boxSpawnPosition, -0.4, 4); - }, - onMessage: function(channel, message, seneder) { - if (this.finished) { - return; - } - if (channel == "Entity-Exploded") { - debug("FarGrab | Got entity-exploded message: ", message); - var data = parseJSON(message); - if (this.birdIDs.indexOf(data.entityID) >= 0) { - debug("FarGrab | It's one of the firecrackers"); - playFirecrackerSound(data.position); - playSuccessSound(); - this.finished = true; - this.onFinish(); - } - } - }, - cleanup: function() { - debug("FarGrab | Cleanup"); - this.finished = true; - setControllerPartLayer('tips', 'blank'); - setControllerPartLayer('trigger', 'normal'); - hideEntitiesWithTag(this.tag, { visible: false}); - deleteEntitiesWithTag(this.tempTag); - if (this.positionWatcher) { - this.positionWatcher.destroy(); - this.positionWatcher = null; - } - } -}; - -function PositionWatcher(entityIDs, originalPosition, minY, maxDistance) { - debug("Creating position watcher"); - this.watcherIntervalID = Script.setInterval(function() { - for (var i = 0; i < entityIDs.length; ++i) { - var entityID = entityIDs[i]; - var props = Entities.getEntityProperties(entityID, ['position']); - if (props.position.y < minY || Vec3.distance(originalPosition, props.position) > maxDistance) { - Entities.editEntity(entityID, { - position: originalPosition, - velocity: { x: 0, y: -0.01, z: 0 }, - angularVelocity: { x: 0, y: 0, z: 0 } - }); - } - } - }, 1000); -} - -PositionWatcher.prototype = { - destroy: function() { - debug("Destroying position watcher"); - Script.clearInterval(this.watcherIntervalID); - } -}; - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Equip // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepEquip = function(tutorialManager) { - const controllerName = tutorialManager.controllerName; - this.controllerName = controllerName; - - this.name = 'equip'; - - this.tags = ["equip", "equip-" + controllerName]; - this.tagsPart1 = ["equip-part1", "equip-part1-" + controllerName]; - this.tagsPart2 = ["equip-part2", "equip-part2-" + controllerName]; - this.tempTag = "equip-temporary"; - - this.PART1 = 0; - this.PART2 = 1; - this.PART3 = 2; - this.COMPLETE = 3; - - Messages.subscribe('Tutorial-Spinner'); - Messages.messageReceived.connect(this.onMessage.bind(this)); -} -stepEquip.prototype = { - start: function(onFinish) { - if (this.controllerName === CONTROLLER_TOUCH) { - setControllerPartLayer('tips', 'grip'); - setControllerPartLayer('grip', 'highlight'); - } else { - setControllerPartLayer('tips', 'trigger'); - setControllerPartLayer('trigger', 'highlight'); - } - - Messages.sendLocalMessage('Hifi-Grab-Disable', JSON.stringify({ - holdEnabled: true, - })); - - var tag = this.tag; - - // Spawn content set - showEntitiesWithTags(this.tags); - showEntitiesWithTags(this.tagsPart1); - - this.currentPart = this.PART1; - - function createLighter() { - var transform = {}; - - var boxSpawnProps = getEntityWithName(GUN_SPAWN_NAME); - transform.position = boxSpawnProps.position; - transform.rotation = boxSpawnProps.rotation; - transform.velocity = { x: 0, y: -0.01, z: 0 }; - transform.angularVelocity = { x: 0, y: 0, z: 0 }; - this.spawnTransform = transform; - return doCreateButaneLighter(transform).id; - } - - - this.lighterID = createLighter.bind(this)(); - this.startWatchingLighter(); - debug("Created lighter", this.lighterID); - this.onFinish = onFinish; - }, - startWatchingLighter: function() { - if (!this.watcherIntervalID) { - debug("Starting to watch lighter position"); - this.watcherIntervalID = Script.setInterval(function() { - debug("Checking lighter position"); - var props = Entities.getEntityProperties(this.lighterID, ['position']); - if (props.position.y < -0.4 - || Vec3.distance(this.spawnTransform.position, props.position) > 4) { - debug("Moving lighter back to table"); - Entities.editEntity(this.lighterID, this.spawnTransform); - } - }.bind(this), 1000); - } - }, - stopWatchingGun: function() { - if (this.watcherIntervalID) { - debug("Stopping watch of lighter position"); - Script.clearInterval(this.watcherIntervalID); - this.watcherIntervalID = null; - } - }, - onMessage: function(channel, message, sender) { - if (this.currentPart == this.COMPLETE) { - return; - } - - debug("Equip | Got message", channel, message, sender, MyAvatar.sessionUUID); - - if (channel == "Tutorial-Spinner") { - if (this.currentPart == this.PART1 && message == "wasLit") { - this.currentPart = this.PART2; - debug("Equip | Starting part 2"); - Script.setTimeout(function() { - debug("Equip | Starting part 3"); - this.currentPart = this.PART3; - hideEntitiesWithTags(this.tagsPart1); - showEntitiesWithTags(this.tagsPart2); - setControllerPartLayer('trigger', 'normal'); - setControllerPartLayer('grip', 'highlight'); - setControllerPartLayer('tips', 'grip'); - Messages.subscribe('Hifi-Object-Manipulation'); - debug("Equip | Finished starting part 3"); - }.bind(this), 9000); - } - } else if (channel == "Hifi-Object-Manipulation") { - if (this.currentPart == this.PART3) { - var data = parseJSON(message); - if (data.action == 'release' && data.grabbedEntity == this.lighterID) { - debug("Equip | Got release, finishing step"); - this.stopWatchingGun(); - this.currentPart = this.COMPLETE; - playSuccessSound(); - Script.setTimeout(this.onFinish.bind(this), 1500); - } - } - } - }, - cleanup: function() { - debug("Equip | Got yaw action"); - if (this.watcherIntervalID) { - Script.clearInterval(this.watcherIntervalID); - this.watcherIntervalID = null; - } - - setControllerPartLayer('tips', 'blank'); - setControllerPartLayer('grip', 'normal'); - setControllerPartLayer('trigger', 'normal'); - this.stopWatchingGun(); - this.currentPart = this.COMPLETE; - - if (this.checkCollidesTimer) { - Script.clearInterval(this.checkCollidesTimer); - this.checkColllidesTimer = null; - } - - hideEntitiesWithTags(this.tagsPart1); - hideEntitiesWithTags(this.tagsPart2); - hideEntitiesWithTags(this.tags); - deleteEntitiesWithTag(this.tempTag); - } -}; - - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Turn Around // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepTurnAround = function(tutorialManager) { - this.name = 'turnAround'; - - this.tags = ["turnAround", "turnAround-" + tutorialManager.controllerName]; - this.tempTag = "turnAround-temporary"; - - this.onActionBound = this.onAction.bind(this); - this.numTimesSnapTurnPressed = 0; - this.numTimesSmoothTurnPressed = 0; -} -stepTurnAround.prototype = { - start: function(onFinish) { - setControllerPartLayer('joystick', 'highlight'); - setControllerPartLayer('touchpad', 'arrows'); - setControllerPartLayer('tips', 'arrows'); - - showEntitiesWithTags(this.tags); - - this.numTimesSnapTurnPressed = 0; - this.numTimesSmoothTurnPressed = 0; - this.smoothTurnDown = false; - Controller.actionEvent.connect(this.onActionBound); - - this.interval = Script.setInterval(function() { - debug("TurnAround | Checking if finished", - this.numTimesSnapTurnPressed, this.numTimesSmoothTurnPressed); - var FORWARD_THRESHOLD = 90; - var REQ_NUM_TIMES_SNAP_TURN_PRESSED = 3; - var REQ_NUM_TIMES_SMOOTH_TURN_PRESSED = 2; - - var dir = Quat.getFront(MyAvatar.orientation); - var angle = Math.atan2(dir.z, dir.x); - var angleDegrees = ((angle / Math.PI) * 180); - - var hasTurnedEnough = this.numTimesSnapTurnPressed >= REQ_NUM_TIMES_SNAP_TURN_PRESSED - || this.numTimesSmoothTurnPressed >= REQ_NUM_TIMES_SMOOTH_TURN_PRESSED; - var facingForward = Math.abs(angleDegrees) < FORWARD_THRESHOLD - if (hasTurnedEnough && facingForward) { - Script.clearInterval(this.interval); - this.interval = null; - playSuccessSound(); - onFinish(); - } - }.bind(this), 100); - }, - onAction: function(action, value) { - var STEP_YAW_ACTION = 6; - var SMOOTH_YAW_ACTION = 4; - - if (action == STEP_YAW_ACTION && value != 0) { - debug("TurnAround | Got step yaw action"); - ++this.numTimesSnapTurnPressed; - } else if (action == SMOOTH_YAW_ACTION) { - debug("TurnAround | Got smooth yaw action"); - if (this.smoothTurnDown && value === 0) { - this.smoothTurnDown = false; - ++this.numTimesSmoothTurnPressed; - } else if (!this.smoothTurnDown && value !== 0) { - this.smoothTurnDown = true; - } - } - }, - cleanup: function() { - debug("TurnAround | Cleanup"); - try { - Controller.actionEvent.disconnect(this.onActionBound); - } catch (e) { - } - - setControllerPartLayer('joystick', 'normal'); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('tips', 'blank'); - - if (this.interval) { - Script.clearInterval(this.interval); - } - hideEntitiesWithTags(this.tags); - deleteEntitiesWithTag(this.tempTag); - } -}; - - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Teleport // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepTeleport = function(tutorialManager) { - this.name = 'teleport'; - - this.tags = ["teleport", "teleport-" + tutorialManager.controllerName]; - this.tempTag = "teleport-temporary"; -} -stepTeleport.prototype = { - start: function(onFinish) { - setControllerPartLayer('button_a', 'highlight'); - setControllerPartLayer('touchpad', 'teleport'); - setControllerPartLayer('tips', 'teleport'); - - Messages.sendLocalMessage('Hifi-Teleport-Disabler', 'none'); - - // Wait until touching teleport pad... - var padProps = getEntityWithName(TELEPORT_PAD_NAME); - var xMin = padProps.position.x - padProps.dimensions.x / 2; - var xMax = padProps.position.x + padProps.dimensions.x / 2; - var zMin = padProps.position.z - padProps.dimensions.z / 2; - var zMax = padProps.position.z + padProps.dimensions.z / 2; - function checkCollides() { - debug("Teleport | Checking if on pad..."); - - var pos = MyAvatar.position; - - debug('Teleport | x', pos.x, xMin, xMax); - debug('Teleport | z', pos.z, zMin, zMax); - - if (pos.x > xMin && pos.x < xMax && pos.z > zMin && pos.z < zMax) { - debug("Teleport | On teleport pad"); - Script.clearInterval(this.checkCollidesTimer); - this.checkCollidesTimer = null; - playSuccessSound(); - onFinish(); - } - } - this.checkCollidesTimer = Script.setInterval(checkCollides.bind(this), 500); - - showEntitiesWithTags(this.tags); - }, - cleanup: function() { - debug("Teleport | Cleanup"); - setControllerPartLayer('button_a', 'normal'); - setControllerPartLayer('touchpad', 'blank'); - setControllerPartLayer('tips', 'blank'); - - if (this.checkCollidesTimer) { - Script.clearInterval(this.checkCollidesTimer); - } - hideEntitiesWithTags(this.tags); - deleteEntitiesWithTag(this.tempTag); - } -}; - - - - - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// // -// STEP: Finish // -// // -/////////////////////////////////////////////////////////////////////////////// -var stepFinish = function() { - this.name = 'finish'; - - this.tag = "finish"; - this.tempTag = "finish-temporary"; -} -stepFinish.prototype = { - start: function(onFinish) { - editEntitiesWithTag('door', { visible: false, collisonless: true }); - showEntitiesWithTag(this.tag); - Settings.setValue("tutorialComplete", true); - onFinish(); - }, - cleanup: function() { - } -}; - -var stepCleanupFinish = function() { - this.name = 'cleanup'; - - this.shouldLog = false; -} -stepCleanupFinish.prototype = { - start: function(onFinish) { - hideEntitiesWithTag('finish'); - onFinish(); - }, - cleanup: function() { - } -}; - - - - - -TutorialManager = function() { - var STEPS; - - var currentStepNum = -1; - var currentStep = null; - var startedTutorialAt = 0; - var startedLastStepAt = 0; - var didFinishTutorial = false; - - var wentToEntryStepNum; - var VERSION = 2; - var tutorialID; - - var self = this; - - // The real controller name is the actual detected controller name, or 'unknown' - // if one is not found. - if (HMD.isSubdeviceContainingNameAvailable("OculusTouch")) { - this.controllerName = "touch"; - this.realControllerName = "touch"; - } else if (HMD.isHandControllerAvailable("OpenVR")) { - this.controllerName = "vive"; - this.realControllerName = "vive"; - } else { - info("ERROR, no known hand controller found, defaulting to Vive"); - this.controllerName = "vive"; - this.realControllerName = "unknown"; - } - - this.startTutorial = function() { - currentStepNum = -1; - currentStep = null; - startedTutorialAt = Date.now(); - - // Old versions of interface do not have the Script.generateUUID function. - // If Script.generateUUID is not available, default to an empty string. - tutorialID = Script.generateUUID ? Script.generateUUID() : ""; - STEPS = [ - new stepStart(this), - new stepOrient(this), - new stepFarGrab(this), - new stepNearGrab(this), - new stepEquip(this), - new stepTurnAround(this), - new stepTeleport(this), - new stepFinish(this), - new stepEnableControllers(this), - ]; - wentToEntryStepNum = STEPS.length; - for (var i = 0; i < STEPS.length; ++i) { - STEPS[i].cleanup(); - } - MyAvatar.shouldRenderLocally = false; - this.startNextStep(); - } - - this.onFinish = function() { - debug("onFinish", currentStepNum); - if (currentStep && currentStep.shouldLog !== false) { - self.trackStep(currentStep.name, currentStepNum); - } - - self.startNextStep(); - } - - this.startNextStep = function() { - if (currentStep) { - currentStep.cleanup(); - } - - ++currentStepNum; - - // This always needs to be set because we use this value when - // tracking that the user has gone through the entry portal. When the - // tutorial finishes, there is a last "pseudo" step that the user - // finishes when stepping into the portal. - startedLastStepAt = Date.now(); - - if (currentStepNum >= STEPS.length) { - // Done - info("DONE WITH TUTORIAL"); - currentStepNum = -1; - currentStep = null; - didFinishTutorial = true; - return false; - } else { - info("Starting step", currentStepNum); - currentStep = STEPS[currentStepNum]; - currentStep.start(this.onFinish); - return true; - } - }.bind(this); - - this.restartStep = function() { - if (currentStep) { - currentStep.cleanup(); - currentStep.start(this.onFinish); - } - } - - this.stopTutorial = function() { - if (currentStep) { - currentStep.cleanup(); - HMD.requestHideHandControllers(); - } - reenableEverything(); - currentStepNum = -1; - currentStep = null; - } - - this.trackStep = function(name, stepNum) { - var timeToFinishStep = (Date.now() - startedLastStepAt) / 1000; - var tutorialTimeElapsed = (Date.now() - startedTutorialAt) / 1000; - UserActivityLogger.tutorialProgress( - name, stepNum, timeToFinishStep, tutorialTimeElapsed, - tutorialID, VERSION, this.realControllerName); - } - - // This is a message sent from the "entry" portal in the courtyard, - // after the tutorial has finished. - this.enteredEntryPortal = function() { - info("Got enteredEntryPortal"); - if (didFinishTutorial) { - info("Tracking wentToEntry"); - this.trackStep("wentToEntry", wentToEntryStepNum); - } - } -} - -// To run the tutorial: -// -//var tutorialManager = new TutorialManager(); -//tutorialManager.startTutorial(); -// -// -//var keyReleaseHandler = function(event) { -// if (event.isShifted && event.isAlt) { -// print('here', event.text); -// if (event.text == "F12") { -// if (!tutorialManager.startNextStep()) { -// tutorialManager.startTutorial(); -// } -// } else if (event.text == "F11") { -// tutorialManager.restartStep(); -// } else if (event.text == "F10") { -// MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; -// } else if (event.text == "r") { -// tutorialManager.stopTutorial(); -// tutorialManager.startTutorial(); -// } -// } -//}; -//Controller.keyReleaseEvent.connect(keyReleaseHandler); diff --git a/tutorial/tutorialEntityIDs.js b/tutorial/tutorialEntityIDs.js deleted file mode 100644 index f53ac1a56c..0000000000 --- a/tutorial/tutorialEntityIDs.js +++ /dev/null @@ -1,360 +0,0 @@ -TUTORIAL_TAG_TO_ENTITY_IDS_MAP = { - "teleport-vive": { - "{7df1abc4-1b7c-4352-985c-f3f6ad8d65b7}": { - "tag": "teleport-vive" - } - }, - "teleport-touch": { - "{ff064b9e-7fa4-4693-a386-a67b9f92a948}": { - "tag": "teleport-touch" - } - }, - "turnAround-vive": { - "{9b14f224-b2f6-447f-bb86-f5d875cf4c33}": { - "tag": "turnAround-vive" - } - }, - "turnAround-touch": { - "{ce74b3ca-d1c7-4980-bd98-2d488095a39e}": { - "tag": "turnAround-touch" - } - }, - "teleport": { - "{4478f7b5-d3ac-4213-9a7b-ad8cd69575b8}": { - "tag": "teleport" - } - }, - "finish": { - "{340e05b5-88df-4b2b-b43c-756dd714d6d8}": { - "tag": "finish" - } - }, - "door": { - "{9c5b0fee-e695-4516-94cd-153371e3857b}": { - "tag": "door" - } - }, - "farGrab": { - "{70fcd96c-cd59-4f23-9ca5-a167f2f85680}": { - "visible": false, - "tag": "farGrab" - }, - "{ff7b9793-0d94-4f18-bc09-4ab589126e60}": { - "tag": "farGrab" - }, - "{fdd77d2c-af36-41c1-ba57-74b7ae79d996}": { - "tag": "farGrab" - }, - "{e11700f6-bc9a-411f-9ddc-bf265d4e3ccf}": { - "tag": "farGrab" - }, - "{95850c56-cd1c-42b9-ab6b-a163a6f2878f}": { - "tag": "farGrab" - } - }, - "nearGrab-vive": { - "{88221a22-b710-4d35-852b-5257b0aa77dc}": { - "tag": "nearGrab-vive" - } - }, - "nearGrab-touch": { - "{7c0f2fde-6c5c-459b-bf82-421979cebf2e}": { - "tag": "nearGrab-touch" - } - }, - "nearGrab": { - "{55c861ef-60ca-4722-a6c5-9c6967966ec5}": { - "tag": "nearGrab" - }, - "{644d655b-ae66-43b1-9bab-a44b9a8ad632}": { - "tag": "nearGrab" - }, - "{8bf0baa1-88d0-448a-a782-100d4413bd82}": { - "tag": "nearGrab" - }, - "{5cf22b9c-fb22-4854-8821-554422980b24}": { - "visible": false, - "tag": "nearGrab" - } - }, - "equip-part1-touch": { - "{470f0634-8be7-4b52-a8bd-5183d489fcb6}": { - "tag": "equip-part1-touch" - } - }, - "equip-part1-vive": { - "{97ced5e7-fc81-40f9-a9e8-f85b4b30f24c}": { - "tag": "equip-part1-vive" - } - }, - "equip-part1": { - "{d73822ca-0a34-4cf4-a530-3258ac459a14}": { - "tag": "equip-part1" - }, - "{8572d991-5777-45df-97bf-7243d7b12f81}": { - "tag": "equip-part1" - }, - "{da5ea72e-54b6-41ac-b711-742b062b6968}": { - "tag": "equip-part1" - }, - "{c8944a13-9acb-4d77-b1ee-851845e98357}": { - "tag": "equip-part1" - }, - "{e9481c78-1a21-43f7-b54c-58f2efdf3c8f}": { - "tag": "equip-part1" - }, - "{ca3c28f3-15fc-4349-a85e-eaca0fad6434}": { - "tag": "equip-part1" - }, - "{09ddcb94-52a7-4f50-a5a2-db9db28fc519}": { - "tag": "equip-part1" - }, - "{dd13fcd5-616f-4749-ab28-2e1e8bc512e9}": { - "tag": "equip-part1" - } - }, - "equip-part2-vive": { - "{b5d17eda-90ab-40cf-b973-efcecb2e992e}": { - "tag": "equip-part2-vive" - }, - "{6307cd16-dd1d-4988-a339-578178436b45}": { - "tag": "equip-part2-vive" - } - }, - "equip-part2-touch": { - "{69195139-e020-4739-bb2c-50faebc6860a}": { - "tag": "equip-part2-touch" - }, - "{9b0a99ae-221b-4e59-ba3c-d8e64a083774}": { - "tag": "equip-part2-touch" - } - }, - "bothGrab": { - "{14792a6e-dc6f-4e7a-843f-4b109b06b5a4}": { - "visible": false, - "tag": "bothGrab", - "collidable": true - }, - "{215dcd14-88fc-4604-9033-cbd2a660178a}": { - "tag": "bothGrab" - }, - "{fbc2e40d-0633-45ac-b1c9-97fc8465f93b}": { - "tag": "bothGrab" - }, - "{6752dad6-109d-4dc5-aef7-dc8509468cf4}": { - "tag": "bothGrab" - }, - "{178e2c71-dff5-4231-8d28-df47fddf4709}": { - "soundKey": { - "playbackGapRange": 0, - "url": "atp:/sounds/crackling_fire.L.wav", - "volume": 0.5, - "playbackGap": 5, - "playing": false, - "loop": true - }, - "tag": "bothGrab" - }, - "{52445ac5-8730-4457-827e-6c076d2c609c}": { - "tag": "bothGrab" - } - }, - "raiseHands": { - "{7139e45d-25cf-470b-b133-c0fda0099d2b}": { - "tag": "raiseHands" - } - }, - "equip": { - "{e7897c9c-f4fa-4989-a383-28af56c2e544}": { - "visible": false, - "tag": "equip" - }, - "{9df518da-9e65-4b76-8a79-eeefdb0b7310}": { - "visible": false, - "tag": "equip" - }, - "{1a77c20e-5d9b-4b54-bf20-1416141a7ca8}": { - "tag": "equip" - } - }, - "orient-vive": { - "{95d233ab-ed0a-46e1-b047-1c542688ef3f}": { - "tag": "orient-vive" - } - }, - "orient-touch": { - "{1c95f945-ec46-4aac-b0f1-e64e073dbfaa}": { - "tag": "orient-touch" - } - } -}; - -TUTORIAL_NAME_TO_ENTITY_PROPERTIES_MAP = { - "tutorial/gun_spawn": { - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 0, - "green": 0, - "red": 255 - }, - "created": "2016-09-08T18:38:24Z", - "dimensions": { - "x": 0.0649842768907547, - "y": 0.0649842768907547, - "z": 0.0649842768907547 - }, - "id": "{9df518da-9e65-4b76-8a79-eeefdb0b7310}", - "ignoreForCollisions": 1, - "lastEdited": 1481926907366120, - "lastEditedBy": "{b80185ea-0936-4397-a5a4-3a64004f545f}", - "name": "tutorial/gun_spawn", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.60231781005859375, - "y": 0.68465065956115723, - "z": 0.39223569631576538 - }, - "queryAACube": { - "scale": 0.11255607008934021, - "x": 0.54603976011276245, - "y": 0.62837260961532593, - "z": 0.33595764636993408 - }, - "rotation": { - "w": -0.025101065635681152, - "x": 0.70666050910949707, - "y": 0.70666050910949707, - "z": -0.025131583213806152 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"visible\":false,\"tag\":\"equip\"}", - "visible": 0 - }, - "tutorial/nearGrab/box_spawn": { - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 255, - "green": 0, - "red": 255 - }, - "created": "2016-09-08T18:38:24Z", - "dimensions": { - "x": 0.082253716886043549, - "y": 0.082253716886043549, - "z": 0.082253716886043549 - }, - "id": "{5cf22b9c-fb22-4854-8821-554422980b24}", - "ignoreForCollisions": 1, - "lastEdited": 1481926907334206, - "lastEditedBy": "{b80185ea-0936-4397-a5a4-3a64004f545f}", - "name": "tutorial/nearGrab/box_spawn", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 0.61857688426971436, - "y": 0.80955326557159424, - "z": 0.36191046237945557 - }, - "queryAACube": { - "scale": 0.14246761798858643, - "x": 0.54734307527542114, - "y": 0.73831945657730103, - "z": 0.29067665338516235 - }, - "rotation": { - "w": 1, - "x": -1.52587890625e-05, - "y": -1.52587890625e-05, - "z": -1.52587890625e-05 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"visible\":false,\"tag\":\"nearGrab\"}", - "visible": 0 - }, - "tutorial/farGrab/box_spawn": { - "clientOnly": 0, - "collisionless": 1, - "color": { - "blue": 255, - "green": 0, - "red": 255 - }, - "created": "2016-09-08T18:38:24Z", - "dimensions": { - "x": 0.16850528120994568, - "y": 0.16850528120994568, - "z": 0.16850528120994568 - }, - "id": "{70fcd96c-cd59-4f23-9ca5-a167f2f85680}", - "ignoreForCollisions": 1, - "lastEdited": 1481926908795578, - "lastEditedBy": "{b80185ea-0936-4397-a5a4-3a64004f545f}", - "name": "tutorial/farGrab/box_spawn", - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "position": { - "x": 3.4866282939910889, - "y": 0.67159509658813477, - "z": 0.47892442345619202 - }, - "queryAACube": { - "scale": 0.64707136154174805, - "x": 3.2037394046783447, - "y": 0.33042514324188232, - "z": 0.14542555809020996 - }, - "rotation": { - "w": 1, - "x": -1.52587890625e-05, - "y": -1.52587890625e-05, - "z": -1.52587890625e-05 - }, - "shape": "Cube", - "type": "Box", - "userData": "{\"visible\":false,\"tag\":\"farGrab\"}", - "visible": 0 - }, - "tutorial/teleport/pad": { - "userData": "{\"tag\":\"teleport\"}", - "rotation": { - "y": -0.9702650308609009, - "x": -2.1246911273919977e-05, - "z": -4.222852112434339e-06, - "w": 0.2420452982187271 - }, - "dimensions": { - "y": 0.4365682601928711, - "x": 2.1751723289489746, - "z": 2.175173044204712 - }, - "collisionless": 1, - "created": "2016-09-08T18:38:24Z", - "queryAACube": { - "y": -1.7979401350021362, - "x": 7.5136213302612305, - "scale": 3.106983184814453, - "z": -1.4602710008621216 - }, - "visible": 0, - "angularVelocity": { - "y": -0.5235987901687622, - "x": 0, - "z": 0 - }, - "clientOnly": 0, - "owningAvatarID": "{00000000-0000-0000-0000-000000000000}", - "angularDamping": 0, - "position": { - "y": -0.2444484978914261, - "x": 9.067112922668457, - "z": 0.09322060644626617 - }, - "modelURL": "atp:/alan/dev/Teleport-Pad.fbx", - "ignoreForCollisions": 1, - "type": "Model", - "id": "{4478f7b5-d3ac-4213-9a7b-ad8cd69575b8}", - "name": "tutorial/teleport/pad" - } -}; diff --git a/tutorial/tutorialStartZone.js b/tutorial/tutorialStartZone.js deleted file mode 100644 index 13e5ac89d0..0000000000 --- a/tutorial/tutorialStartZone.js +++ /dev/null @@ -1,51 +0,0 @@ -(function() { - var TutorialStartZone = function() { - print("TutorialStartZone | Creating"); - }; - - TutorialStartZone.prototype = { - preload: function(entityID) { - print("TutorialStartZone | Preload"); - this.entityID = entityID; - this.sendStartIntervalID = null; - }, - enterEntity: function() { - var self = this; - // send message to outer zone - print("TutorialStartZone | Entered the tutorial start area"); - if (HMD.isHMDAvailable() && HMD.isHandControllerAvailable() && HMD.active) { - function sendStart() { - print("TutorialStartZone | Checking parent ID"); - var parentID = Entities.getEntityProperties(self.entityID, 'parentID').parentID; - print("TutorialStartZone | Parent ID is: ", parentID); - if (parentID) { - print("TutorialStartZone | Sending start"); - Entities.callEntityMethod(parentID, 'onEnteredStartZone'); - } else { - print("TutorialStartZone | ERROR: No parent id found on tutorial start zone"); - } - } - this.sendStartIntervalID = Script.setInterval(sendStart, 1500); - sendStart(); - } else { - print("TutorialStartZone | User tried to go to tutorial without active HMD and hand controllers, sending back to /"); - Window.alert("To proceed with this tutorial, please connect your Vive or Oculus headset and hand controllers."); - location = "/"; - } - }, - leaveEntity: function() { - print("TutorialStartZone | Exited the tutorial start area"); - if (this.sendStartIntervalID) { - Script.clearInterval(this.sendStartIntervalID); - } - var parentID = Entities.getEntityProperties(this.entityID, 'parentID').parentID; - print("TutorialStartZone | Parent ID is: ", parentID); - if (parentID) { - print("TutorialStartZone | Sending onLeftStartZone"); - Entities.callEntityMethod(parentID, 'on'); - } - } - }; - - return new TutorialStartZone(); -}); diff --git a/tutorial/tutorialZone.js b/tutorial/tutorialZone.js deleted file mode 100644 index 78443ef7df..0000000000 --- a/tutorial/tutorialZone.js +++ /dev/null @@ -1,142 +0,0 @@ -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; - }; -} - -(function() { - Script.include("ownershipToken.js"); - Script.include("tutorial.js"); - - var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable"; - function setAwayEnabled(value) { - var message = value ? 'enable' : 'disable'; - Messages.sendLocalMessage(CHANNEL_AWAY_ENABLE, message); - } - - var TutorialZone = function() { - print("TutorialZone | Creating"); - this.token = null; - }; - - TutorialZone.prototype = { - keyReleaseHandler: function(event) { - print(event.text); - if (event.isShifted && event.isAlt) { - if (event.text == "F12") { - if (!this.tutorialManager.startNextStep()) { - this.tutorialManager.startTutorial(); - } - } else if (event.text == "F11") { - this.tutorialManager.restartStep(); - } else if (event.text == "F10") { - MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; - } else if (event.text == "r") { - this.tutorialManager.stopTutorial(); - this.tutorialManager.startTutorial(); - } - } - }, - preload: function(entityID) { - print("TutorialZone | Preload"); - this.entityID = entityID; - }, - onEnteredStartZone: function() { - print("TutorialZone | Got onEnteredStartZone"); - var self = this; - if (!this.token) { - print("TutorialZone | Creating token"); - // The start zone has been entered, hide the overlays immediately - setAwayEnabled(false); - Menu.setIsOptionChecked("Overlays", false); - MyAvatar.shouldRenderLocally = false; - Toolbars.getToolbar("com.highfidelity.interface.toolbar.system").writeProperty("visible", false); - this.token = new OwnershipToken(Math.random() * 100000, this.entityID, { - onGainedOwnership: function(token) { - print("TutorialZone | GOT OWNERSHIP"); - if (!self.tutorialManager) { - self.tutorialManager = new TutorialManager(); - } - self.tutorialManager.startTutorial(); - print("TutorialZone | making bound release handler"); - self.keyReleaseHandlerBound = self.keyReleaseHandler.bind(self); - print("TutorialZone | binding"); - Controller.keyReleaseEvent.connect(self.keyReleaseHandlerBound); - print("TutorialZone | done"); - }, - onLostOwnership: function(token) { - print("TutorialZone | LOST OWNERSHIP"); - if (self.tutorialManager) { - print("TutorialZone | stopping tutorial.."); - self.tutorialManager.stopTutorial(); - print("TutorialZone | done"); - Controller.keyReleaseEvent.disconnect(self.keyReleaseHandlerBound); - } else { - print("TutorialZone | no tutorial manager..."); - } - } - }); - } - }, - onLeftStartZone: function() { - print("TutorialZone | Got onLeftStartZone"); - - // If the start zone was exited, and the tutorial hasn't started, go ahead and - // re-enable the HUD/Overlays - if (!this.tutorialManager) { - Menu.setIsOptionChecked("Overlays", true); - MyAvatar.shouldRenderLocally = true; - setAwayEnabled(true); - Toolbars.getToolbar("com.highfidelity.interface.toolbar.system").writeProperty("visible", true); - } - }, - - onEnteredEntryPortal: function() { - print("TutorialZone | Got onEnteredEntryPortal"); - if (this.tutorialManager) { - print("TutorialZone | Calling enteredEntryPortal"); - this.tutorialManager.enteredEntryPortal(); - } - }, - - enterEntity: function() { - print("TutorialZone | ENTERED THE TUTORIAL AREA"); - }, - leaveEntity: function() { - print("TutorialZone | EXITED THE TUTORIAL AREA"); - if (this.token) { - print("TutorialZone | Destroying token"); - this.token.destroy(); - this.token = null; - } - if (this.tutorialManager) { - this.tutorialManager.stopTutorial(); - //this.tutorialManager = null; - } - } - }; - - return new TutorialZone(); -});