685 lines
26 KiB
JavaScript
685 lines
26 KiB
JavaScript
//
|
|
// trivia.js
|
|
//
|
|
// Created by Rebecca Stankus on 06/11/18
|
|
// Copyright 2018 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
|
|
|
|
/* global EventBridge */
|
|
|
|
(function() {
|
|
var TRIVIA_CHANNEL = "TriviaChannel";
|
|
var TABLET_BUTTON_IMAGE = Script.resolvePath('assets/icons/questionMark-i.png');
|
|
var TABLET_BUTTON_PRESSED = Script.resolvePath('assets/icons/questionMark-a.png');
|
|
var TIMER_SOUND = SoundCache.getSound(Script.resolvePath('assets/sounds/timer.wav'));
|
|
var SEARCH_RADIUS = 100;
|
|
var ONE_SECOND_MS = 1000;
|
|
var TEN_SECONDS_MS = 10000;
|
|
var ZONE_COLOR_INDEX = 19;
|
|
var HALF_MULTIPLIER = 0.5;
|
|
var FIRST_WAIT_TO_COUNT_AVATARS = 1000;
|
|
var SECOND_WAIT_TO_COUNT_AVATARS = 3000;
|
|
var WAIT_TO_SHOW_QUESTION = 500;
|
|
|
|
var audioVolume = 0.7;
|
|
var tablet = Tablet.getTablet('com.highfidelity.interface.tablet.system');
|
|
var appPage = Script.resolvePath('trivia.html?006');
|
|
var button = tablet.addButton({
|
|
text: 'TRIVIA',
|
|
icon: TABLET_BUTTON_IMAGE,
|
|
activeIcon: TABLET_BUTTON_PRESSED
|
|
});
|
|
var open = false;
|
|
var injector;
|
|
var questionText;
|
|
var choiceTexts = [];
|
|
var answerText;
|
|
var triviaData;
|
|
var request = Script.require('./modules/request.js').request;
|
|
var type = null;
|
|
var category = null;
|
|
var difficulty = null;
|
|
var currentChoices = [];
|
|
var lights = [];
|
|
var correctHighlights = [];
|
|
var timer;
|
|
var playerCounterZone;
|
|
var playerCounterZoneProperties;
|
|
var avatarCounter;
|
|
var bubble;
|
|
|
|
var htmlEnDeCode = (function() {
|
|
var charToEntityRegex,
|
|
entityToCharRegex,
|
|
charToEntity,
|
|
entityToChar;
|
|
|
|
function resetCharacterEntities() {
|
|
charToEntity = {};
|
|
entityToChar = {};
|
|
// add the default set
|
|
addCharacterEntities({
|
|
'&': '&',
|
|
'>': '>',
|
|
'<': '<',
|
|
'"': '"',
|
|
''': "'",
|
|
'‘': '"',
|
|
'’': '"',
|
|
'“': '"',
|
|
'”': '"',
|
|
'é': 'e',
|
|
'′': '\'',
|
|
'&Prime': '"'
|
|
});
|
|
}
|
|
|
|
function addCharacterEntities(newEntities) {
|
|
var charKeys = [],
|
|
entityKeys = [],
|
|
key, encodedChar;
|
|
for (key in newEntities) {
|
|
encodedChar = newEntities[key];
|
|
entityToChar[key] = encodedChar;
|
|
charToEntity[encodedChar] = key;
|
|
charKeys.push(encodedChar);
|
|
entityKeys.push(key);
|
|
}
|
|
charToEntityRegex = new RegExp('(' + charKeys.join('|') + ')', 'g');
|
|
entityToCharRegex = new RegExp('(' + entityKeys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');
|
|
}
|
|
|
|
function htmlEncode(value) {
|
|
var htmlEncodeReplace = function(match, capture) {
|
|
return charToEntity[capture];
|
|
};
|
|
|
|
return (!value) ? value : String(value).replace(charToEntityRegex, htmlEncodeReplace);
|
|
}
|
|
|
|
function htmlDecode(value) {
|
|
var htmlDecodeReplaceFn = function(match, capture) {
|
|
return (capture in entityToChar) ? entityToChar[capture] : String.fromCharCode(parseInt(capture.substr(2), 10));
|
|
};
|
|
|
|
return (!value) ? value : String(value).replace(entityToCharRegex, htmlDecodeReplaceFn);
|
|
}
|
|
|
|
resetCharacterEntities();
|
|
|
|
return {
|
|
htmlEncode: htmlEncode,
|
|
htmlDecode: htmlDecode
|
|
};
|
|
})();
|
|
|
|
function begin() {
|
|
findTargets();
|
|
clearGame();
|
|
lights.forEach(function(light) {
|
|
Entities.editEntity(light, { locked: false });
|
|
Entities.editEntity(light, { visible: true });
|
|
Entities.editEntity(light, { locked: true });
|
|
});
|
|
bubbleOn();
|
|
updateAvatarCounter();
|
|
}
|
|
|
|
function bubbleOn() {
|
|
Entities.editEntity(bubble, { locked: false });
|
|
Entities.editEntity(bubble, {
|
|
visible: true,
|
|
collisionless: false,
|
|
collidesWith: "static,dynamic,kinematic,myAvatar,otherAvatar"
|
|
});
|
|
Entities.editEntity(bubble, { locked: true });
|
|
}
|
|
|
|
function bubbleOff() {
|
|
Entities.editEntity(bubble, { locked: false });
|
|
Entities.editEntity(bubble, {
|
|
visible: false,
|
|
collidesWith: "static,dynamic,kinematic"
|
|
});
|
|
Entities.editEntity(bubble, { locked: true });
|
|
}
|
|
|
|
function findTargets() {
|
|
Entities.findEntities(MyAvatar.position, SEARCH_RADIUS).forEach(function(element) {
|
|
var name = Entities.getEntityProperties(element, ['name']).name;
|
|
if (name.indexOf("Trivia") !== -1) {
|
|
if (name.indexOf("Light") !== -1) {
|
|
lights.push(element);
|
|
} else if (name.indexOf("Correct") !== -1) {
|
|
correctHighlights.push(element);
|
|
} else {
|
|
switch (name) {
|
|
case "Trivia Question":
|
|
questionText = element;
|
|
break;
|
|
case "Trivia Answer":
|
|
answerText = element;
|
|
break;
|
|
case "Trivia Choice Text Green":
|
|
choiceTexts[0] = element;
|
|
break;
|
|
case "Trivia Choice Text Yellow":
|
|
choiceTexts[1] = element;
|
|
break;
|
|
case "Trivia Choice Text Red":
|
|
choiceTexts[2] = element;
|
|
break;
|
|
case "Trivia Choice Text Blue":
|
|
choiceTexts[3] = element;
|
|
break;
|
|
case "Trivia Timer Countdown":
|
|
timer = element;
|
|
break;
|
|
case "Trivia Avatar Counter Total":
|
|
avatarCounter = element;
|
|
break;
|
|
case "Trivia Player Counter Zone":
|
|
playerCounterZone = element;
|
|
playerCounterZoneProperties = Entities.getEntityProperties(playerCounterZone,
|
|
["position", "dimensions", "rotation", "userData"]);
|
|
break;
|
|
case "Trivia Bubble":
|
|
bubble = element;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function onClicked() {
|
|
if (open) {
|
|
tablet.gotoHomeScreen();
|
|
} else {
|
|
tablet.gotoWebScreen(appPage);
|
|
}
|
|
}
|
|
|
|
function getQuestion() {
|
|
try {
|
|
var triviaURL = "https://opentdb.com/api.php?amount=1";
|
|
if (type) {
|
|
triviaURL = triviaURL + "&type=" + type;
|
|
}
|
|
if (difficulty) {
|
|
triviaURL = triviaURL + "&difficulty=" + difficulty;
|
|
}
|
|
if (category) {
|
|
triviaURL = triviaURL + "&category=" + category;
|
|
}
|
|
request(triviaURL, function (error, data) {
|
|
if (!error) {
|
|
tablet.emitScriptEvent(JSON.stringify(data.results[0]));
|
|
triviaData = data.results;
|
|
}
|
|
});
|
|
} catch (err) {
|
|
print("Could not get domain data using userData domainAPIURL");
|
|
}
|
|
}
|
|
|
|
function shuffle(array) {
|
|
var currentIndex = array.length, temporaryValue, randomIndex;
|
|
|
|
while (currentIndex !== 0) {
|
|
randomIndex = Math.floor(Math.random() * currentIndex);
|
|
currentIndex -= 1;
|
|
temporaryValue = array[currentIndex];
|
|
array[currentIndex] = array[randomIndex];
|
|
array[randomIndex] = temporaryValue;
|
|
}
|
|
return array;
|
|
}
|
|
|
|
function showQuestion() {
|
|
clearBoard();
|
|
Script.setTimeout(function() {
|
|
choiceTexts.forEach(function(choice) {
|
|
Entities.editEntity(choice, { locked: false });
|
|
Entities.editEntity(choice, {
|
|
text: "",
|
|
visible: true
|
|
});
|
|
Entities.editEntity(choice, { locked: true });
|
|
});
|
|
var formattedQuestion = htmlEnDeCode.htmlDecode(triviaData[0].question);
|
|
|
|
Entities.editEntity(questionText, { locked: false });
|
|
Entities.editEntity(questionText, { text: formattedQuestion });
|
|
Entities.editEntity(questionText, { locked: true });
|
|
|
|
Entities.editEntity(answerText, { locked: false });
|
|
Entities.editEntity(answerText, {
|
|
text: "",
|
|
visible: false
|
|
});
|
|
Entities.editEntity(answerText, { locked: true });
|
|
|
|
Messages.sendMessage(TRIVIA_CHANNEL, JSON.stringify({
|
|
type: "newQuestion"
|
|
}));
|
|
}, WAIT_TO_SHOW_QUESTION);
|
|
}
|
|
|
|
function showAnswers() {
|
|
if (triviaData[0].type === "boolean") {
|
|
Entities.editEntity(choiceTexts[0], { locked: false });
|
|
Entities.editEntity(choiceTexts[0], {
|
|
text: "",
|
|
visible: false
|
|
});
|
|
Entities.editEntity(choiceTexts[0], { locked: true });
|
|
|
|
Entities.editEntity(choiceTexts[1], { locked: false });
|
|
Entities.editEntity(choiceTexts[1], {
|
|
text: "True",
|
|
visible: true
|
|
});
|
|
Entities.editEntity(choiceTexts[1], { locked: true });
|
|
|
|
Entities.editEntity(choiceTexts[2], { locked: false });
|
|
Entities.editEntity(choiceTexts[2], {
|
|
text: "False",
|
|
visible: true
|
|
});
|
|
Entities.editEntity(choiceTexts[2], { locked: true });
|
|
|
|
Entities.editEntity(choiceTexts[3], { locked: false });
|
|
Entities.editEntity(choiceTexts[3], {
|
|
text: "",
|
|
visible: false
|
|
});
|
|
Entities.editEntity(choiceTexts[3], { locked: true });
|
|
|
|
currentChoices = [];
|
|
currentChoices.push("True");
|
|
currentChoices.push("False");
|
|
lights.forEach(function(light) {
|
|
var lightName = Entities.getEntityProperties(light, 'name').name;
|
|
if (lightName.indexOf("Green") !== -1) {
|
|
Entities.editEntity(light, { locked: false });
|
|
Entities.editEntity(light, { visible: false });
|
|
Entities.editEntity(light, { locked: true });
|
|
} else if (lightName.indexOf("Blue") !== -1) {
|
|
Entities.editEntity(light, { locked: false });
|
|
Entities.editEntity(light, { visible: false });
|
|
Entities.editEntity(light, { locked: true });
|
|
}
|
|
});
|
|
} else {
|
|
currentChoices = [];
|
|
currentChoices.push(htmlEnDeCode.htmlDecode(triviaData[0].correct_answer));
|
|
triviaData[0].incorrect_answers.forEach(function(choice) {
|
|
currentChoices.push(htmlEnDeCode.htmlDecode(choice));
|
|
});
|
|
shuffle(currentChoices);
|
|
currentChoices.forEach(function(choice, index) {
|
|
Entities.editEntity(choiceTexts[index], { locked: false });
|
|
Entities.editEntity(choiceTexts[index], {
|
|
text: choice,
|
|
visible: true
|
|
});
|
|
Entities.editEntity(choiceTexts[index], { locked: true });
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
function clearGame() {
|
|
bubbleOff();
|
|
lights.forEach(function(light) {
|
|
Entities.editEntity(light, { locked: false });
|
|
Entities.editEntity(light, { visible: false });
|
|
Entities.editEntity(light, { locked: true });
|
|
});
|
|
|
|
correctHighlights.forEach(function(highlight) {
|
|
Entities.editEntity(highlight, { locked: false });
|
|
Entities.editEntity(highlight, { visible: false });
|
|
Entities.editEntity(highlight, { locked: true });
|
|
});
|
|
|
|
Entities.editEntity(questionText, { locked: false });
|
|
Entities.editEntity(questionText, { text: "Questions will appear here" });
|
|
Entities.editEntity(questionText, { locked: true });
|
|
|
|
choiceTexts.forEach(function(choice) {
|
|
Entities.editEntity(choice, { locked: false });
|
|
Entities.editEntity(choice, {
|
|
text: "Answers appear here",
|
|
visible: true
|
|
});
|
|
Entities.editEntity(choice, { locked: true });
|
|
});
|
|
|
|
Entities.editEntity(answerText, { locked: false });
|
|
Entities.editEntity(answerText, {
|
|
text: "",
|
|
visible: false
|
|
});
|
|
Entities.editEntity(answerText, { locked: true });
|
|
|
|
Entities.editEntity(avatarCounter, { locked: false });
|
|
Entities.editEntity(avatarCounter, { text: 0});
|
|
Entities.editEntity(avatarCounter, { locked: true });
|
|
}
|
|
|
|
function isPositionInsideBox(position) {
|
|
var localPosition = Vec3.multiplyQbyV(Quat.inverse(playerCounterZoneProperties.rotation),
|
|
Vec3.subtract(position, playerCounterZoneProperties.position));
|
|
var halfDimensions = Vec3.multiply(playerCounterZoneProperties.dimensions, HALF_MULTIPLIER);
|
|
return -halfDimensions.x <= localPosition.x &&
|
|
halfDimensions.x >= localPosition.x &&
|
|
-halfDimensions.y <= localPosition.y &&
|
|
halfDimensions.y >= localPosition.y &&
|
|
-halfDimensions.z <= localPosition.z &&
|
|
halfDimensions.z >= localPosition.z;
|
|
}
|
|
|
|
function usersInZone() {
|
|
var count = 0;
|
|
AvatarList.getAvatarIdentifiers().forEach(function(avatarID) {
|
|
var avatar = AvatarList.getAvatar(avatarID);
|
|
if (avatar.sessionUUID) {
|
|
if (isPositionInsideBox(avatar.position, playerCounterZoneProperties)) {
|
|
count++;
|
|
}
|
|
}
|
|
});
|
|
return count;
|
|
}
|
|
|
|
function updateAvatarCounter() {
|
|
var count = usersInZone(playerCounterZoneProperties);
|
|
Entities.editEntity(avatarCounter, { locked: false });
|
|
Entities.editEntity(avatarCounter, { text: count});
|
|
Entities.editEntity(avatarCounter, { locked: true });
|
|
}
|
|
|
|
function clearBoard() {
|
|
lights.forEach(function(light) {
|
|
Entities.editEntity(light, { locked: false });
|
|
Entities.editEntity(light, { visible: true });
|
|
Entities.editEntity(light, { locked: true });
|
|
});
|
|
|
|
correctHighlights.forEach(function(highlight) {
|
|
Entities.editEntity(highlight, { locked: false });
|
|
Entities.editEntity(highlight, { visible: false });
|
|
Entities.editEntity(highlight, { locked: true });
|
|
});
|
|
|
|
Entities.editEntity(questionText, { locked: false });
|
|
Entities.editEntity(questionText, { text: "" });
|
|
Entities.editEntity(questionText, { locked: true });
|
|
|
|
choiceTexts.forEach(function(choice) {
|
|
Entities.editEntity(choice, { locked: false });
|
|
Entities.editEntity(choice, {
|
|
text: "",
|
|
visible: true
|
|
});
|
|
Entities.editEntity(choice, { locked: true });
|
|
});
|
|
|
|
Entities.editEntity(answerText, { locked: false });
|
|
Entities.editEntity(answerText, {
|
|
text: "",
|
|
visible: false
|
|
});
|
|
Entities.editEntity(answerText, { locked: true });
|
|
}
|
|
|
|
function startTimer() {
|
|
playSound(TIMER_SOUND);
|
|
var seconds = 10;
|
|
Entities.editEntity(timer, { locked: false });
|
|
Entities.editEntity(timer, { text: JSON.stringify(seconds) });
|
|
Entities.editEntity(timer, { locked: true });
|
|
var interval = Script.setInterval(function() {
|
|
seconds--;
|
|
Entities.editEntity(timer, { locked: false });
|
|
Entities.editEntity(timer, { text: JSON.stringify(seconds) });
|
|
Entities.editEntity(timer, { locked: true });
|
|
if (seconds === 0) {
|
|
Script.clearInterval(interval);
|
|
}
|
|
}, ONE_SECOND_MS);
|
|
}
|
|
|
|
function showCorrect() {
|
|
var formattedAnswer = htmlEnDeCode.htmlDecode(triviaData[0].correct_answer);
|
|
var correctColor;
|
|
choiceTexts.forEach(function(textEntity) {
|
|
var properties = Entities.getEntityProperties(textEntity, ['name', 'text']);
|
|
if (properties.text === htmlEnDeCode.htmlDecode(triviaData[0].correct_answer)) {
|
|
var color = properties.name.substr(ZONE_COLOR_INDEX);
|
|
correctColor = color;
|
|
}
|
|
});
|
|
lights.forEach(function(light) {
|
|
var lightName = Entities.getEntityProperties(light, 'name').name;
|
|
if (lightName.indexOf(correctColor) === -1) {
|
|
Entities.editEntity(light, { locked: false });
|
|
Entities.editEntity(light, { visible: false });
|
|
Entities.editEntity(light, { locked: true });
|
|
}
|
|
});
|
|
correctHighlights.forEach(function(highlight) {
|
|
var highlightName = Entities.getEntityProperties(highlight, 'name').name;
|
|
if (highlightName.indexOf(correctColor) !== -1) {
|
|
Entities.editEntity(highlight, { locked: false });
|
|
Entities.editEntity(highlight, { visible: true });
|
|
Entities.editEntity(highlight, { locked: true });
|
|
}
|
|
});
|
|
Messages.sendMessage(TRIVIA_CHANNEL, JSON.stringify({
|
|
type: "check",
|
|
correct: correctColor
|
|
}));
|
|
|
|
Entities.editEntity(answerText, { locked: false });
|
|
Entities.editEntity(answerText, {
|
|
text: formattedAnswer,
|
|
visible: true
|
|
});
|
|
Entities.editEntity(answerText, { locked: true });
|
|
|
|
Script.setTimeout(function() {
|
|
updateAvatarCounter();
|
|
}, FIRST_WAIT_TO_COUNT_AVATARS);
|
|
Script.setTimeout(function() {
|
|
updateAvatarCounter();
|
|
}, SECOND_WAIT_TO_COUNT_AVATARS);
|
|
}
|
|
|
|
function onWebEventReceived(event) {
|
|
if (typeof event === 'string') {
|
|
event = JSON.parse(event);
|
|
if (event.app === 'trivia') {
|
|
switch (event.type) {
|
|
case 'begin':
|
|
begin();
|
|
break;
|
|
case 'end':
|
|
clearGame();
|
|
break;
|
|
case 'type':
|
|
switch (event.value) {
|
|
case "Any Type":
|
|
type = null;
|
|
break;
|
|
case "Multiple Choice":
|
|
type = "multiple";
|
|
break;
|
|
case "True or False":
|
|
type = "boolean";
|
|
break;
|
|
}
|
|
break;
|
|
case 'difficulty':
|
|
switch (event.value) {
|
|
case "Any Difficulty":
|
|
difficulty = null;
|
|
break;
|
|
case "Easy":
|
|
difficulty = "easy";
|
|
break;
|
|
case "Medium":
|
|
difficulty = "medium";
|
|
break;
|
|
case "Hard":
|
|
difficulty = "hard";
|
|
break;
|
|
}
|
|
break;
|
|
case 'category':
|
|
switch (event.value) {
|
|
case "Any Category":
|
|
category = null;
|
|
break;
|
|
case "General Knowledge":
|
|
category = "9";
|
|
break;
|
|
case "Books":
|
|
category = "10";
|
|
break;
|
|
case "Film":
|
|
category = "11";
|
|
break;
|
|
case "Music":
|
|
category = "12";
|
|
break;
|
|
case "Musicals and Theatres":
|
|
category = "13";
|
|
break;
|
|
case "Television":
|
|
category = "14";
|
|
break;
|
|
case "Video Games":
|
|
category = "15";
|
|
break;
|
|
case "Board Games":
|
|
category = "16";
|
|
break;
|
|
case "Comics":
|
|
category = "29";
|
|
break;
|
|
case "Japanese Anime and Manga":
|
|
category = "31";
|
|
break;
|
|
case "Cartoon and Animations":
|
|
category = "32";
|
|
break;
|
|
case "Computers":
|
|
category = "18";
|
|
break;
|
|
case "Mathematics":
|
|
category = "19";
|
|
break;
|
|
case "Gadgets":
|
|
category = "30";
|
|
break;
|
|
case "Art":
|
|
category = "25";
|
|
break;
|
|
case "Celebrities":
|
|
category = "26";
|
|
break;
|
|
case "Animals":
|
|
category = "27";
|
|
break;
|
|
case "Vehicles":
|
|
category = "28";
|
|
break;
|
|
case "Mythology":
|
|
category = "20";
|
|
break;
|
|
case "Sports":
|
|
category = "21";
|
|
break;
|
|
case "History":
|
|
category = "23";
|
|
break;
|
|
case "Geography":
|
|
category = "22";
|
|
break;
|
|
case "Politics":
|
|
category = "24";
|
|
break;
|
|
}
|
|
break;
|
|
case 'newQuestion':
|
|
getQuestion();
|
|
break;
|
|
case 'showQuestion':
|
|
showQuestion();
|
|
break;
|
|
case 'showAnswers':
|
|
showAnswers();
|
|
startTimer();
|
|
Script.setTimeout(function() {
|
|
Messages.sendMessage(TRIVIA_CHANNEL, JSON.stringify({type: "timeUp"}));
|
|
showCorrect();
|
|
}, TEN_SECONDS_MS);
|
|
break;
|
|
case 'showCorrect':
|
|
showCorrect();
|
|
break;
|
|
case 'clearBoard':
|
|
clearBoard();
|
|
break;
|
|
default:
|
|
print(JSON.stringify(event));
|
|
print("error in detecting event.type");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function onScreenChanged(type, url) {
|
|
open = (url === appPage);
|
|
button.editProperties({isActive: open});
|
|
}
|
|
|
|
function appEnding() {
|
|
Messages.unsubscribe(TRIVIA_CHANNEL);
|
|
clearGame();
|
|
button.clicked.disconnect(onClicked);
|
|
tablet.removeButton(button);
|
|
tablet.screenChanged.disconnect(onScreenChanged);
|
|
tablet.webEventReceived.disconnect(onWebEventReceived);
|
|
}
|
|
|
|
function playSound(sound) {
|
|
if (sound.downloaded) {
|
|
if (injector) {
|
|
injector.stop();
|
|
}
|
|
injector = Audio.playSound(sound, {
|
|
position: MyAvatar.position,
|
|
volume: audioVolume
|
|
});
|
|
}
|
|
}
|
|
|
|
this.unload = function() {
|
|
clearGame();
|
|
};
|
|
|
|
findTargets();
|
|
Messages.subscribe(TRIVIA_CHANNEL);
|
|
button.clicked.connect(onClicked);
|
|
tablet.screenChanged.connect(onScreenChanged);
|
|
tablet.webEventReceived.connect(onWebEventReceived);
|
|
clearGame();
|
|
|
|
Script.scriptEnding.connect(appEnding);
|
|
}());
|