diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 1560ac5b44..9e454eafc0 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -20,7 +20,6 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var NEAR_MIN_RADIUS = 0.1; var NEAR_MAX_RADIUS = 1.0; - var DISPATCHER_PROPERTIES = [ "position", "registrationPoint", @@ -39,7 +38,39 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); "userData" ]; - this.runningPluginName = null; + // a module can occupy one or more slots while it's running. If all the required slots for a module are + // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name + // is stored as the value, rather than false. + this.activitySlots = { + leftHand: false, + rightHand: false + }; + + this.slotsAreAvailable = function (plugin) { + for (var i = 0; i < plugin.parameters.activitySlots.length; i++) { + if (_this.activitySlots[plugin.parameters.activitySlots[i]]) { + return false; // something is already using a slot which _this plugin requires + } + } + return true; + }; + + this.markSlots = function (plugin, used) { + for (var i = 0; i < plugin.parameters.activitySlots.length; i++) { + _this.activitySlots[plugin.parameters.activitySlots[i]] = used; + } + }; + + this.unmarkSlotsForPluginName = function (runningPluginName) { + // this is used to free activity-slots when a plugin is deactivated while it's running. + for (var activitySlot in _this.activitySlots) { + if (activitySlot.hasOwnProperty(activitySlot) && _this.activitySlots[activitySlot] == runningPluginName) { + _this.activitySlots[activitySlot] = false; + } + } + }; + + this.runningPluginNames = {}; this.leftTriggerValue = 0; this.leftTriggerClicked = 0; this.rightTriggerValue = 0; @@ -68,7 +99,7 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); getControllerWorldLocation(Controller.Standard.RightHand, true)]; var nearbyEntityProperties = [[], []]; - for (var h = LEFT_HAND; h <= RIGHT_HAND; h ++) { + for (var h = LEFT_HAND; h <= RIGHT_HAND; h++) { // todo: check controllerLocations[h].valid var controllerPosition = controllerLocations[h].position; var nearbyEntityIDs = Entities.findEntities(controllerPosition, NEAR_MIN_RADIUS); @@ -82,11 +113,12 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); } } // sort by distance from each hand - nearbyEntityProperties[h].sort(function (a, b) { + var sorter = function (a, b) { var aDistance = Vec3.distance(a.position, controllerLocations[h]); var bDistance = Vec3.distance(b.position, controllerLocations[h]); return aDistance - bDistance; - }); + }; + nearbyEntityProperties[h].sort(sorter); } var controllerData = { @@ -96,20 +128,35 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); nearbyEntityProperties: nearbyEntityProperties, }; - if (_this.runningPluginName) { - var plugin = controllerDispatcherPlugins[_this.runningPluginName]; - if (!plugin || !plugin.run(controllerData)) { - _this.runningPluginName = null; + + // check for plugins that would like to start + for (var pluginName in controllerDispatcherPlugins) { + // TODO sort names by plugin.priority + if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { + var candidatePlugin = controllerDispatcherPlugins[pluginName]; + if (_this.slotsAreAvailable(candidatePlugin) && candidatePlugin.isReady(controllerData)) { + // this plugin will start. add it to the list of running plugins and mark the + // activity-slots which this plugin consumes as "in use" + _this.runningPluginNames[pluginName] = true; + _this.markSlots(candidatePlugin, pluginName); + } } - } else if (controllerDispatcherPlugins) { - for (var pluginName in controllerDispatcherPlugins) { - // TODO sort names by plugin.priority - if (controllerDispatcherPlugins.hasOwnProperty(pluginName)) { - var candidatePlugin = controllerDispatcherPlugins[pluginName]; - if (candidatePlugin.isReady(controllerData)) { - _this.runningPluginName = pluginName; - break; - } + } + + // give time to running plugins + for (var runningPluginName in _this.runningPluginNames) { + if (_this.runningPluginNames.hasOwnProperty(runningPluginName)) { + var plugin = controllerDispatcherPlugins[runningPluginName]; + if (!plugin) { + // plugin was deactivated while running. find the activity-slots it was using and make + // them available. + delete _this.runningPluginNames[runningPluginName]; + _this.unmarkSlotsForPluginName(runningPluginName); + } else if (!plugin.run(controllerData)) { + // plugin is finished running, for now. remove it from the list + // of running plugins and mark its activity-slots as "not in use" + delete _this.runningPluginNames[runningPluginName]; + _this.markSlots(plugin, false); } } } @@ -117,14 +164,14 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; var mapping = Controller.newMapping(MAPPING_NAME); - mapping.from([Controller.Standard.RT]).peek().to(this.rightTriggerPress); - mapping.from([Controller.Standard.RTClick]).peek().to(this.rightTriggerClick); - mapping.from([Controller.Standard.LT]).peek().to(this.leftTriggerPress); - mapping.from([Controller.Standard.LTClick]).peek().to(this.leftTriggerClick); + mapping.from([Controller.Standard.RT]).peek().to(_this.rightTriggerPress); + mapping.from([Controller.Standard.RTClick]).peek().to(_this.rightTriggerClick); + mapping.from([Controller.Standard.LT]).peek().to(_this.leftTriggerPress); + mapping.from([Controller.Standard.LTClick]).peek().to(_this.leftTriggerClick); Controller.enableMapping(MAPPING_NAME); this.cleanup = function () { - Script.update.disconnect(this.update); + Script.update.disconnect(_this.update); Controller.disableMapping(MAPPING_NAME); }; diff --git a/scripts/system/controllers/controllerDispatcherUtils.js b/scripts/system/controllers/controllerDispatcherUtils.js index 8717be34d5..7f494761f5 100644 --- a/scripts/system/controllers/controllerDispatcherUtils.js +++ b/scripts/system/controllers/controllerDispatcherUtils.js @@ -9,6 +9,7 @@ /* global Camera, HMD, MyAvatar, controllerDispatcherPlugins, MSECS_PER_SEC, LEFT_HAND, RIGHT_HAND, NULL_UUID, AVATAR_SELF_ID, FORBIDDEN_GRAB_TYPES, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + makeDispatcherModuleParameters, enableDispatcherModule, disableDispatcherModule, getGrabbableData, @@ -31,6 +32,20 @@ HAPTIC_PULSE_STRENGTH = 1.0; HAPTIC_PULSE_DURATION = 13.0; +// priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step +// activitySlots -- indicates which "slots" must not yet be in use for this module to start +// requiredDataForStart -- which "situation" parts this module looks at to decide if it will start +// sleepMSBetweenRuns -- how long to wait between calls to this module's "run" method +makeDispatcherModuleParameters = function (priority, activitySlots, requiredDataForStart, sleepMSBetweenRuns) { + return { + priority: priority, + activitySlots: activitySlots, + requiredDataForStart: requiredDataForStart, + sleepMSBetweenRuns: sleepMSBetweenRuns + }; +}; + + enableDispatcherModule = function (moduleName, module, priority) { if (!controllerDispatcherPlugins) { controllerDispatcherPlugins = {}; diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index 57df123c1d..d6b5ffe4f7 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -8,7 +8,7 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, entityIsGrabbable, - Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation + Quat, Vec3, MSECS_PER_SEC, getControllerWorldLocation, makeDispatcherModuleParameters */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -21,6 +21,12 @@ Script.include("/~/system/libraries/controllers.js"); this.grabbedThingID = null; this.actionID = null; // action this script created... + this.parameters = makeDispatcherModuleParameters( + 500, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + var NEAR_GRABBING_ACTION_TIMEFRAME = 0.05; // how quickly objects move to their new position var ACTION_TTL = 15; // seconds var ACTION_TTL_REFRESH = 5; @@ -192,8 +198,8 @@ Script.include("/~/system/libraries/controllers.js"); }; } - enableDispatcherModule("LeftNearActionGrabEntity", new NearActionGrabEntity(LEFT_HAND), 500); - enableDispatcherModule("RightNearActionGrabEntity", new NearActionGrabEntity(RIGHT_HAND), 500); + enableDispatcherModule("LeftNearActionGrabEntity", new NearActionGrabEntity(LEFT_HAND)); + enableDispatcherModule("RightNearActionGrabEntity", new NearActionGrabEntity(RIGHT_HAND)); this.cleanup = function () { disableDispatcherModule("LeftNearActionGrabEntity"); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 70e8bb363a..58bd3d2dab 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -7,8 +7,9 @@ /* global Script, Entities, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, AVATAR_SELF_ID, - getControllerJointIndex, getGrabbableData, NULL_UUID, enableDispatcherModule, disableDispatcherModule, - FORBIDDEN_GRAB_TYPES, propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION + getControllerJointIndex, NULL_UUID, enableDispatcherModule, disableDispatcherModule, + propsArePhysical, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, + makeDispatcherModuleParameters, entityIsGrabbable */ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); @@ -25,6 +26,12 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); this.previousParentJointIndex = {}; this.previouslyUnhooked = {}; + this.parameters = makeDispatcherModuleParameters( + 500, + this.hand === RIGHT_HAND ? ["rightHand"] : ["leftHand"], + [], + 100); + // XXX does handJointIndex change if the avatar changes? this.handJointIndex = MyAvatar.getJointIndex(this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"); @@ -156,8 +163,8 @@ Script.include("/~/system/controllers/controllerDispatcherUtils.js"); }; } - enableDispatcherModule("LeftNearParentingGrabEntity", new NearParentingGrabEntity(LEFT_HAND), 500); - enableDispatcherModule("RightNearParentingGrabEntity", new NearParentingGrabEntity(RIGHT_HAND), 500); + enableDispatcherModule("LeftNearParentingGrabEntity", new NearParentingGrabEntity(LEFT_HAND)); + enableDispatcherModule("RightNearParentingGrabEntity", new NearParentingGrabEntity(RIGHT_HAND)); this.cleanup = function () { disableDispatcherModule("LeftNearParentingGrabEntity");