diff --git a/public/assets.json b/public/assets.json
deleted file mode 100644
index e69de29..0000000
diff --git a/public/index.html b/public/index.html
deleted file mode 100644
index b9c2474..0000000
--- a/public/index.html
+++ /dev/null
@@ -1,203 +0,0 @@
-
-
-
-
-
-
diff --git a/public/questions/vote-config.yaml b/public/questions/vote-config.yaml
index 027e05e..190751c 100644
--- a/public/questions/vote-config.yaml
+++ b/public/questions/vote-config.yaml
@@ -29,4 +29,4 @@ questions:
option-2: "Long long long answer that's just very very long just fine fine fine just fine fine fine and then some more"
option-3: "Long long long answer that's just very very long just fine fine fine just fine fine fine and then some more"
option-4: "Long long long answer that's just very very long just fine fine fine just fine fine fine and then some more"
- correct-option: 3
\ No newline at end of file
+ correct-option: 2
\ No newline at end of file
diff --git a/src/aframe-scene.js b/src/aframe-scene.js
index 16ba41b..abe3551 100644
--- a/src/aframe-scene.js
+++ b/src/aframe-scene.js
@@ -6,9 +6,10 @@ import './components/bubble';
// import './components/floating-camera';
// import './components/custom-controls';
import './components/gradient';
-// import './components/simple-navmesh';
-// import './components/surface-placer';
import './components/ocean-shader';
+import 'js-yaml';
+import 'loglevel';
+import 'lit-html';
import 'aframe-orbit-controls';
import 'aframe-extras';
diff --git a/src/reveal-presentation.js b/src/reveal-presentation.js
index c32b856..61fc5e6 100644
--- a/src/reveal-presentation.js
+++ b/src/reveal-presentation.js
@@ -9,20 +9,20 @@ Reveal.initialize({
progress: false,
controls: true
}).then(() => {
- $(document).ready(
- function(){
- $('body').on('submit','form',function() {
- var sessionCode = $('#session_code')[0].value;
- $("section[data-microsquad]").each(
- function(){
- var iframeUrl = new URL( $(this).data("background-iframe") );
- iframeUrl.searchParams.set("sc", sessionCode);
- $(this).data("background-iframe",iframeUrl.href);
- console.log("Now "+$(this).data("background-iframe"));
- }
- );
- return false;
- });
- }
- );
+ // $(document).ready(
+ // function(){
+ // // $('body').on('submit','form',function() {
+ // // var sessionCode = $('#session_code')[0].value;
+ // // $("section[data-microsquad]").each(
+ // // function(){
+ // // var iframeUrl = new URL( $(this).data("background-iframe") );
+ // // iframeUrl.searchParams.set("sc", sessionCode);
+ // // $(this).data("background-iframe",iframeUrl.href);
+ // // console.log("Now "+$(this).data("background-iframe"));
+ // // }
+ // // );
+ // // return false;
+ // // });
+ // }
+ // );
});
diff --git a/src/scene-quizz/QuizzSceneController.js b/src/scene-quizz/QuizzSceneController.js
index bfb4c86..6a2995a 100644
--- a/src/scene-quizz/QuizzSceneController.js
+++ b/src/scene-quizz/QuizzSceneController.js
@@ -40,6 +40,9 @@ class QuizzSceneController {
this.questionParentElement.appendChild(this.questionDisplayElement);
this.emoji = '🎁';
+
+ this.revealButton = null;
+ this.playerAnswers = new Map();
}
setupLogging() {
@@ -67,13 +70,12 @@ class QuizzSceneController {
}
}
-
- rule ProcessTerminalVoteUpdate {
+ rule ProcessPlayerVote {
when {
- update: PropertyUpdate update.deviceId.startsWith('terminal-') && update.nodeId === 'vote' && update.propertyId === 'option'
+ update: PropertyUpdate update.deviceId.startsWith('terminal-') && update.nodeId === 'vote' && update.propertyId === 'option' && update.value.trim().length != 0
}
then {
- handleTerminalVoteUpdate(update);
+ handlePlayerVote(update);
}
}
`, {
@@ -84,7 +86,7 @@ class QuizzSceneController {
scope: {
handlePropertyUpdate: this.handlePropertyUpdate.bind(this),
handleVoteUpdate: this.handleVoteUpdate.bind(this),
- handleTerminalVoteUpdate: this.handleTerminalVoteUpdate.bind(this),
+ handlePlayerVote: this.handlePlayerVote.bind(this),
logger: console
},
name: "quizz"
@@ -149,24 +151,153 @@ class QuizzSceneController {
this.currentVote.properties[update.propertyId] = update.value;
+ if (update.propertyId === 'correct-option') {
+ this.currentVote.correctOption = update.value;
+ }
+
+
if (update.propertyId === 'question-statement' ||
update.propertyId === 'option-1' ||
update.propertyId === 'option-2' ||
update.propertyId === 'option-3' ||
update.propertyId === 'option-4') {
this.updateQuestionDisplay();
+ this.createRevealButton();
}
log.debug(`Updating vote property: ${update.deviceId}/${update.propertyId} = ${update.value}`);
}
+ handlePlayerVote(update) {
+ const terminalId = update.deviceId.split('-')[1];
+ const playerNodeId = this.terminalToPlayerMap.get(terminalId);
+ if (playerNodeId) {
+ this.playerAnswers.set(playerNodeId, update.value);
+ }
+ this.handleTerminalVoteUpdate(update);
+ }
+
+ createRevealButton() {
+ if (!this.revealButton) {
+ this.revealButton = document.createElement('button');
+ this.revealButton.id = 'mode-toggle';
+ this.revealButton.style.position = 'absolute';
+ this.revealButton.style.top = '10px';
+ this.revealButton.style.left = '10px';
+ this.revealButton.style.zIndex = '1000';
+ this.revealButton.textContent = 'Reveal Results';
+ this.revealButton.addEventListener('click', () => this.revealResults());
+ document.body.appendChild(this.revealButton);
+ }
+ }
+
+ createNextButton() {
+ if (!this.nextButton) {
+ this.nextButton = document.createElement('button');
+ this.nextButton.id = 'next-button';
+ this.nextButton.style.position = 'absolute';
+ this.nextButton.style.top = '10px';
+ this.nextButton.style.left = '120px';
+ this.nextButton.style.zIndex = '1000';
+ this.nextButton.textContent = 'Next Question';
+ this.nextButton.style.display = 'none';
+ this.nextButton.addEventListener('click', () => this.prepareNextQuestion());
+ document.body.appendChild(this.nextButton);
+ }
+ }
+
+
+ revealResults() {
+ if (this.currentVote && this.currentVote.correctOption) {
+ this.highlightCorrectAnswer();
+ this.updatePlayerFeedback();
+ this.showNextButton();
+ }
+ }
+
+ showNextButton() {
+ if (this.nextButton) {
+ this.nextButton.style.display = 'block';
+ } else {
+ this.createNextButton();
+ this.nextButton.style.display = 'block';
+ }
+ }
+
+ prepareNextQuestion() {
+ this.resetPlayerAnimations();
+ this.clearPlayerVotes();
+ this.clearAllEmojis();
+ this.hideNextButton();
+ this.clearQuestionDisplay();
+ }
+
+ clearAllEmojis() {
+ this.playerSayEntities.forEach((sayEntity, playerNodeId) => {
+ if (sayEntity) {
+ sayEntity.setAttribute('visible', 'false');
+ render(html``, sayEntity); // Clear the content of the say entity
+ }
+ });
+ log.debug('Cleared all player emojis');
+ }
+
+ resetPlayerAnimations() {
+ this.players.forEach((player, nodeId) => {
+ this.updatePlayerAnimationLocally(nodeId, "clip: Idle; loop: repeat");
+ });
+ }
+
+ clearPlayerVotes() {
+ this.terminalToPlayerMap.forEach((playerNodeId, terminalId) => {
+ this.clearTerminalVote(terminalId);
+ });
+ this.playerAnswers.clear();
+ }
+
+ clearTerminalVote(terminalId) {
+ const deviceId = `terminal-${terminalId}`;
+ const nodeId = 'vote';
+ const propertyId = 'option';
+ const value = undefined;
+
+ // Publish MQTT message to clear the vote
+ this.homieObserver.publish(`${deviceId}/${nodeId}/${propertyId}`, value);
+ }
+
+ hideNextButton() {
+ if (this.nextButton) {
+ this.nextButton.style.display = 'none';
+ }
+ }
+
+ clearQuestionDisplay() {
+ render(html``, this.questionDisplayElement);
+ this.currentVote = null;
+ }
+
+ highlightCorrectAnswer() {
+ const correctOptionIndex = parseInt(this.currentVote.correctOption) - 1;
+ const updatedTemplate = questionDisplayTemplate(
+ this.currentVote.properties['question-statement'],
+ [
+ this.currentVote.properties['option-1'],
+ this.currentVote.properties['option-2'],
+ this.currentVote.properties['option-3'],
+ this.currentVote.properties['option-4']
+ ],
+ correctOptionIndex
+ );
+ render(updatedTemplate, this.questionDisplayElement);
+ }
+
handleTerminalVoteUpdate(update) {
const terminalId = update.deviceId.split('-')[1]; // remove the "terminal-" prefix
log.debug(`Received vote from terminal: ${terminalId}, option: ${update.value}`);
this.displayEmoji(terminalId);
}
-displayEmoji(terminalId) {
+ displayEmoji(terminalId) {
const playerNodeId = this.terminalToPlayerMap.get(terminalId);
if (playerNodeId) {
@@ -177,6 +308,7 @@ displayEmoji(terminalId) {
}
}
+
renderEmojiEntity(playerEntity, playerNodeId) {
const template = this.createEmojiTemplate(playerNodeId);
@@ -214,6 +346,67 @@ displayEmoji(terminalId) {
`;
}
+ updatePlayerFeedback() {
+ this.playerAnswers.forEach((answer, playerNodeId) => {
+ const isCorrect = ((parseInt(answer)+1) === parseInt(this.currentVote.correctOption));
+ const emoji = isCorrect ? '🟩' : '❌';
+ this.updatePlayerEmoji(playerNodeId, emoji);
+
+ if (!isCorrect) {
+ this.updatePlayerAnimationLocally(playerNodeId, "clip: Death; loop: once; clampWhenFinished: true");
+ setTimeout(() => {
+ this.updatePlayerAnimationLocally(playerNodeId, "clip: Idle; loop: repeat");
+ }, 5000); // Reset to Idle after 5 seconds
+ }
+ });
+ }
+
+
+ updatePlayerEmoji(playerNodeId, emoji) {
+ const playerEntity = this.teamParentElement.querySelector(`#${playerNodeId}`);
+ if (playerEntity) {
+ const template = html`
+
+
+ ${emoji}
+
+
+ `;
+ let sayEntity = this.playerSayEntities.get(playerNodeId);
+ if (!sayEntity) {
+ sayEntity = document.createElement('a-entity');
+ sayEntity.setAttribute('id', `${playerNodeId}-say`);
+ playerEntity.appendChild(sayEntity);
+ this.playerSayEntities.set(playerNodeId, sayEntity);
+ }
+ render(template, sayEntity);
+ sayEntity.setAttribute('visible', 'true');
+ }
+ }
+
+ updatePlayerAnimationLocally(playerNodeId, animationValue) {
+ const playerEntity = this.teamParentElement.querySelector(`#${playerNodeId}`);
+ if (playerEntity) {
+ playerEntity.setAttribute('animation-mixer', animationValue);
+ log.debug(`Updated animation for player ${playerNodeId}: ${animationValue}`);
+ }
+ }
+
+ updatePlayerAnimation(player) {
+ const playerEntity = this.teamParentElement.querySelector(`#${player.nodeId}`);
+ if (playerEntity) {
+ playerEntity.setAttribute('animation-mixer', player.properties['animation-mixer']);
+ log.debug(`Updated animation for player ${player.nodeId}: ${player.properties['animation-mixer']}`);
+ }
+ }
+
+
+
updateQuestionDisplay() {
if (this.currentVote &&
this.currentVote.properties['question-statement'] &&
@@ -237,14 +430,6 @@ displayEmoji(terminalId) {
}
}
- updatePlayerAnimation(player) {
- const playerEntity = this.parentElement.querySelector(`#${player.nodeId}`);
- if (playerEntity) {
- playerEntity.setAttribute('animation-mixer', player.properties['animation-mixer']);
- log.debug(`Updated animation for player ${player.nodeId}: ${player.properties['animation-mixer']}`);
- }
- }
-
updateTerminalToPlayerMap(player, terminalId) {
for (let [key, value] of this.terminalToPlayerMap) {
if (value === player.nodeId) {
diff --git a/src/scene-quizz/questionDisplayTemplate.js b/src/scene-quizz/questionDisplayTemplate.js
index 9ce068e..c44789f 100644
--- a/src/scene-quizz/questionDisplayTemplate.js
+++ b/src/scene-quizz/questionDisplayTemplate.js
@@ -1,22 +1,27 @@
import { html } from 'lit-html';
-export const questionDisplayTemplate = (question, options) => html`
+export const questionDisplayTemplate = (question, options, correctOptionIndex = null) => html`
- ${options.map((option, index) => html`
-
-
-
-
+ ${options.map((option, index) => {
+ const isCorrect = correctOptionIndex !== null && index === correctOptionIndex;
+ const planeColor = isCorrect ? "#00FF00" : "#333333";
+ const textColor = isCorrect ? "#000000" : "#FFFFFF";
+ return html`
+
+
+
+
-
-
-
- `)}
+
+
+
+ `;
+ })}
`;
\ No newline at end of file
diff --git a/webpack.config.js b/webpack.config.js
index da1256d..b7f1eb3 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -7,6 +7,7 @@ module.exports = {
aframe: './src/aframe-scene.js',
tutorial: './src/tutorial-scene.js',
quizz: './src/quizz-scene.js',
+ gallery: './src/gallery-scene.js',
presentation: './src/reveal-presentation.js',
voteAggregator: './src/vote-aggregator.js'
},