From a05f68170474bef32a9d1dd5b8ed3cce808ed213 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 15 Sep 2016 13:02:33 -0700 Subject: [PATCH] Add ownership token tot tutorial --- tutorial/ownershipToken.js | 188 +++++++++++++++++++++++++++++++++++ tutorial/tutorial.js | 197 ++++++++++++++++++++----------------- 2 files changed, 292 insertions(+), 93 deletions(-) create mode 100644 tutorial/ownershipToken.js diff --git a/tutorial/ownershipToken.js b/tutorial/ownershipToken.js new file mode 100644 index 0000000000..ae212baa84 --- /dev/null +++ b/tutorial/ownershipToken.js @@ -0,0 +1,188 @@ +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; + //debug("Owner lifetime: ", properties.lifetime, properties.age); + if (properties.age > 0.5 && 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.name = 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.onRequestingOwnership = getOption(options, 'onRequestingOwnership', function() { }); + 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) { + 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, return"); + 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); + }, +}; diff --git a/tutorial/tutorial.js b/tutorial/tutorial.js index 537931080d..3ccb994ba3 100644 --- a/tutorial/tutorial.js +++ b/tutorial/tutorial.js @@ -30,6 +30,7 @@ Script.include("entityData.js"); Script.include("viveHandsv2.js"); Script.include("lighter/createButaneLighter.js"); +Script.include('ownershipToken.js'); var BASKET_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trach-Can-3.fbx"; var BASKET_COLLIDER_URL = "http://hifi-content.s3.amazonaws.com/alan/dev/Trash-Can-4.obj"; @@ -146,19 +147,19 @@ function spawnWithTag(entityData, transform, tag) { } function deleteEntitiesWithTag(tag) { - print("searching for...:", tag); + print("searching for...:", tag); var entityIDs = findEntitiesWithTag(tag); for (var i = 0; i < entityIDs.length; ++i) { - print("Deleteing:", entityIDs[i]); + //print("Deleteing:", entityIDs[i]); Entities.deleteEntity(entityIDs[i]); } } function editEntitiesWithTag(tag, propertiesOrFn) { - print("Editing:", tag); + //print("Editing:", tag); var entityIDs = findEntitiesWithTag(tag); - print("Editing...", entityIDs); + //print("Editing...", entityIDs); for (var i = 0; i < entityIDs.length; ++i) { - print("Editing...", entityIDs[i]); + //print("Editing...", entityIDs[i]); if (isFunction(propertiesOrFn)) { Entities.editEntity(entityIDs[i], propertiesOrFn(entityIDs[i])); } else { @@ -318,11 +319,11 @@ stepOrient.prototype = { } }; - this.overlay = new StayInFrontOverlay("model", { - url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/welcome.fbx?11", - ignoreRayIntersection: true, - visible: false - }, 1.5, { x: 0, y: 0.3, z: 0 }); + // this.overlay = new StayInFrontOverlay("model", { + // url: "http://hifi-content.s3.amazonaws.com/alan/dev/Prompt-Cards/welcome.fbx?11", + // ignoreRayIntersection: true, + // visible: false + // }, 1.5, { x: 0, y: 0.3, z: 0 }); // Spawn content set //spawnWithTag(HandsAboveHeadData, defaultTransform, tag); @@ -952,105 +953,115 @@ function hideEntitiesWithTag(tag) { }); } -var STEPS; -var currentStepNum = -1; -var currentStep = null; -function startTutorial() { - currentStepNum = -1; - currentStep = null; - STEPS = [ - new stepDisableControllers("step0"), - new stepOrient("orient"), - //new stepWelcome("welcome"), - new stepRaiseAboveHead("raiseHands"), - new stepNearGrab("nearGrab"), - new stepFarGrab("farGrab"), - new stepEquip("equip"), - new stepTurnAround("turnAround"), - new stepTeleport("teleport"), - new stepFinish("finish"), - ]; - for (var i = 0; i < STEPS.length; ++i) { - STEPS[i].cleanup(); - } - location = "/tutorial_begin"; - //location = "/tutorial"; - MyAvatar.shouldRenderLocally = false; - startNextStep(); -} +TutorialManager = function() { + var STEPS; -function startNextStep() { - if (currentStep) { - currentStep.cleanup(); - } + var currentStepNum = -1; + var currentStep = null; - ++currentStepNum; - - if (currentStepNum >= STEPS.length) { - // Done - print("DONE WITH TUTORIAL"); + this.startTutorial = function() { + currentStepNum = -1; + currentStep = null; + STEPS = [ + new stepDisableControllers("step0"), + new stepOrient("orient"), + //new stepWelcome("welcome"), + new stepRaiseAboveHead("raiseHands"), + new stepNearGrab("nearGrab"), + new stepFarGrab("farGrab"), + new stepEquip("equip"), + new stepTurnAround("turnAround"), + new stepTeleport("teleport"), + new stepFinish("finish"), + ]; + for (var i = 0; i < STEPS.length; ++i) { + STEPS[i].cleanup(); + } + location = "/tutorial_begin"; + //location = "/tutorial"; + MyAvatar.shouldRenderLocally = false; + this.startNextStep(); + } + + this.startNextStep = function() { + if (currentStep) { + currentStep.cleanup(); + } + + ++currentStepNum; + + if (currentStepNum >= STEPS.length) { + // Done + print("DONE WITH TUTORIAL"); + currentStepNum = -1; + currentStep = null; + return false; + } else { + print("Starting step", currentStepNum); + currentStep = STEPS[currentStepNum]; + currentStep.start(this.startNextStep); + return true; + } + }.bind(this); + this.restartStep = function() { + if (currentStep) { + currentStep.cleanup(); + currentStep.start(this.startNextStep); + } + } + + this.stopTutorial = function() { + if (currentStep) { + currentStep.cleanup(); + } currentStepNum = -1; currentStep = null; - return false; - } else { - print("Starting step", currentStepNum); - currentStep = STEPS[currentStepNum]; - currentStep.start(startNextStep); - return true; } } -function restartStep() { - if (currentStep) { - currentStep.cleanup(); - currentStep.start(startNextStep); - } -} - -function skipTutorial() { -} - -function stopTutorial() { - if (currentStep) { - currentStep.cleanup(); - } - currentStepNum = -1; - currentStep = null; -} - -startTutorial(); Script.scriptEnding.connect(function() { Controller.enableMapping('handControllerPointer-click'); }); Controller.disableMapping('handControllerPointer-click'); -//mapping.from([Controller.Standard.RY]).to(noop); - //{ "from": "Vive.LeftApplicationMenu", "to": "Standard.LeftSecondaryThumb" }, -//mapping.from([Controller.Standard.RY]).when("Controller.Application.Grounded").to(noop); -//mapping.from([Controller.Standard.RY]).when(Controller.Application.Grounded).to(noop); +// var entityID = '{be3d10a3-262a-4827-b30c-ec025c4325dc}'; +// var token = new OwnershipToken(Math.random() * 100000, entityID, { +// onGainedOwnership: function(token) { +// //Script.setTimeout(function() { token.destroy() }, 15000); +// Controller.keyReleaseEvent.connect(keyReleaseHandler); +// startTutorial(); +// }, +// onLostOwnership: function(token) { +// Controller.keyReleaseEvent.disconnect(keyReleaseHandler); +// stopTutorial(); +// } +// }); - -Script.scriptEnding.connect(stopTutorial); - - - -Controller.keyReleaseEvent.connect(function (event) { - print(event.text); - if (event.text == ",") { - if (!startNextStep()) { - startTutorial(); - } - } else if (event.text == "F11") { - restartStep(); - } else if (event.text == "F10") { - MyAvatar.shouldRenderLocally = !MyAvatar.shouldRenderLocally; - } else if (event.text == "r") { - stopTutorial(); - startTutorial(); - } +//tutorialManager = new TutorialManager(); +//tutorialManager.startTutorial(); +//Controller.keyReleaseEvent.connect(keyReleaseHandler); +Script.scriptEnding.connect(function() { + //token.destroy(); + //stopTutorial(); }); +// function keyReleaseHandler(event) { +// print(event.text); +// if (event.text == ",") { +// 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(); +// } +// } +// // Messages.sendLocalMessage('Controller-Display', JSON.stringify({ // name: "menu", // visible: false,