diff --git a/unpublishedScripts/interaction/Interaction.js b/unpublishedScripts/interaction/Interaction.js new file mode 100644 index 0000000000..a488e9f279 --- /dev/null +++ b/unpublishedScripts/interaction/Interaction.js @@ -0,0 +1,179 @@ +// +// Interaction.js +// scripts/interaction +// +// Created by Trevor Berninger on 3/20/17. +// Copyright 2017 Limitless ltd. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function(){ + print("loading interaction script"); + + var Avatar = false; + var NPC = false; + var previousNPC = false; + var hasCenteredOnNPC = false; + var distance = 10; + var r = 8; + var player = false; + + var baselineX = 0; + var baselineY = 0; + var nodRange = 20; + var shakeRange = 20; + + var ticker = false; + var heartbeatTimer = false; + + function callOnNPC(message) { + if(NPC) + Messages.sendMessage("interactionComs", NPC + ":" + message); + else + Messages.sendMessage("interactionComs", previousNPC + ":" + message); + } + + LimitlessSpeechRecognition.onFinishedSpeaking.connect(function(speech) { + print("Got: " + speech); + callOnNPC("voiceData:" + speech); + }); + + LimitlessSpeechRecognition.onReceivedTranscription.connect(function(speech) { + callOnNPC("speaking"); + }); + + function setBaselineRotations(rot) { + baselineX = rot.x; + baselineY = rot.y; + } + + function findLookedAtNPC() { + var intersection = AvatarList.findRayIntersection({origin: MyAvatar.position, direction: Quat.getFront(Camera.getOrientation())}, true); + if (intersection.intersects && intersection.distance <= distance){ + var npcAvatar = AvatarList.getAvatar(intersection.avatarID); + if (npcAvatar.displayName.search("NPC") != -1) { + setBaselineRotations(Quat.safeEulerAngles(Camera.getOrientation())); + return intersection.avatarID; + } + } + return false; + } + + function isStillFocusedNPC() { + var avatar = AvatarList.getAvatar(NPC); + if (avatar) { + var avatarPosition = avatar.position; + return Vec3.distance(MyAvatar.position, avatarPosition) <= distance && Math.abs(Quat.dot(Camera.getOrientation(), Quat.lookAtSimple(MyAvatar.position, avatarPosition))) > 0.6; + } + return false; // NPC reference died. Maybe it crashed or we teleported to a new world? + } + + function onWeLostFocus() { + print("lost NPC: " + NPC); + callOnNPC("onLostFocused"); + var baselineX = 0; + var baselineY = 0; + } + + function onWeGainedFocus() { + print("found NPC: " + NPC); + callOnNPC("onFocused"); + var rotation = Quat.safeEulerAngles(Camera.getOrientation()); + baselineX = rotation.x; + baselineY = rotation.y; + LimitlessSpeechRecognition.setListeningToVoice(true); + } + + function checkFocus() { + var newNPC = findLookedAtNPC(); + + if (NPC && newNPC != NPC && !isStillFocusedNPC()) { + onWeLostFocus(); + previousNPC = NPC; + NPC = false; + } + if (!NPC && newNPC != false) { + NPC = newNPC; + onWeGainedFocus(); + } + } + + function checkGesture() { + var rotation = Quat.safeEulerAngles(Camera.getOrientation()); + + var deltaX = Math.abs(rotation.x - baselineX); + if (deltaX > 180) { + deltaX -= 180; + } + var deltaY = Math.abs(rotation.y - baselineY); + if (deltaY > 180) { + deltaY -= 180; + } + + if (deltaX >= nodRange && deltaY <= shakeRange) { + callOnNPC("onNodReceived"); + } else if (deltaY >= shakeRange && deltaX <= nodRange) { + callOnNPC("onShakeReceived"); + } + } + + function tick() { + checkFocus(); + if (NPC) { + checkGesture(); + } + } + + function heartbeat() { + callOnNPC("beat"); + } + + Messages.subscribe("interactionComs"); + + Messages.messageReceived.connect(function (channel, message, sender) { + if(channel === "interactionComs" && player) { + var codeIndex = message.search('clientexec'); + if(codeIndex != -1) { + var code = message.substr(codeIndex+11); + Script.evaluate(code, ''); + } + } + }); + + this.enterEntity = function(id) { + player = true; + print("Something entered me: " + id); + LimitlessSpeechRecognition.setAuthKey("testKey"); + if (!ticker) { + ticker = Script.setInterval(tick, 333); + } + if(!heartbeatTimer) { + heartbeatTimer = Script.setInterval(heartbeat, 1000); + } + }; + this.leaveEntity = function(id) { + LimitlessSpeechRecognition.setListeningToVoice(false); + player = false; + print("Something left me: " + id); + if (previousNPC) + Messages.sendMessage("interactionComs", previousNPC + ":leftArea"); + if (ticker) { + ticker.stop(); + ticker = false; + } + if (heartbeatTimer) { + heartbeatTimer.stop(); + heartbeatTimer = false; + } + }; + this.unload = function() { + print("Okay. I'm Unloading!"); + if (ticker) { + ticker.stop(); + ticker = false; + } + }; + print("finished loading interaction script"); +}); diff --git a/unpublishedScripts/interaction/NPCHelpers.js b/unpublishedScripts/interaction/NPCHelpers.js new file mode 100644 index 0000000000..d36a71ea93 --- /dev/null +++ b/unpublishedScripts/interaction/NPCHelpers.js @@ -0,0 +1,179 @@ +// +// NPCHelpers.js +// scripts/interaction +// +// Created by Trevor Berninger on 3/20/17. +// Copyright 2017 Limitless ltd. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var audioInjector = false; +var blocked = false; +var playingResponseAnim = false; +var storyURL = ""; +var _qid = "start"; + +print("TESTTEST"); + +function strContains(str, sub) { + return str.search(sub) != -1; +} + +function callbackOnCondition(conditionFunc, ms, callback, count) { + var thisCount = 0; + if (typeof count !== 'undefined') { + thisCount = count; + } + if (conditionFunc()) { + callback(); + } else if (thisCount < 10) { + Script.setTimeout(function() { + callbackOnCondition(conditionFunc, ms, callback, thisCount + 1); + }, ms); + } else { + print("callbackOnCondition timeout"); + } +} + +function playAnim(animURL, looping, onFinished) { + print("got anim: " + animURL); + print("looping: " + looping); + // Start caching the animation if not already cached. + AnimationCache.getAnimation(animURL); + + // Tell the avatar to animate so that we can tell if the animation is ready without crashing + Avatar.startAnimation(animURL, 30, 1, false, false, 0, 1); + + // Continually check if the animation is ready + callbackOnCondition(function(){ + var details = Avatar.getAnimationDetails(); + // if we are running the request animation and are past the first frame, the anim is loaded properly + print("running: " + details.running); + print("url and animURL: " + details.url.trim().replace(/ /g, "%20") + " | " + animURL.trim().replace(/ /g, "%20")); + print("currentFrame: " + details.currentFrame); + return details.running && details.url.trim().replace(/ /g, "%20") == animURL.trim().replace(/ /g, "%20") && details.currentFrame > 0; + }, 250, function(){ + var timeOfAnim = ((AnimationCache.getAnimation(animURL).frames.length / 30) * 1000) + 100; // frames to miliseconds plus a small buffer + print("animation loaded. length: " + timeOfAnim); + // Start the animation again but this time with frame information + Avatar.startAnimation(animURL, 30, 1, looping, true, 0, AnimationCache.getAnimation(animURL).frames.length); + if (typeof onFinished !== 'undefined') { + print("onFinished defined. setting the timeout with timeOfAnim"); + timers.push(Script.setTimeout(onFinished, timeOfAnim)); + } + }); +} + +function playSound(soundURL, onFinished) { + callbackOnCondition(function() { + return SoundCache.getSound(soundURL).downloaded; + }, 250, function() { + if (audioInjector) { + audioInjector.stop(); + } + audioInjector = Audio.playSound(SoundCache.getSound(soundURL), {position: Avatar.position, volume: 1.0}); + if (typeof onFinished !== 'undefined') { + audioInjector.finished.connect(onFinished); + } + }); +} + +function npcRespond(soundURL, animURL, onFinished) { + if (typeof soundURL !== 'undefined' && soundURL != '') { + print("npcRespond got soundURL!"); + playSound(soundURL, function(){ + print("sound finished"); + var animDetails = Avatar.getAnimationDetails(); + print("animDetails.lastFrame: " + animDetails.lastFrame); + print("animDetails.currentFrame: " + animDetails.currentFrame); + if (animDetails.lastFrame < animDetails.currentFrame + 1 || !playingResponseAnim) { + onFinished(); + } + audioInjector = false; + }); + } + if (typeof animURL !== 'undefined' && animURL != '') { + print("npcRespond got animURL!"); + playingResponseAnim = true; + playAnim(animURL, false, function() { + print("anim finished"); + playingResponseAnim = false; + print("injector: " + audioInjector); + if (!audioInjector || !audioInjector.isPlaying()) { + print("resetting Timer"); + print("about to call onFinished"); + onFinished(); + } + }); + } +} + +function npcRespondBlocking(soundURL, animURL, onFinished) { + print("blocking response requested"); + if (!blocked) { + print("not already blocked"); + blocked = true; + npcRespond(soundURL, animURL, function(){ + if (onFinished){ + onFinished(); + }blocked = false; + }); + } +} + +function npcContinueStory(soundURL, animURL, nextID, onFinished) { + if (!nextID) { + nextID = _qid; + } + npcRespondBlocking(soundURL, animURL, function(){ + if (onFinished){ + onFinished(); + }setQid(nextID); + }); +} + +function setQid(newQid) { + print("setting quid"); + print("_qid: " + _qid); + _qid = newQid; + print("_qid: " + _qid); + doActionFromServer("init"); +} + +function runOnClient(code) { + Messages.sendMessage("interactionComs", "clientexec:" + code); +} + +function doActionFromServer(action, data, useServerCache) { + if (action == "start") { + ignoreCount = 0; + _qid = "start"; + } + var xhr = new XMLHttpRequest(); + xhr.open("POST", "http://gserv_devel.studiolimitless.com/story", true); + xhr.onreadystatechange = function(){ + if (xhr.readyState == 4){ + if (xhr.status == 200) { + print("200!"); + print("evaluating: " + xhr.responseText); + Script.evaluate(xhr.responseText, ""); + } else if (xhr.status == 444) { + print("Limitless Serv 444: API error: " + xhr.responseText); + } else { + print("HTTP Code: " + xhr.status + ": " + xhr.responseText); + } + } + }; + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + var postData = "url=" + storyURL + "&action=" + action + "&qid=" + _qid; + if (typeof data !== 'undefined' && data != '') { + postData += "&data=" + data; + } + if (typeof useServerCache !== 'undefined' && !useServerCache) { + postData += "&nocache=true"; + } + print("Sending: " + postData); + xhr.send(postData); +} diff --git a/unpublishedScripts/interaction/NPC_AC.js b/unpublishedScripts/interaction/NPC_AC.js new file mode 100644 index 0000000000..6eaee6a6e3 --- /dev/null +++ b/unpublishedScripts/interaction/NPC_AC.js @@ -0,0 +1,102 @@ +// +// NPC_AC.js +// scripts/interaction +// +// Created by Trevor Berninger on 3/20/17. +// Copyright 2017 Limitless ltd. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +var currentlyUsedIndices = []; +var timers = []; +var currentlyEngaged = false; +var questionNumber = 0; +var heartbeatTimeout = false; +function getRandomRiddle() { + var randIndex = null; + do { + randIndex = Math.floor(Math.random() * 15) + 1; + } while (randIndex in currentlyUsedIndices); + + currentlyUsedIndices.push(randIndex); + return randIndex.toString(); +} + +Script.include("https://raw.githubusercontent.com/Delamare2112/hifi/Interaction/unpublishedScripts/interaction/NPCHelpers.js", function(){ + print("NPCHelpers included.");main(); +}); + +var idleAnim = "https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/idle.fbx"; +var FST = "https://s3.amazonaws.com/hifi-public/tony/fixed-sphinx/sphinx.fst"; + +Agent.isAvatar = true; +Avatar.skeletonModelURL = FST; +Avatar.displayName = "NPC"; +Avatar.position = {x: 0.3, y: -23.4, z: 8.0}; +Avatar.orientation = {x: 0, y: 1, z: 0, w: 0}; +// Avatar.position = {x: 1340.3555, y: 4.078, z: -420.1562}; +// Avatar.orientation = {x: 0, y: -0.707, z: 0, w: 0.707}; +Avatar.scale = 2; + +Messages.subscribe("interactionComs"); + +function endInteraction() { + print("ending interaction"); + blocked = false; + currentlyEngaged = false; + if(audioInjector) + audioInjector.stop(); + for (var t in timers) { + Script.clearTimeout(timers[t]); + } + if(_qid != "Restarting") { + npcRespondBlocking( + 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/EarlyExit_0' + (Math.floor(Math.random() * 2) + 1).toString() + '.wav', + 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/reversedSphinx.fbx', + function(){ + Avatar.startAnimation('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx', 0); + } + ); + } +} + +function main() { + storyURL = "https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Sphinx.json"; + Messages.messageReceived.connect(function (channel, message, sender) { + if(!strContains(message, 'beat')) + print(sender + " -> NPC @" + Agent.sessionUUID + ": " + message); + if (channel === "interactionComs" && strContains(message, Agent.sessionUUID)) { + if (strContains(message, 'beat')) { + if(heartbeatTimeout) { + Script.clearTimeout(heartbeatTimeout); + heartbeatTimeout = false; + } + heartbeatTimeout = Script.setTimeout(endInteraction, 1500); + } + else if (strContains(message, "onFocused") && !currentlyEngaged) { + blocked = false; + currentlyEngaged = true; + currentlyUsedIndices = []; + doActionFromServer("start"); + } else if (strContains(message, "leftArea")) { + + } else if (strContains(message, "speaking")) { + + } else { + var voiceDataIndex = message.search("voiceData"); + if (voiceDataIndex != -1) { + var words = message.substr(voiceDataIndex+10); + if (!isNaN(_qid) && (strContains(words, "repeat") || (strContains(words, "say") && strContains(words, "again")))) { + doActionFromServer("init"); + } else { + doActionFromServer("words", words); + } + } + } + } + }); + // Script.update.connect(updateGem); + Avatar.startAnimation("https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx", 0); +} diff --git a/unpublishedScripts/interaction/Sphinx.json b/unpublishedScripts/interaction/Sphinx.json new file mode 100644 index 0000000000..2a76417fd7 --- /dev/null +++ b/unpublishedScripts/interaction/Sphinx.json @@ -0,0 +1,159 @@ +{ + "Name": "10 Questions", + "Defaults": + { + "Actions": + { + "positive": "var x=function(){if(questionNumber>=2){setQid('Finished');return;}var suffix=['A', 'B'][questionNumber++] + '_0' + (Math.floor(Math.random() * 2) + 2).toString() + '.wav';npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/RightAnswer'+suffix, 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/RightAnswerB_02.fbx', getRandomRiddle());};x();", + "unknown": "var suffix=(Math.floor(Math.random() * 3) + 1).toString();npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/WrongAnswer_0' + suffix + '.wav','https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/WrongAnswer_0' + suffix + '.fbx', getRandomRiddle());", + "hint": "var suffix=(Math.floor(Math.random() * 2) + 1).toString();npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Hint_0' + suffix + '.wav','https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hint_0' + suffix + '.fbx')" + }, + "Responses": + { + "positive": ["yes","yup","yeah","yahoo","sure","affirmative","okay","aye","right","exactly","course","naturally","unquestionably","positively","yep","definitely","certainly","fine","absolutely","positive","love","fantastic"], + "thinking": ["oh", "think about", "i know", "what was", "well", "not sure", "one before", "hold", "one moment", "one second", "1 second", "1 sec", "one sec"], + "hint": ["hint", "heads"] + } + }, + "Story": + [ + { + "QID": "start", + "init": "questionNumber=0;npcContinueStory('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/HiFi_Sphinx_Anim_Combined_Entrance_Audio.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx', getRandomRiddle());" + }, + { + "QID": "1", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Blackboard.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Blackboard.fbx');", + "responses": + { + "positive": ["blackboard", "chalkboard", "chalk board", "slate"] + } + }, + { + "QID": "2", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Breath.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Breath.fbx');", + "responses": + { + "positive": ["breath", "death"] + } + }, + { + "QID": "3", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Clock.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Clock.fbx');", + "responses": + { + "positive": ["clock", "cock"] + } + }, + { + "QID": "4", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Coffin.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Coffin.fbx');", + "responses": + { + "positive": ["coffin", "casket", "possum"] + } + }, + { + "QID": "5", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Coin.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Coin.fbx');", + "responses": + { + "positive": ["coin", "boing", "coinage", "coin piece", "change", "join"] + } + }, + { + "QID": "6", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Corn.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Corn.fbx');", + "responses": + { + "positive": ["corn", "born", "maize", "maze", "means", "torn", "horn", "worn", "porn"] + } + }, + { + "QID": "7", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Darkness.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Darkness.fbx');", + "responses": + { + "positive": ["darkness", "dark", "blackness"] + } + }, + { + "QID": "8", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Gloves.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Gloves.fbx');", + "responses": + { + "positive": ["gloves", "love"] + } + }, + { + "QID": "9", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Gold.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Gold.fbx');", + "responses": + { + "positive": ["gold", "old", "bold", "cold", "told"] + } + }, + { + "QID": "10", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_River.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_River.fbx');", + "responses": + { + "positive": ["river", "bigger", "stream", "creek", "brook"] + } + }, + { + "QID": "11", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Secret.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Secret.fbx');", + "responses": + { + "positive": ["secret"] + } + }, + { + "QID": "12", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Shadow.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Shadow.fbx');", + "responses": + { + "positive": ["shadow"] + } + }, + { + "QID": "13", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Silence.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Silence.fbx');", + "responses": + { + "positive": ["silence", "lance", "quiet"] + } + }, + { + "QID": "14", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Stairs.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Stairs.fbx');", + "responses": + { + "positive": ["stairs", "steps", "stair", "stairwell", "there's", "stairway"] + } + }, + { + "QID": "15", + "init": "npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/Riddle_Umbrella.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Riddle_Umbrella.fbx');", + "responses": + { + "positive": ["umbrella"] + } + }, + { + "QID": "Finished", + "init": "Script.clearTimeout(heartbeatTimeout);heartbeatTimeout = false;npcRespondBlocking('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/ScratchDialogue/ConclusionRight_02.wav', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/ConclusionRight_02.fbx', function(){runOnClient('MyAvatar.goToLocation({x: 5, y: -29, z: -63}, true, true);');setQid('Restarting');});", + "positive": "", + "negative": "", + "unknown": "" + }, + { + "QID": "Restarting", + "init": "npcRespondBlocking('', 'https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/reversedSphinx.fbx', function(){Avatar.startAnimation('https://storage.googleapis.com/limitlessserv-144100.appspot.com/hifi%20assets/Animation/Hifi_Sphinx_Anim_Entrance_Kneel_Combined_with_Intro.fbx', 0);_qid='';});", + "positive": "", + "negative": "", + "unknown": "" + } + ] +}