219 lines · 8.7 KB · JavaScript
RawDownload
"use strict";////  bubble.js//  scripts/system/////  Created by Brad Hefta-Gaub on November 18th, 2016//  Copyright 2016 High Fidelity, Inc.//  Copyright 2023 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html///* global Script, Users, Entities, AvatarList, Controller, Camera, getControllerWorldLocation, UserActivityLogger */ (function () { // BEGIN LOCAL_SCOPE    var button;    // Used for animating and disappearing the bubble    var bubbleOverlayTimestamp;    // Used for rate limiting the bubble sound    var lastBubbleSoundTimestamp = 0;    // Affects bubble height    var BUBBLE_HEIGHT_SCALE = 0.15;    // The bubble model itself    var bubbleOverlay = Entities.addEntity({        "type": "Model",        "modelURL": Script.resolvePath("assets/models/Bubble-v14.fbx"), // If you'd like to change the model, modify this line (and the dimensions below)        "dimensions": {             "x": MyAvatar.sensorToWorldScale,            "y": (0.75 * MyAvatar.sensorToWorldScale),            "z": MyAvatar.sensorToWorldScale        },        "position": { "x": MyAvatar.position.x, "y": -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE, "z": MyAvatar.position.z },        "rotation": Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({"x": 0.0, "y": 180.0, "z": 0.0})),        "visible": false,        "ignorePickIntersection": true    }, "local");    // The bubble activation sound    var bubbleActivateSound = SoundCache.getSound(Script.resolvePath("assets/sounds/bubble.wav"));    // Is the update() function connected?    var updateConnected = false;     var BUBBLE_VISIBLE_DURATION_MS = 3000;    var BUBBLE_RAISE_ANIMATION_DURATION_MS = 750;    var BUBBLE_SOUND_RATE_LIMIT_MS = 15000;     // Hides the bubble model overlay    function hideOverlays() {        Entities.editEntity(bubbleOverlay, {            "visible": false        });    }     //create a menu item in "Setings" to toggle the bubble/shield HUD button     var menuItemName = "HUD Shield Button";    Menu.addMenuItem({        menuName: "Settings",        menuItemName: menuItemName,        isCheckable: true,        isChecked: AvatarInputs.showBubbleTools    });    Menu.menuItemEvent.connect(onToggleHudShieldButton);    AvatarInputs.showBubbleToolsChanged.connect(showBubbleToolsChanged);     function onToggleHudShieldButton(menuItem) {        if (menuItem === menuItemName) {            AvatarInputs.setShowBubbleTools(Menu.isOptionChecked(menuItem));        };    }     function showBubbleToolsChanged(show) {        Menu.setIsOptionChecked(menuItemName, show);    }     // Make the bubble overlay visible, set its position, and play the sound    function createOverlays() {        var nowTimestamp = Date.now();        if (nowTimestamp - lastBubbleSoundTimestamp >= BUBBLE_SOUND_RATE_LIMIT_MS) {            Audio.playSound(bubbleActivateSound, {                position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },                localOnly: true,                volume: 0.2            });            lastBubbleSoundTimestamp = nowTimestamp;        }        hideOverlays();        if (updateConnected === true) {            updateConnected = false;            Script.update.disconnect(update);        }         Entities.editEntity(bubbleOverlay, {            "dimensions": {                 "x": MyAvatar.sensorToWorldScale,                "y": 0.75 * MyAvatar.sensorToWorldScale,                "z": MyAvatar.sensorToWorldScale            },            "position": {                 "x": MyAvatar.position.x,                 "y": -MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE,                 "z": MyAvatar.position.z             },            "rotation": Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({"x": 0.0, "y": 180.0, "z": 0.0})),            "visible": true        });        bubbleOverlayTimestamp = nowTimestamp;        Script.update.connect(update);        updateConnected = true;    }     // Called from the C++ scripting interface to show the bubble overlay    function enteredIgnoreRadius() {        createOverlays();        UserActivityLogger.privacyShieldActivated();    }     // Used to set the state of the bubble HUD button    function writeButtonProperties(parameter) {        button.editProperties({isActive: parameter});    }     // The bubble script's update function    function update() {        var timestamp = Date.now();        var delay = (timestamp - bubbleOverlayTimestamp);        var overlayAlpha = 1.0 - (delay / BUBBLE_VISIBLE_DURATION_MS);        if (overlayAlpha > 0) {            if (delay < BUBBLE_RAISE_ANIMATION_DURATION_MS) {                Entities.editEntity(bubbleOverlay, {                    "dimensions": {                         "x": MyAvatar.sensorToWorldScale,                        "y": (0.75 * MyAvatar.sensorToWorldScale) * (1 - ((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)),                        "z": MyAvatar.sensorToWorldScale                    },                    // Quickly raise the bubble from the ground up                    "position": {                        "x": MyAvatar.position.x,                        "y": (-((BUBBLE_RAISE_ANIMATION_DURATION_MS - delay) / BUBBLE_RAISE_ANIMATION_DURATION_MS)) * MyAvatar.scale * 2 + MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE,                        "z": MyAvatar.position.z                    },                    "rotation": Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({"x": 0.0, "y": 180.0, "z": 0.0}))                });            } else {                // Keep the bubble in place for a couple seconds                Entities.editEntity(bubbleOverlay, {                    "dimensions": {                         "x": MyAvatar.sensorToWorldScale,                        "y": 0.75 * MyAvatar.sensorToWorldScale,                        "z": MyAvatar.sensorToWorldScale                    },                                "position": {                        "x": MyAvatar.position.x,                        "y": MyAvatar.position.y + MyAvatar.scale * BUBBLE_HEIGHT_SCALE,                        "z": MyAvatar.position.z                    },                    "rotation": Quat.multiply(MyAvatar.orientation, Quat.fromVec3Degrees({"x": 0.0, "y": 180.0, "z": 0.0}))                });            }        } else {            hideOverlays();            if (updateConnected === true) {                Script.update.disconnect(update);                updateConnected = false;            }        }    }     // When the space bubble is toggled...    // NOTE: the c++ calls this with just the first param -- we added a second    // just for not logging the initial state of the bubble when we startup.    function onBubbleToggled(enabled, doNotLog) {        writeButtonProperties(enabled);        if (doNotLog !== true) {            UserActivityLogger.privacyShieldToggled(enabled);        }        if (enabled) {            createOverlays();        } else {            hideOverlays();            if (updateConnected === true) {                Script.update.disconnect(update);                updateConnected = false;            }        }    }     // Setup the bubble button    var buttonName = "SHIELD";    var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");    button = tablet.addButton({        icon: "icons/tablet-icons/bubble-i.svg",        activeIcon: "icons/tablet-icons/bubble-a.svg",        text: buttonName,        sortOrder: 4    });     onBubbleToggled(Users.getIgnoreRadiusEnabled(), true); // pass in true so we don't log this initial one in the UserActivity table     button.clicked.connect(Users.toggleIgnoreRadius);    Users.ignoreRadiusEnabledChanged.connect(onBubbleToggled);    Users.enteredIgnoreRadius.connect(enteredIgnoreRadius);     // Cleanup the tablet button and overlays when script is stopped    Script.scriptEnding.connect(function () {        Menu.menuItemEvent.disconnect(onToggleHudShieldButton);        AvatarInputs.showBubbleToolsChanged.disconnect(showBubbleToolsChanged);        Menu.removeMenuItem("Settings", menuItemName);        button.clicked.disconnect(Users.toggleIgnoreRadius);        if (tablet) {            tablet.removeButton(button);        }        Users.ignoreRadiusEnabledChanged.disconnect(onBubbleToggled);        Users.enteredIgnoreRadius.disconnect(enteredIgnoreRadius);        Entities.deleteEntity(bubbleOverlay);        if (updateConnected === true) {            Script.update.disconnect(update);        }    }); }()); // END LOCAL_SCOPE