content/hifi-content/DomainContent/AvatarStore/attachmentItemScript.js
2022-02-13 22:49:05 +01:00

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();
});