content/hifi-content/rebecca/Trivia/dev/trivia.js
2022-02-14 02:04:11 +01:00

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({
'&': '&',
'>': '>',
'&lt;': '<',
'&quot;': '"',
'&#39;': "'",
'&lsquo;': '"',
'&rsquo;': '"',
'&ldquo;': '"',
'&rdquo;': '"',
'&eacute;': 'e',
'&prime;': '\'',
'&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);
}());