content/hifi-content/Experiences/Releases/events/loveseat/scripts/setPiecesDirector.js
2022-02-13 23:16:46 +01:00

354 lines
12 KiB
JavaScript

/*!
setPiecesDirector.js
Created by David Rowe on 8 Aug 2019.
Copyright 2019 David Rowe.
Distributed under the Apache License, Version 2.0.
See: http://www.apache.org/licenses/LICENSE-2.0.html
*/
/* global Entities, OverlayWebWindow Vec3 */
(function () {
"use strict";
var APP_ICON_INACTIVE = Script.resolvePath("./assets/set-pieces-i.svg"),
APP_ICON_ACTIVE = Script.resolvePath("./assets/set-pieces-a.svg"),
APP_NAME = "SET PIECES",
HTML_FILE = "html/setPiecesDirector.html",
setPieces, // JSON data plus runtime augmentations.
/*
Note: When out, all entities specified in the JSON are set to dynamic=false and collisionless=false, unless these
properties are set to "true" in the JSON.
Note: When in, all entities specified in the JSON are set to dynamic=false and collisionless=true so as to reduce
the likelihood of their causing problems.
Note: For a group of entities that are parented to each other, you need only include the root entity in the JSON.
Note: In a group of entities that are parented to each other, the child entities currently do NOT have their
dynamic and collisionless properties changed or set when in or out.
Note: If you edit the JSON while Interface is running, you need to reload all scripts ("RELOAD ALL" button in the
Running Scripts dialog) in order to use the updated JSON.
*/
setPiecesIndexes = {}, // Entity ID to JSON index.
movingSetPieces = [], // Indexes in setPieces of those that are currently moving.
Dialog,
tablet = null,
button = null;
Dialog = (function () {
var WINDOW_TITLE = "Set Pieces Director",
WINDOW_DEFAULT_WIDTH = 400,
WINDOW_DEFAULT_HEIGHT = 600,
// Event bridge commands.
ADD_GROUP = "add-group", // Main script ==> Dialog
SET_BUTTON = "set-button", // Main script ==> Dialog
TOGGLE_BUTTON = "toggle-button", // Main script <== Dialog
SET_COUNTDOWN = "set-countdowm", // Main script ==> Dialog
dialogMessageCallback,
windowClosedCallback,
window,
isVisible = false;
function getVisible() {
return isVisible;
}
function setVisible(visible) {
window.setVisible(visible);
if (visible) {
window.raise();
}
isVisible = visible;
}
function toggleVisible() {
setVisible(!isVisible);
}
function onWindowClosed() {
isVisible = false;
windowClosedCallback();
}
function addGroup(id, group) {
window.emitScriptEvent(JSON.stringify({
command: ADD_GROUP,
id: id,
group: group
}));
}
function setButtonOut(id, isOut) {
window.emitScriptEvent(JSON.stringify({
command: SET_BUTTON,
id: id,
isOut: isOut
}));
}
function setCountDownValue(id, value) {
window.emitScriptEvent(JSON.stringify({
command: SET_COUNTDOWN,
id: id,
value: value
}));
}
function onWebEventReceived(message) {
var data;
data = JSON.parse(message);
switch (data.command) {
case TOGGLE_BUTTON:
dialogMessageCallback(data);
break;
default:
print("ERROR: Unexpected message! - " + message);
}
}
function setUp(theDialogMessageCallback, theWindowClosedCallback) {
var html,
size;
dialogMessageCallback = theDialogMessageCallback;
windowClosedCallback = theWindowClosedCallback;
html = Script.resolvePath(HTML_FILE);
size = {
width: WINDOW_DEFAULT_WIDTH,
height: WINDOW_DEFAULT_HEIGHT
};
window = new OverlayWebWindow(WINDOW_TITLE, html, size.width, size.height, false);
window.setVisible(false);
window.webEventReceived.connect(onWebEventReceived);
window.closed.connect(onWindowClosed);
}
function tearDown() {
window.webEventReceived.disconnect(onWebEventReceived);
}
return {
addGroup: addGroup,
setButtonOut: setButtonOut,
setCountDownValue: setCountDownValue,
isVisible: getVisible,
setVisible: setVisible,
toggleVisible: toggleVisible,
setUp: setUp,
tearDown: tearDown
};
}());
function onButtonClicked() {
Dialog.toggleVisible();
button.editProperties({ isActive: Dialog.isVisible() });
}
function onWindowClosed() {
button.editProperties({ isActive: false });
}
function finishMovingEntities(setPieceIndex, moveOut) {
var setPiece = setPieces[setPieceIndex],
NUDGE_VELOCITY = { x: 0, y: 0.1, z: 0 }, // To kick off physics for dynamic entities.
i;
// Stop moving in update loop.
movingSetPieces.splice(movingSetPieces.indexOf(setPieceIndex), 1);
// Set to exact target position.
for (i = 0; i < setPiece.entities.length; i++) {
Entities.editEntity(setPiece.entities[i].id, {
velocity: moveOut && setPiece.entities[i].dynamic === true ? NUDGE_VELOCITY : Vec3.ZERO,
dynamic: moveOut ? setPiece.entities[i].dynamic === true : false,
collisionless: moveOut ? setPiece.entities[i].collisionless === true : true,
position: moveOut ? setPiece.entities[i]["out"].position : setPiece.entities[i]["in"].position
});
}
// Finish counting down.
if (setPiece.countDownTimer) {
Script.clearInterval(setPiece.countDownTimer);
setPiece.countDownTimer = null;
}
setPiece.countDownValue = 0;
Dialog.setCountDownValue(setPiece.id, 0);
setPiece.finishMovingEntitiesTimer = null;
}
function moveEntities(setPieceIndex, moveOut) {
var setPiece = setPieces[setPieceIndex],
movingIndex,
entity,
i;
// Clear any current movement.
movingIndex = movingSetPieces.indexOf(setPieceIndex);
if (movingIndex !== -1) {
movingSetPieces.splice(movingIndex, 1);
}
if (setPiece.finishMovingEntitiesTimer) {
Script.clearTimeout(setPiece.finishMovingEntitiesTimer);
setPiece.finishMovingEntitiesTimer = null;
}
// Start/restart count-down indicator.
if (setPiece.countDownTimer) {
Script.clearInterval(setPiece.countDownTimer);
setPiece.countDownTimer = null;
}
setPiece.countDownValue = setPiece.duration;
Dialog.setCountDownValue(setPiece.id, setPiece.countDownValue);
setPiece.countDownTimer = Script.setInterval(function () {
setPiece.countDownValue -= 0.5;
Dialog.setCountDownValue(setPiece.id, setPiece.countDownValue);
}, 500);
// Set collisionless and non-dynamic while moving.
for (i = 0; i < setPiece.entities.length; i++) {
Entities.editEntity(setPiece.entities[i].id, {
collisionless: true,
dynamic: false,
velocity: Vec3.ZERO
});
}
// Start moving in update loop.
setPiece.movementTargetTime = Date.now() + setPiece.duration * 1000;
for (i = 0; i < setPiece.entities.length; i++) {
entity = setPiece.entities[i];
entity.movementTarget = moveOut ? entity["out"].position : entity["in"].position;
entity.movementVector =
Vec3.subtract(entity.movementTarget, Entities.getEntityProperties(entity.id, "position").position);
}
movingSetPieces.push(setPieceIndex);
// Schedule completion of movement.
setPiece.finishMovingEntitiesTimer = Script.setTimeout(function () {
finishMovingEntities(setPieceIndex, moveOut);
}, setPiece.duration * 1000);
}
function onUpdate() {
var setPiece,
nowTime = Date.now(),
entity,
factor,
i, iLength,
j, jLength;
for (i = 0, iLength = movingSetPieces.length; i < iLength; i++) {
setPiece = setPieces[movingSetPieces[i]];
factor = (setPiece.movementTargetTime - nowTime) / (setPiece.duration * 1000);
for (j = 0, jLength = setPiece.entities.length; j < jLength; j++) {
entity = setPiece.entities[j];
Entities.editEntity(entity.id, {
position: Vec3.subtract(entity.movementTarget, Vec3.multiply(factor, entity.movementVector))
});
}
}
}
function onDialogMessage(data) {
var id,
index,
isOut;
id = data.id;
index = setPiecesIndexes[id];
isOut = !setPieces[index].isOut;
setPieces[index].isOut = isOut;
Dialog.setButtonOut(id, isOut);
moveEntities(index, isOut);
}
function setUpSetPieces() {
var id,
i;
setPieces = Script.require("./setPiecesDirector.json");
for (i = 0; i < setPieces.length; i++) {
// Generate an ID to identify the group in HTML.
id = "group" + i;
setPieces[i].id = id;
setPiecesIndexes[id] = i;
// Generate the group in HTML.
setPieces[i].isOut = true; // Assume that the set pieces are out, so that director can move all in at start.
Dialog.addGroup(id, setPieces[i]);
// Explicitly record that timers aren't running.
setPieces[i].finishMovingEntitiesTimer = null;
setPieces[i].countDownTimer = null;
}
}
function setUp() {
Dialog.setUp(onDialogMessage, onWindowClosed);
Script.setTimeout(function () {
// Event bridge isn't available straight away.
setUpSetPieces();
// Wait for other scripts to set themselves up so as to avoid contention.
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
if (tablet) {
button = tablet.addButton({
icon: APP_ICON_INACTIVE,
activeIcon: APP_ICON_ACTIVE,
text: APP_NAME,
isActive: Dialog.isVisible()
});
}
if (button) {
button.clicked.connect(onButtonClicked);
}
}, 2500);
Script.update.connect(onUpdate);
}
function tearDown() {
Script.update.disconnect(onUpdate);
Dialog.setVisible(false);
if (button) {
button.clicked.disconnect(onButtonClicked);
if (tablet) {
tablet.removeButton(button);
tablet = null;
}
button = null;
}
Dialog.tearDown();
}
setUp();
Script.scriptEnding.connect(tearDown);
}());