// chair.js // // Restrictions right now: // Chair objects need to be set as not colliding with avatars, so that they pull avatar // avatar into collision with them. Also they need to be at or above standing height // (like a stool). // // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // (function(){ var CHECK_INTERVAL_MSECS = 250; // When sitting, check for need to stand up var SETTINGS_INTERVAL_MSECS = 1000; // Periodically check user data for updates var DEFAULT_SIT_DISTANCE = 1.0; // How far away from the chair can you sit? var HYSTERESIS = 1.1; var sitTarget = { x: 0, y: 0, z: 0 }; // Offset where your butt should go relative // to the object's center. var SITTING = 0; var STANDING = 1; var state = STANDING; var sitDistance = DEFAULT_SIT_DISTANCE; var entity; var props; var checkTimer = false; var settingsTimer = false; var sitting = false; var _this; var WANT_DEBUG = false; function debugPrint(string) { if (WANT_DEBUG) { print(string); } } function howFarAway(position) { return Vec3.distance(MyAvatar.position, position); } function isSeatOpen(position, distance) { closest = true; AvatarList.getAvatarIdentifiers().forEach(function(avatarSessionUUID) { var avatar = AvatarList.getAvatar(avatarSessionUUID); if (avatarSessionUUID && Vec3.distance(avatar.position, position) < distance) { debugPrint("Seat Occupied!"); closest = false; } }); return closest; } function enterSitPose() { var rot; var UPPER_LEG_ANGLE = 240; var LOWER_LEG_ANGLE = -80; rot = Quat.safeEulerAngles(MyAvatar.getJointRotation("RightUpLeg")); MyAvatar.setJointData("RightUpLeg", Quat.fromPitchYawRollDegrees(UPPER_LEG_ANGLE, rot.y, rot.z), MyAvatar.getJointTranslation("RightUpLeg")); rot = Quat.safeEulerAngles(MyAvatar.getJointRotation("RightLeg")); MyAvatar.setJointData("RightLeg", Quat.fromPitchYawRollDegrees(LOWER_LEG_ANGLE, rot.y, rot.z), MyAvatar.getJointTranslation("RightLeg")); rot = Quat.safeEulerAngles(MyAvatar.getJointRotation("LeftUpLeg")); MyAvatar.setJointData("LeftUpLeg", Quat.fromPitchYawRollDegrees(UPPER_LEG_ANGLE, rot.y, rot.z), MyAvatar.getJointTranslation("LeftUpLeg")); rot = Quat.safeEulerAngles(MyAvatar.getJointRotation("LeftLeg")); MyAvatar.setJointData("LeftLeg", Quat.fromPitchYawRollDegrees(LOWER_LEG_ANGLE, rot.y, rot.z), MyAvatar.getJointTranslation("LeftLeg")); } function leaveSitPose() { MyAvatar.clearJointData("RightUpLeg"); MyAvatar.clearJointData("LeftUpLeg"); MyAvatar.clearJointData("RightLeg"); MyAvatar.clearJointData("LeftLeg"); } function sitDown(position, rotation) { var eulers = Quat.safeEulerAngles(MyAvatar.orientation); eulers.y = Quat.safeEulerAngles(rotation).y; MyAvatar.position = Vec3.sum(position, Vec3.multiplyQbyV(props.rotation, sitTarget)); MyAvatar.orientation = Quat.fromPitchYawRollDegrees(eulers.x, eulers.y, eulers.z); enterSitPose(); state = SITTING; } this.preload = function(entityID) { // Load the sound and range from the entity userData fields, and note the position of the entity. debugPrint("chair preload"); entity = entityID; _this = this; settingsTimer = Script.setInterval(this.checkSettings, SETTINGS_INTERVAL_MSECS); }; this.maybeStand = function() { props = Entities.getEntityProperties(entity, [ "position", "rotation" ]); // First, check if the entity is far enough away to not need to do anything with it var howFar = howFarAway(props.position); if ((state === SITTING) && (howFar > sitDistance * HYSTERESIS)) { leaveSitPose(); Script.clearInterval(checkTimer); checkTimer = null; state = STANDING; debugPrint("Standing"); } } this.clickDownOnEntity = function(entityID, mouseEvent) { // If entity is clicked, sit props = Entities.getEntityProperties(entity, [ "position", "rotation" ]); if ((state === STANDING) && isSeatOpen(props.position, sitDistance)) { sitDown(props.position, props.rotation); checkTimer = Script.setInterval(this.maybeStand, CHECK_INTERVAL_MSECS); debugPrint("Sitting from mouse click"); } } this.startFarTrigger = function() { // If entity is far clicked, sit props = Entities.getEntityProperties(entity, [ "position", "rotation" ]); if ((state === STANDING) && isSeatOpen(props.position, sitDistance)) { sitDown(props.position, props.rotation); checkTimer = Script.setInterval(this.maybeStand, CHECK_INTERVAL_MSECS); debugPrint("Sitting from far trigger"); } } this.checkSettings = function() { var dataProps = Entities.getEntityProperties(entity, [ "userData" ]); if (dataProps.userData) { var data = JSON.parse(dataProps.userData); if (data.sitDistance) { if (!(sitDistance === data.sitDistance)) { debugPrint("Read new sit distance: " + data.sitDistance); } sitDistance = data.sitDistance; } if (data.sitTarget) { if (data.sitTarget.y && (data.sitTarget.y != sitTarget.y)) { debugPrint("Read new sitTarget.y: " + data.sitTarget.y); sitTarget.y = data.sitTarget.y; } if (data.sitTarget.x && (data.sitTarget.x != sitTarget.x)) { debugPrint("Read new sitTarget.x: " + data.sitTarget.x); sitTarget.x = data.sitTarget.x; } if (data.sitTarget.z && (data.sitTarget.z != sitTarget.z)) { debugPrint("Read new sitTarget.z: " + data.sitTarget.z); sitTarget.z = data.sitTarget.z; } } } } this.unload = function(entityID) { debugPrint("chair unload"); if (checkTimer) { Script.clearInterval(checkTimer); } if (settingsTimer) { Script.clearInterval(settingsTimer); } }; })