350 lines
16 KiB
JavaScript
350 lines
16 KiB
JavaScript
//
|
|
// attachmentItemScript.js
|
|
//
|
|
// This script is a simplified version of the original attachmentItemScript.js
|
|
// Copyright 2017 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
|
|
//
|
|
/* global Render, Selection */
|
|
|
|
(function() {
|
|
var highlightGrabToggle = false;
|
|
var GRAB_SOUND = SoundCache.getSound(Script.resolvePath('sounds/sound1.wav'));
|
|
var ATTACH_SOUND = SoundCache.getSound(Script.resolvePath('sounds/sound2.wav'));
|
|
var DETACH_SOUND = SoundCache.getSound(Script.resolvePath('sounds/sound7.wav'));
|
|
var SHARED = Script.require('./attachmentZoneShared.js');
|
|
var MARKETPLACE_SHARED = Script.require('./marketplaceShared.js');
|
|
var LEFT_RIGHT_PLACEHOLDER = '[LR]';
|
|
var RELEASE_LIFETIME = 10;
|
|
var GRAB_LIST = "grabHighlightList";
|
|
var CANNOT_ATTACH_LIST = "noAttachJointList";
|
|
var TRIGGER_INTENSITY = 1.0;
|
|
var TRIGGER_TIME = 0.2;
|
|
var EMPTY_PARENT_ID = "{00000000-0000-0000-0000-000000000000}";
|
|
var ATTACH_SCALE = 3;
|
|
var REMOVED_FROM_PARENT_CHANNEL_BASE = "AvatarStoreRemovedFromParent";
|
|
var RELEASE_GRAB_CHANNEL_BASE = "AvatarStoreReleaseGrab";
|
|
var NOT_ATTACHED_DESTROY_RADIUS = 0.1;
|
|
var IN_CHECKOUT_SETTINGS = 'io.highfidelity.avatarStore.checkOut.isInside';
|
|
|
|
var removedFromParentChannel;
|
|
var releaseGrabChannel;
|
|
var releaseGrabHandler;
|
|
var _entityID;
|
|
var _attachmentData;
|
|
var _supportedJoints = [];
|
|
var _isNearGrabbingWithHand = false;
|
|
var isAttached;
|
|
var firstGrab = true;
|
|
var prevID = 0;
|
|
var listType = "entity";
|
|
var attachDistance;
|
|
var initialParentPosition;
|
|
var initialParentPositionSet = false;
|
|
var cachedMarketplaceItemData = null;
|
|
var grabOutlineStyle = {
|
|
outlineUnoccludedColor: { red: 45, green: 156, blue: 219 },
|
|
outlineOccludedColor: { red: 45, green: 156, blue: 219 },
|
|
fillUnoccludedColor: { red: 45, green: 156, blue: 219 },
|
|
fillOccludedColor: { red: 45, green: 156, blue: 219 },
|
|
outlineUnoccludedAlpha: 1,
|
|
outlineOccludedAlpha: 0,
|
|
fillUnoccludedAlpha: 0,
|
|
fillOccludedAlpha: 0,
|
|
outlineWidth: 3,
|
|
isOutlineSmooth: true
|
|
};
|
|
var noAttachOutlineStyle = {
|
|
outlineUnoccludedColor: { red: 255, green: 0, blue: 0 },
|
|
outlineOccludedColor: { red: 255, green: 0, blue: 0 },
|
|
fillUnoccludedColor: { red: 255, green: 0, blue: 0 },
|
|
fillOccludedColor: { red: 255, green: 0, blue: 0 },
|
|
outlineUnoccludedAlpha: 1,
|
|
outlineOccludedAlpha: 0,
|
|
fillUnoccludedAlpha: 0,
|
|
fillOccludedAlpha: 0,
|
|
outlineWidth: 3,
|
|
isOutlineSmooth: true
|
|
};
|
|
var overlayMatch;
|
|
|
|
var attachFunction = function() {
|
|
attachDistance = MyAvatar.getEyeHeight() / ATTACH_SCALE;
|
|
|
|
};
|
|
|
|
var lastDesktopSupportedJointIndex = -1;
|
|
|
|
var playAttachSound = function() {
|
|
if (ATTACH_SOUND.downloaded) {
|
|
Audio.playSound(ATTACH_SOUND, {
|
|
position: MyAvatar.position,
|
|
volume: SHARED.AUDIO_VOLUME_LEVEL,
|
|
localOnly: true
|
|
});
|
|
}
|
|
};
|
|
|
|
function checkReleaseGrabOnParent() {
|
|
// If placed back within NOT_ATTACHED_DESTROY_RADIUS of the original parent entity
|
|
// and it is not attached then destroy it (if the user is putting it back)
|
|
var properties = Entities.getEntityProperties(_entityID, ['userData', 'position']);
|
|
var isAttached = JSON.parse(properties.userData).Attachment.attached;
|
|
if (!isAttached && initialParentPositionSet &&
|
|
Vec3.distance(initialParentPosition, properties.position) < NOT_ATTACHED_DESTROY_RADIUS) {
|
|
Entities.deleteEntity(_entityID);
|
|
}
|
|
}
|
|
|
|
var logAttachEvent = function(marketplaceID) {
|
|
if (!cachedMarketplaceItemData) {
|
|
MARKETPLACE_SHARED.requestMarketplaceDataForID(marketplaceID, function(error, marketplaceItemData) {
|
|
if (!error) {
|
|
cachedMarketplaceItemData = marketplaceItemData;
|
|
logAttachEvent(marketplaceID);
|
|
} else {
|
|
print('Error retrieving logAttachEvent marketplace data!');
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
|
|
UserActivityLogger.logAction('avatarStore_attach', {
|
|
name: cachedMarketplaceItemData.name,
|
|
creator: cachedMarketplaceItemData.creator,
|
|
avatar: MyAvatar.skeletonModelURL,
|
|
marketplaceID: marketplaceID
|
|
});
|
|
};
|
|
|
|
function AttachableItem() {
|
|
|
|
}
|
|
|
|
AttachableItem.prototype = {
|
|
preload : function(entityID) {
|
|
_entityID = entityID;
|
|
Selection.enableListHighlight(GRAB_LIST, grabOutlineStyle);
|
|
Selection.enableListHighlight(CANNOT_ATTACH_LIST, noAttachOutlineStyle);
|
|
Selection.clearSelectedItemsList(GRAB_LIST);
|
|
Selection.clearSelectedItemsList(CANNOT_ATTACH_LIST);
|
|
var properties = Entities.getEntityProperties(entityID, ['parentID', 'userData']);
|
|
var userData = JSON.parse(properties.userData);
|
|
_attachmentData = userData.Attachment;
|
|
var _marketplaceID = userData.marketplaceID;
|
|
|
|
if (_attachmentData.joint.indexOf(LEFT_RIGHT_PLACEHOLDER) !== -1) {
|
|
var baseJoint = _attachmentData.joint.substring(4);
|
|
_supportedJoints.push("Left".concat(baseJoint));
|
|
_supportedJoints.push("Right".concat(baseJoint));
|
|
} else {
|
|
_supportedJoints.push(_attachmentData.joint);
|
|
}
|
|
|
|
isAttached = _attachmentData.attached;
|
|
|
|
if (Entities.getNestableType(properties.parentID) !== "avatar" && !isAttached) {
|
|
removedFromParentChannel = REMOVED_FROM_PARENT_CHANNEL_BASE + properties.parentID;
|
|
Messages.subscribe(removedFromParentChannel);
|
|
}
|
|
|
|
releaseGrabChannel = RELEASE_GRAB_CHANNEL_BASE + entityID;
|
|
Messages.subscribe(releaseGrabChannel);
|
|
releaseGrabHandler = function(channel, data, sender) {
|
|
if (channel === releaseGrabChannel) {
|
|
checkReleaseGrabOnParent();
|
|
}
|
|
};
|
|
Messages.messageReceived.connect(releaseGrabHandler);
|
|
|
|
Entities.editEntity(entityID, {marketplaceID: _marketplaceID});
|
|
MyAvatar.scaleChanged.connect(attachFunction);
|
|
attachDistance = MyAvatar.getEyeHeight() / ATTACH_SCALE;
|
|
|
|
// We only want to store the initial parent position for the original parent wearable entity
|
|
if (properties.parentID !== EMPTY_PARENT_ID && Entities.getNestableType(properties.parentID) !== "avatar") {
|
|
initialParentPosition = Entities.getEntityProperties(properties.parentID, ['position']).position;
|
|
initialParentPositionSet = true;
|
|
}
|
|
},
|
|
unload: function() {
|
|
MyAvatar.scaleChanged.disconnect(attachFunction);
|
|
Messages.unsubscribe(releaseGrabChannel);
|
|
Messages.unsubscribe(removedFromParentChannel);
|
|
},
|
|
/**
|
|
* Local remote function to be called from desktopAttachment.js whenever a click event is registered.
|
|
* @param entityID current entityID
|
|
* @param args array of arguments to be passed in from remote server
|
|
*/
|
|
desktopAttach: function(entityID, args) {
|
|
var newEntityProperties = Entities.getEntityProperties(_entityID, ['dimensions', 'userData']);
|
|
var attachmentData = null;
|
|
var marketplaceID = null;
|
|
SHARED.touchJSONUserData(newEntityProperties, function(userData) {
|
|
userData.Attachment.attached = true;
|
|
attachmentData = userData.Attachment;
|
|
marketplaceID = userData.marketplaceID;
|
|
});
|
|
lastDesktopSupportedJointIndex = (lastDesktopSupportedJointIndex + 1) % _supportedJoints.length;
|
|
|
|
var defaultPosition = {x: 0, y: 0, z: 0};
|
|
if (attachmentData.defaultPosition !== undefined) {
|
|
defaultPosition = attachmentData.defaultPosition;
|
|
}
|
|
var defaultRotation = {x: 0, y: 0, z: 0, w: 1};
|
|
if (attachmentData.defaultRotation !== undefined) {
|
|
defaultRotation = attachmentData.defaultRotation;
|
|
}
|
|
var defaultDimensions = newEntityProperties.dimensions;
|
|
if (attachmentData.defaultDimensions !== undefined) {
|
|
defaultDimensions = attachmentData.defaultDimensions;
|
|
}
|
|
var jointIndex = MyAvatar.getJointIndex(_supportedJoints[lastDesktopSupportedJointIndex]);
|
|
if (jointIndex === -1) {
|
|
// fail when no joint index is found and delete entity since a new one is being created already.
|
|
Entities.deleteEntity(_entityID);
|
|
return;
|
|
}
|
|
|
|
// Finally, if all worked out, set the attachment properties.
|
|
Entities.editEntity(_entityID, {
|
|
visible: true,
|
|
localPosition: defaultPosition,
|
|
localRotation: defaultRotation,
|
|
localVelocity: {x: 0, y: 0, z: 0},
|
|
localAngularVelocity: {x: 0, y: 0, z: 0},
|
|
dimensions: defaultDimensions,
|
|
parentID: MyAvatar.sessionUUID,
|
|
parentJointIndex: jointIndex,
|
|
userData: newEntityProperties.userData,
|
|
lifetime: -1
|
|
});
|
|
playAttachSound();
|
|
|
|
if (marketplaceID) {
|
|
logAttachEvent(marketplaceID);
|
|
}
|
|
},
|
|
startNearGrab: function(entityID, args) {
|
|
if (prevID !== entityID) {
|
|
var jointName = _attachmentData.joint;
|
|
var jointIndex = MyAvatar.getJointIndex(jointName);
|
|
if (jointIndex !== -1) {
|
|
if (highlightGrabToggle){
|
|
Selection.addToSelectedItemsList(GRAB_LIST, listType, entityID);
|
|
}
|
|
} else {
|
|
Selection.addToSelectedItemsList(CANNOT_ATTACH_LIST, listType, entityID);
|
|
}
|
|
if (Settings.getValue(IN_CHECKOUT_SETTINGS,false)) {
|
|
var userDataObject = JSON.parse(Entities.getEntityProperties(entityID, 'userData').userData);
|
|
overlayMatch = userDataObject.replicaOverlayID;
|
|
Selection.addToSelectedItemsList(GRAB_LIST, "overlay", overlayMatch);
|
|
}
|
|
prevID = entityID;
|
|
}
|
|
if (firstGrab) {
|
|
if (!Entities.getEntityProperties(entityID, 'visible').visible) {
|
|
Entities.editEntity(entityID, {visible: true});
|
|
}
|
|
firstGrab = false;
|
|
attachDistance = MyAvatar.getEyeHeight() / ATTACH_SCALE;
|
|
}
|
|
if (GRAB_SOUND.downloaded) {
|
|
Audio.playSound(GRAB_SOUND, {
|
|
position: MyAvatar.position,
|
|
volume: SHARED.AUDIO_VOLUME_LEVEL,
|
|
localOnly: true
|
|
});
|
|
}
|
|
},
|
|
continueNearGrab: function(entity, args) {
|
|
_isNearGrabbingWithHand = true;
|
|
},
|
|
releaseGrab: function(entityID, args) {
|
|
if (!_isNearGrabbingWithHand) {
|
|
return;
|
|
}
|
|
_isNearGrabbingWithHand = false;
|
|
var hand = args[0];
|
|
var properties = Entities.getEntityProperties(entityID, ['parentID', 'userData', 'position']);
|
|
if (prevID !== 0) {
|
|
Selection.removeFromSelectedItemsList(GRAB_LIST, listType, prevID);
|
|
Selection.removeFromSelectedItemsList(CANNOT_ATTACH_LIST, listType, prevID);
|
|
Selection.removeFromSelectedItemsList(GRAB_LIST, "overlay", overlayMatch);
|
|
prevID = 0;
|
|
}
|
|
if (Entities.getNestableType(properties.parentID) === "entity") {
|
|
Messages.sendMessage(removedFromParentChannel, "Removed Item :" + entityID);
|
|
Messages.unsubscribe(removedFromParentChannel);
|
|
Entities.editEntity(entityID, {parentID: EMPTY_PARENT_ID});
|
|
}
|
|
var userData = properties.userData;
|
|
var position = properties.position;
|
|
var attachmentData = JSON.parse(userData).Attachment;
|
|
isAttached = attachmentData.attached;
|
|
if (!isAttached) {
|
|
_supportedJoints.forEach(function(joint) {
|
|
var jointPosition = MyAvatar.getJointPosition(joint);
|
|
if (Vec3.distance(position, jointPosition) <= attachDistance) {
|
|
// Check that we are not holding onto an arm attachment in a hand
|
|
if (joint.toLowerCase().indexOf(hand) !== -1) {
|
|
return;
|
|
}
|
|
var newEntityProperties = Entities.getEntityProperties(_entityID, 'userData');
|
|
var marketplaceID = null;
|
|
SHARED.touchJSONUserData(newEntityProperties, function(userData) {
|
|
marketplaceID = userData.marketplaceID;
|
|
userData.Attachment.attached = true;
|
|
});
|
|
Entities.editEntity(_entityID, {
|
|
parentID: MyAvatar.sessionUUID,
|
|
parentJointIndex: MyAvatar.getJointIndex(joint),
|
|
localVelocity: {x: 0, y: 0, z: 0},
|
|
localAngularVelocity: {x: 0, y: 0, z: 0},
|
|
userData: newEntityProperties.userData,
|
|
lifetime: -1
|
|
});
|
|
playAttachSound();
|
|
isAttached = true;
|
|
Controller.triggerHapticPulse(TRIGGER_INTENSITY, TRIGGER_TIME, hand);
|
|
if (marketplaceID) {
|
|
logAttachEvent(marketplaceID);
|
|
}
|
|
}
|
|
});
|
|
} else if (isAttached) {
|
|
var jointPosition = (properties.parentID === MyAvatar.sessionUUID) ?
|
|
MyAvatar.getJointPosition(properties.parentJointIndex) :
|
|
AvatarList.getAvatar(properties.parentID).getJointPosition(properties.parentJointIndex);
|
|
if (Vec3.distance(position, jointPosition) > attachDistance) {
|
|
var newDetachEntityProperties = Entities.getEntityProperties(entityID);
|
|
SHARED.touchJSONUserData(newDetachEntityProperties, function(userData) {
|
|
userData.Attachment.attached = false;
|
|
});
|
|
Entities.editEntity(_entityID, {
|
|
parentID: EMPTY_PARENT_ID,
|
|
lifetime: Entities.getEntityProperties(_entityID, 'age').age + RELEASE_LIFETIME,
|
|
userData: newDetachEntityProperties.userData
|
|
});
|
|
if (DETACH_SOUND.downloaded) {
|
|
Audio.playSound(DETACH_SOUND, {
|
|
position: MyAvatar.position,
|
|
volume: SHARED.AUDIO_VOLUME_LEVEL,
|
|
localOnly: true
|
|
});
|
|
}
|
|
isAttached = false;
|
|
Controller.triggerHapticPulse(TRIGGER_INTENSITY, TRIGGER_TIME, hand);
|
|
}
|
|
}
|
|
|
|
Messages.sendMessage(releaseGrabChannel, "Released Grab: " + entityID);
|
|
checkReleaseGrabOnParent();
|
|
}
|
|
};
|
|
return new AttachableItem();
|
|
});
|