diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index a9e62de4df..211989d661 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -257,7 +257,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.parameters = makeDispatcherModuleParameters( 300, - this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip"] : ["leftHand", "rightHandEquip"], + this.hand === RIGHT_HAND ? ["rightHand", "rightHandEquip"] : ["leftHand", "leftHandEquip"], [], 100); @@ -539,6 +539,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa ensureDynamic(this.targetEntityID); this.targetEntityID = null; + this.messageGrabEntity = false; + this.grabEntityProps = null; }; this.updateInputs = function (controllerData) { @@ -594,11 +596,18 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } return makeRunningValues(true, [potentialEquipHotspot.entityID], []); } else { - this.messageGrabEnity = false; return makeRunningValues(false, [], []); } }; + this.isTargetIDValid = function() { + var entityProperties = Entities.getEntityProperties(this.targetEntityID); + for (var propertry in entityProperties) { + return true; + } + return false; + }; + this.isReady = function (controllerData, deltaTime) { var timestamp = Date.now(); this.updateInputs(controllerData); @@ -609,6 +618,11 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var timestamp = Date.now(); this.updateInputs(controllerData); + if (!this.isTargetIDValid()) { + this.endEquipEntity(); + return makeRunningValues(false, [], []); + } + if (!this.targetEntityID) { return this.checkNearbyHotspots(controllerData, deltaTime, timestamp); } @@ -681,7 +695,8 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa if (channel === 'Hifi-Hand-Grab') { try { data = JSON.parse(message); - var equipModule = (data.hand === 'left') ? leftEquipEntity : rightEquipEntity; + print(JSON.stringify(data)); + var equipModule = (data.hand === "left") ? leftEquipEntity : rightEquipEntity; var entityProperties = Entities.getEntityProperties(data.entityID, DISPATCHER_PROPERTIES); entityProperties.id = data.entityID; equipModule.setMessageGrabData(entityProperties); diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index 822ad55f88..44360c2e39 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -439,6 +439,7 @@ Script.include("/~/system/libraries/controllers.js"); // gather up the readiness of the near-grab modules var nearGrabNames = [ + this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay" diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js new file mode 100644 index 0000000000..671fc58c7d --- /dev/null +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -0,0 +1,234 @@ +"use strict"; + +// farTrigger.js +// +// 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, Controller, LaserPointers, RayPick, RIGHT_HAND, LEFT_HAND, Mat4, MyAvatar, Vec3, Camera, Quat, + getGrabPointSphereOffset, getEnabledModuleByName, makeRunningValues, Entities, NULL_UUID, + enableDispatcherModule, disableDispatcherModule, entityIsDistanceGrabbable, + makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, + AVATAR_SELF_ID, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, + getControllerWorldLocation, projectOntoEntityXYPlane, getGrabbableData + +*/ + +Script.include("/~/system/controllers/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { + var halfPath = { + type: "line3d", + color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + lineWidth: 5, + ignoreRayIntersection: true, // always ignore this + drawInFront: true, // Even when burried inside of something, show it. + parentID: AVATAR_SELF_ID + }; + var halfEnd = { + type: "sphere", + solid: true, + color: COLORS_GRAB_SEARCHING_HALF_SQUEEZE, + alpha: 0.9, + ignoreRayIntersection: true, + drawInFront: true, // Even when burried inside of something, show it. + visible: true + }; + var fullPath = { + type: "line3d", + color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + lineWidth: 5, + ignoreRayIntersection: true, // always ignore this + drawInFront: true, // Even when burried inside of something, show it. + parentID: AVATAR_SELF_ID + }; + var fullEnd = { + type: "sphere", + solid: true, + color: COLORS_GRAB_SEARCHING_FULL_SQUEEZE, + alpha: 0.9, + ignoreRayIntersection: true, + drawInFront: true, // Even when burried inside of something, show it. + visible: true + }; + var holdPath = { + type: "line3d", + color: COLORS_GRAB_DISTANCE_HOLD, + visible: true, + alpha: 1, + solid: true, + glow: 1.0, + lineWidth: 5, + ignoreRayIntersection: true, // always ignore this + drawInFront: true, // Even when burried inside of something, show it. + parentID: AVATAR_SELF_ID + }; + + var renderStates = [ + {name: "half", path: halfPath, end: halfEnd}, + {name: "full", path: fullPath, end: fullEnd}, + {name: "hold", path: holdPath} + ]; + + var defaultRenderStates = [ + {name: "half", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: halfPath}, + {name: "full", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: fullPath}, + {name: "hold", distance: DEFAULT_SEARCH_SPHERE_DISTANCE, path: holdPath} + ]; + + function entityWantsNearTrigger(props) { + var grabbableData = getGrabbableData(props); + return grabbableData.triggerable || grabbableData.wantsTrigger; + } + + function FarTriggerEntity(hand) { + this.hand = hand; + this.targetEntityID = null; + this.grabbing = false; + this.previousParentID = {}; + this.previousParentJointIndex = {}; + this.previouslyUnhooked = {}; + + this.parameters = makeDispatcherModuleParameters( + 520, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + + this.handToController = function() { + return (this.hand === RIGHT_HAND) ? Controller.Standard.RightHand : Controller.Standard.LeftHand; + }; + + this.updateLaserPointer = function(controllerData) { + var SEARCH_SPHERE_SIZE = 0.011; + var MIN_SPHERE_SIZE = 0.0005; + var radius = Math.max(1.2 * SEARCH_SPHERE_SIZE * this.intersectionDistance, MIN_SPHERE_SIZE); + var dim = {x: radius, y: radius, z: radius}; + var mode = "none"; + if (controllerData.triggerClicks[this.hand]) { + mode = "full"; + } else { + mode = "half"; + } + + var laserPointerID = this.laserPointer; + if (mode === "full") { + var fullEndToEdit = this.fullEnd; + fullEndToEdit.dimensions = dim; + LaserPointers.editRenderState(laserPointerID, mode, {path: fullPath, end: fullEndToEdit}); + } else if (mode === "half") { + var halfEndToEdit = this.halfEnd; + halfEndToEdit.dimensions = dim; + LaserPointers.editRenderState(laserPointerID, mode, {path: halfPath, end: halfEndToEdit}); + } + LaserPointers.enableLaserPointer(laserPointerID); + LaserPointers.setRenderState(laserPointerID, mode); + }; + + this.laserPointerOff = function() { + LaserPointers.disableLaserPointer(this.laserPointer); + }; + + this.getTargetProps = function (controllerData) { + // nearbyEntityProperties is already sorted by length from controller + var targetEntity = controllerData.rayPicks[this.hand].objectID; + if (targetEntity) { + var targetProperties = Entities.getEntityProperties(targetEntity); + if (entityWantsNearTrigger(targetProperties)) { + return targetProperties; + } + } + return null; + }; + + this.startFarTrigger = function (controllerData) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "startFarTrigger", args); + }; + + this.continueFarTrigger = function (controllerData) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "continueFarTrigger", args); + }; + + this.endFarTrigger = function (controllerData) { + var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID]; + Entities.callEntityMethod(this.targetEntityID, "stopFarTrigger", args); + this.laserPointerOff(); + }; + + this.isReady = function (controllerData) { + this.targetEntityID = null; + if (controllerData.triggerClicks[this.hand] === 0) { + return makeRunningValues(false, [], []); + } + + var targetProps = this.getTargetProps(controllerData); + if (targetProps) { + this.targetEntityID = targetProps.id; + this.startFarTrigger(controllerData); + return makeRunningValues(true, [this.targetEntityID], []); + } else { + return makeRunningValues(false, [], []); + } + }; + + this.run = function (controllerData) { + var targetEntity = controllerData.rayPicks[this.hand].objectID; + if (controllerData.triggerClicks[this.hand] === 0 || this.targetEntityID !== targetEntity) { + this.endFarTrigger(controllerData); + return makeRunningValues(false, [], []); + } + + this.updateLaserPointer(controllerData); + this.continueFarTrigger(controllerData); + return makeRunningValues(true, [this.targetEntityID], []); + }; + + this.halfEnd = halfEnd; + this.fullEnd = fullEnd; + this.laserPointer = LaserPointers.createLaserPointer({ + joint: (this.hand === RIGHT_HAND) ? "_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND" : "_CAMERA_RELATIVE_CONTROLLER_LEFTHAND", + filter: RayPick.PICK_ENTITIES | RayPick.PICK_OVERLAYS, + maxDistance: PICK_MAX_DISTANCE, + posOffset: getGrabPointSphereOffset(this.handToController()), + renderStates: renderStates, + faceAvatar: true, + defaultRenderStates: defaultRenderStates + }); + + this.cleanup = function () { + if (this.targetEntityID) { + this.endFarTrigger(); + } + + LaserPointers.disableLaserPointer(this.laserPointer); + LaserPointers.removeLaserPointer(this.laserPointer); + }; + } + + var leftFarTriggerEntity = new FarTriggerEntity(LEFT_HAND); + var rightFarTriggerEntity = new FarTriggerEntity(RIGHT_HAND); + + enableDispatcherModule("LeftFarTriggerEntity", leftFarTriggerEntity); + enableDispatcherModule("RightFarTriggerEntity", rightFarTriggerEntity); + + this.cleanup = function () { + leftFarTriggerEntity.cleanup(); + rightFarTriggerEntity.cleanup(); + disableDispatcherModule("LeftFarTriggerEntity"); + disableDispatcherModule("RightFarTriggerEntity"); + }; + Script.scriptEnding.connect(this.cleanup); +}()); diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 60b820f06b..569fad4855 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -27,6 +27,7 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/webEntityLaserInput.js", "controllerModules/inEditMode.js", "controllerModules/disableOtherModule.js", + "controllerModules/farTrigger.js", "teleport.js" ];