354 lines
12 KiB
JavaScript
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);
|
|
|
|
}());
|