387 lines · 12.3 KB · JavaScript
RawDownload
"use strict";////  away.js////  Created by Howard Stearns November, 3rd, 2015//  Copyright 2015 High Fidelity, Inc.//  Copyright 2021 Vircadia contributors.//  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//// Goes into "paused" when the '.' key (and automatically when started in HMD), and normal when pressing any key.// See MAIN CONTROL, below, for what "paused" actually does. /* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ (function() { // BEGIN LOCAL_SCOPE var controllerStandard = Controller.Standard; var BASIC_TIMER_INTERVAL = 50; // 50ms = 20hzvar OVERLAY_WIDTH = 1920;var OVERLAY_HEIGHT = 1080;var OVERLAY_DATA = {    "width": OVERLAY_WIDTH,    "height": OVERLAY_HEIGHT,    "imageURL": Script.resolvePath("assets/images/Overlay-Viz-blank.png"),    "emissive": true,    "drawInFront": true,    "alpha": 1.0};var AVATAR_MOVE_FOR_ACTIVE_DISTANCE = 0.8; // meters -- no longer away if avatar moves this far while away var CAMERA_MATRIX = -7; var OVERLAY_DATA_HMD = {    "type": "Image",    "localPosition": {"x": 0, "y": 0, "z": -1 * MyAvatar.sensorToWorldScale},    "localRotation": {"x": 0, "y": 0, "z": 0, "w": 1},    "keepAspectRatio": true,    "imageURL": Script.resolvePath("assets/images/Overlay-Viz-blank.png"),    "color": {"red": 255, "green": 255, "blue": 255},    "alpha": 1.0,    "dimensions": Vec3.multiply({"x": 2, "y": 2, "z": 2}, MyAvatar.sensorToWorldScale),    "emissive": true,    "renderLayer": "front",    "parentID": MyAvatar.SELF_ID,    "parentJointIndex": CAMERA_MATRIX,    "ignorePickIntersection": true}; var AWAY_INTRO = {    url: "qrc:///avatar/animations/afk_texting.fbx",    playbackRate: 30.0,    loopFlag: true,    startFrame: 1.0,    endFrame: 489.0}; // MAIN CONTROLvar isEnabled = true;var wasMuted; // unknonwn?var isAway = false; // we start in the un-away statevar eventMappingName = "io.highfidelity.away"; // goActive on hand controller button events, too.var eventMapping = Controller.newMapping(eventMappingName);var avatarPosition = MyAvatar.position;var wasHmdMounted = HMD.mounted;var previousBubbleState = Users.getIgnoreRadiusEnabled(); var enterAwayStateWhenFocusLostInVR = HMD.enterAwayStateWhenFocusLostInVR; // some intervals we may create/deletevar avatarMovedInterval;  // prefetch the kneel animation and hold a ref so it's always resident in memory when we need it.var _animation = AnimationCache.prefetch(AWAY_INTRO.url); function playAwayAnimation() {    MyAvatar.overrideAnimation(AWAY_INTRO.url,            AWAY_INTRO.playbackRate,            AWAY_INTRO.loopFlag,            AWAY_INTRO.startFrame,            AWAY_INTRO.endFrame);} function stopAwayAnimation() {    MyAvatar.restoreAnimation();} // OVERLAYvar overlay = Overlays.addOverlay("image", OVERLAY_DATA);var overlayHMD = Entities.addEntity(OVERLAY_DATA_HMD, "local"); function showOverlay() {    if (HMD.active) {        // make sure desktop version is hidden        Overlays.editOverlay(overlay, { "visible": false });        Entities.editEntity(overlayHMD, { "visible": true });    } else {        // make sure HMD is hidden        Entities.editEntity(overlayHMD, { "visible": false });         // Update for current screen size, keeping overlay proportions constant.        var screen = Controller.getViewportDimensions();         // keep the overlay it's natural size and always center it...        Overlays.editOverlay(overlay, {            "visible": true,            "x": ((screen.x - OVERLAY_WIDTH) / 2),            "y": ((screen.y - OVERLAY_HEIGHT) / 2)        });    }} function hideOverlay() {    Overlays.editOverlay(overlay, {"visible": false});    Entities.editEntity(overlayHMD, {"visible": false});} hideOverlay(); function maybeMoveOverlay() {    if (isAway) {        // if we switched from HMD to Desktop, make sure to hide our HUD overlay and show the        // desktop overlay        if (!HMD.active) {            showOverlay(); // this will also recenter appropriately        }         if (HMD.active) {             var sensorScaleFactor = MyAvatar.sensorToWorldScale;            var localPosition = {x: 0, y: 0, z: -1 * sensorScaleFactor};            Entities.editEntity(overlayHMD, { "visible": true, "localPosition": localPosition, "dimensions": Vec3.multiply({"x": 2, "y": 2, "z": 2}, MyAvatar.sensorToWorldScale )});             // make sure desktop version is hidden            Overlays.editOverlay(overlay, { "visible": false });             // also remember avatar position            avatarPosition = MyAvatar.position;         }    }} function ifAvatarMovedGoActive() {    var newAvatarPosition = MyAvatar.position;    if (Vec3.distance(newAvatarPosition, avatarPosition) > AVATAR_MOVE_FOR_ACTIVE_DISTANCE) {        goActive();    }    avatarPosition = newAvatarPosition;} function goAway(fromStartup) {    if (!isEnabled || isAway) {        return;    }     // If we're entering away mode from some other state than startup, then we create our move timer immediately.    // However if we're just stating up, we need to delay this process so that we don't think the initial teleport    // is actually a move.    if (fromStartup === undefined || fromStartup === false) {        avatarMovedInterval = Script.setInterval(ifAvatarMovedGoActive, BASIC_TIMER_INTERVAL);    } else {        var WAIT_FOR_MOVE_ON_STARTUP = 3000; // 3 seconds        Script.setTimeout(function() {            avatarMovedInterval = Script.setInterval(ifAvatarMovedGoActive, BASIC_TIMER_INTERVAL);        }, WAIT_FOR_MOVE_ON_STARTUP);    }     previousBubbleState = Users.getIgnoreRadiusEnabled();    if (!previousBubbleState) {        Users.toggleIgnoreRadius();    }    UserActivityLogger.privacyShieldToggled(Users.getIgnoreRadiusEnabled());    UserActivityLogger.toggledAway(true);    MyAvatar.isAway = true;} function goActive() {    if (!isAway) {        return;    }     UserActivityLogger.toggledAway(false);    MyAvatar.isAway = false;     if (Users.getIgnoreRadiusEnabled() !== previousBubbleState) {        Users.toggleIgnoreRadius();        UserActivityLogger.privacyShieldToggled(Users.getIgnoreRadiusEnabled());    }     if (!Window.hasFocus()) {        Window.setFocus();    }} MyAvatar.wentAway.connect(setAwayProperties);MyAvatar.wentActive.connect(setActiveProperties); function setAwayProperties() {    isAway = true;    wasMuted = Audio.muted;    if (!wasMuted) {        Audio.muted = !Audio.muted;    }    MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view    playAwayAnimation(); // animation is still seen by others    showOverlay();     HMD.requestShowHandControllers();     // tell the Reticle, we want to stop capturing the mouse until we come back    Reticle.allowMouseCapture = false;    // Allow users to find their way to other applications, our menus, etc.    // For desktop, that means we want the reticle visible.    // For HMD, the hmd preview will show the system mouse because of allowMouseCapture,    // but we want to turn off our Reticle so that we don't get two in preview and a stuck one in headset.    Reticle.visible = !HMD.active;    wasHmdMounted = HMD.mounted; // always remember the correct state     avatarPosition = MyAvatar.position;} function setActiveProperties() {    isAway = false;    if (Audio.muted && !wasMuted) {        Audio.muted = false;    }    MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting.    stopAwayAnimation();     HMD.requestHideHandControllers();     // update the UI sphere to be centered about the current HMD orientation.    HMD.centerUI();     // forget about any IK joint limits    MyAvatar.clearIKJointLimitHistory();     // update the avatar hips to point in the same direction as the HMD orientation.    MyAvatar.centerBody();     hideOverlay();     // tell the Reticle, we are ready to capture the mouse again and it should be visible    Reticle.allowMouseCapture = true;    Reticle.visible = true;    if (HMD.active) {        Reticle.position = HMD.getHUDLookAtPosition2D();    }    wasHmdMounted = HMD.mounted; // always remember the correct state     Script.clearInterval(avatarMovedInterval);} var wasHmdActive = HMD.active;var wasMouseCaptured = Reticle.mouseCaptured; function maybeGoAway() {    // If our active state change (went to or from HMD mode), and we are now in the HMD, go into away    if (HMD.active !== wasHmdActive) {        wasHmdActive = !wasHmdActive;        if (wasHmdActive) {            goAway();            return;        }    }     // If the mouse has gone from captured, to non-captured state, then it likely means the person is still in the HMD,    // but tabbed away from the application (meaning they don't have mouse control) and they likely want to go into    // an away state    if (Reticle.mouseCaptured !== wasMouseCaptured) {        wasMouseCaptured = !wasMouseCaptured;        if (!wasMouseCaptured) {            if (enterAwayStateWhenFocusLostInVR) {                goAway();                return;            }        }    }     // If you've removed your HMD from your head, and we can detect it, we will also go away...    if (HMD.mounted !== wasHmdMounted) {        wasHmdMounted = HMD.mounted;        print("HMD mounted changed...");         // We're putting the HMD on... switch to those devices        if (HMD.mounted) {            print("NOW mounted...");        } else {            print("HMD NOW un-mounted...");             if (HMD.active) {                goAway();                return;            }        }    }} function setEnabled(value) {    if (!value) {        goActive();    }    isEnabled = value;} function checkAudioToggled() {    if (isAway && !Audio.muted) {        goActive();    }}  var CHANNEL_AWAY_ENABLE = "Hifi-Away-Enable";var handleMessage = function(channel, message, sender) {    if (channel === CHANNEL_AWAY_ENABLE && sender === MyAvatar.sessionUUID) {        print("away.js | Got message on Hifi-Away-Enable: ", message);        if (message === 'enable') {            setEnabled(true);        } else if (message === 'toggle') {            toggleAway();        }    }};Messages.subscribe(CHANNEL_AWAY_ENABLE);Messages.messageReceived.connect(handleMessage); function toggleAway() {    if (!isAway) {        goAway();    } else {        goActive();    }} var maybeIntervalTimer = Script.setInterval(function() {    maybeMoveOverlay();    maybeGoAway();    checkAudioToggled();}, BASIC_TIMER_INTERVAL);  Controller.mousePressEvent.connect(goActive);// Note peek() so as to not interfere with other mappings.eventMapping.from(controllerStandard.LeftPrimaryThumb).peek().to(goActive);eventMapping.from(controllerStandard.RightPrimaryThumb).peek().to(goActive);eventMapping.from(controllerStandard.LeftSecondaryThumb).peek().to(goActive);eventMapping.from(controllerStandard.RightSecondaryThumb).peek().to(goActive);eventMapping.from(controllerStandard.LT).peek().to(goActive);eventMapping.from(controllerStandard.LB).peek().to(goActive);eventMapping.from(controllerStandard.LS).peek().to(goActive);eventMapping.from(controllerStandard.LeftGrip).peek().to(goActive);eventMapping.from(controllerStandard.RT).peek().to(goActive);eventMapping.from(controllerStandard.RB).peek().to(goActive);eventMapping.from(controllerStandard.RS).peek().to(goActive);eventMapping.from(controllerStandard.RightGrip).peek().to(goActive);eventMapping.from(controllerStandard.Back).peek().to(goActive);eventMapping.from(controllerStandard.Start).peek().to(goActive);Controller.enableMapping(eventMappingName); function awayStateWhenFocusLostInVRChanged(enabled) {    enterAwayStateWhenFocusLostInVR = enabled;} Script.scriptEnding.connect(function () {    Script.clearInterval(maybeIntervalTimer);    goActive();    HMD.awayStateWhenFocusLostInVRChanged.disconnect(awayStateWhenFocusLostInVRChanged);    Controller.disableMapping(eventMappingName);    Controller.mousePressEvent.disconnect(goActive);    Messages.messageReceived.disconnect(handleMessage);    Messages.unsubscribe(CHANNEL_AWAY_ENABLE);}); HMD.awayStateWhenFocusLostInVRChanged.connect(awayStateWhenFocusLostInVRChanged); if (HMD.active && !HMD.mounted) {    print("Starting script, while HMD is active and not mounted...");    goAway(true);}  }()); // END LOCAL_SCOPE