Needs a lot of cleanup. Data has been de-duplicated, and where identical copies existed, one of them has been replaced with a symlink. Some files have been excluded, such as binaries, installers and debug dumps. Some of that may still be present.
389 lines
No EOL
17 KiB
JavaScript
389 lines
No EOL
17 KiB
JavaScript
// shopItemEntityScript.js
|
|
//
|
|
// This script makes the shop items react properly to the grab (see shopItemGrab.js) and gives it the capability to interact with the shop environment (inspection entity and cart)
|
|
// The first time the item is grabbed a copy of itself is created on the shelf.
|
|
// If the item isn't held by the user it can be inInspect or inCart, otherwise it's destroyed
|
|
// The start and release grab methods handle the creation of the item copy, UI, inspection entity
|
|
// If the item is released into a valid zone, the doSomething() method of that zone is called and the item sends its ID
|
|
|
|
// Created by Alessandro Signa and Edgar Pironti on 01/13/2016
|
|
// 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 utilitiesScript = Script.resolvePath("../../libraries/utils.js");
|
|
var overlayManagerScript = Script.resolvePath("../../libraries/overlayManager.js");
|
|
var inspectEntityScript = Script.resolvePath("../inspect/shopInspectEntityScript.js");
|
|
|
|
Script.include(utilitiesScript);
|
|
Script.include(overlayManagerScript);
|
|
|
|
|
|
var RED_IMAGE_URL = "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/inspRED.png";
|
|
var GREEN_IMAGE_URL = "https://dl.dropboxusercontent.com/u/14127429/FBX/VRshop/inspGREEN.png";
|
|
var MIN_DIMENSION_THRESHOLD = null;
|
|
var MAX_DIMENSION_THRESHOLD = null;
|
|
var PENETRATION_THRESHOLD = 0.2;
|
|
var MAPPING_NAME = "controllerMapping_Inspection";
|
|
var SHOPPING_CART_NAME = "Shopping cart";
|
|
|
|
var _this;
|
|
var onShelf = true;
|
|
var inspecting = false;
|
|
var inCart = false;
|
|
var overlayInspectRed = true;
|
|
var zoneID = null;
|
|
var newPosition;
|
|
var originalDimensions = null;
|
|
var deltaLX = 0;
|
|
var deltaLY = 0;
|
|
var deltaRX = 0;
|
|
var deltaRY = 0;
|
|
var inspectingEntity = null;
|
|
var inspectPanel = null;
|
|
var background = null;
|
|
var textCart = null;
|
|
var cartID = null;
|
|
|
|
// this is the "constructor" for the entity as a JS object we don't do much here, but we do want to remember
|
|
// our this object, so we can access it in cases where we're called without a this (like in the case of various global signals)
|
|
ItemEntity = function() {
|
|
_this = this;
|
|
};
|
|
|
|
function update(deltaTime) {
|
|
if(inspecting){
|
|
_this.orientationPositionUpdate();
|
|
}
|
|
};
|
|
|
|
|
|
ItemEntity.prototype = {
|
|
|
|
// preload() will be called when the entity has become visible (or known) to the interface
|
|
// it gives us a chance to set our local JavaScript object up. In this case it means:
|
|
// * remembering our entityID, so we can access it in cases where we're called without an entityID
|
|
// * connecting to the update signal so we can check our grabbed state
|
|
preload: function(entityID) {
|
|
this.entityID = entityID;
|
|
print("PRELOAD: " + Entities.getEntityProperties(this.entityID).name + " " + entityID + " at:");
|
|
Vec3.print(Entities.getEntityProperties(this.entityID).name, Entities.getEntityProperties(this.entityID).position);
|
|
|
|
Script.update.connect(update);
|
|
|
|
MIN_DIMENSION_THRESHOLD = Vec3.length(Entities.getEntityProperties(this.entityID).dimensions)/2;
|
|
MAX_DIMENSION_THRESHOLD = Vec3.length(Entities.getEntityProperties(this.entityID).dimensions)*2;
|
|
|
|
},
|
|
|
|
//create a rectangle red or green do guide the user in putting the item in the inspection area
|
|
createInspectOverlay: function (entityBindID) {
|
|
inspectPanel = new OverlayPanel({
|
|
anchorPositionBinding: { entity: entityBindID },
|
|
anchorRotationBinding: { entity: entityBindID },
|
|
isFacingAvatar: false
|
|
});
|
|
|
|
background = new Image3DOverlay({
|
|
url: RED_IMAGE_URL,
|
|
dimensions: {
|
|
x: 0.6,
|
|
y: 0.6,
|
|
},
|
|
isFacingAvatar: false,
|
|
alpha: 1,
|
|
ignoreRayIntersection: false,
|
|
offsetPosition: {
|
|
x: 0,
|
|
y: 0,
|
|
z: 0
|
|
},
|
|
emissive: true,
|
|
});
|
|
|
|
inspectPanel.addChild(background);
|
|
},
|
|
|
|
//add an overlay on the item grabbed by the customer to give him the feedback if the item is inside the bounding box of the cart
|
|
createCartOverlay: function (entityBindID) {
|
|
cartPanel = new OverlayPanel({
|
|
anchorPositionBinding: { entity: entityBindID },
|
|
offsetPosition: { x: 0, y: 0.2, z: 0.1 },
|
|
isFacingAvatar: true,
|
|
|
|
});
|
|
var entityProperties = Entities.getEntityProperties(entityBindID);
|
|
var userDataObj = JSON.parse(entityProperties.userData);
|
|
var availabilityNumber = userDataObj.infoKey.availability;
|
|
|
|
textCart = new Text3DOverlay({
|
|
text: availabilityNumber > 0 ? "Store the item!" : "Not available!",
|
|
isFacingAvatar: false,
|
|
alpha: 1.0,
|
|
ignoreRayIntersection: true,
|
|
dimensions: { x: 0, y: 0 },
|
|
backgroundColor: { red: 255, green: 255, blue: 255 },
|
|
color: { red: 0, green: 0, blue: 0 },
|
|
topMargin: 0.00625,
|
|
leftMargin: 0.00625,
|
|
bottomMargin: 0.1,
|
|
rightMargin: 0.00625,
|
|
lineHeight: 0.02,
|
|
alpha: 1,
|
|
backgroundAlpha: 0.3,
|
|
visible: false
|
|
});
|
|
|
|
cartPanel.addChild(textCart);
|
|
|
|
},
|
|
|
|
|
|
|
|
setCartOverlayVisible: function () {
|
|
textCart.visible = true;
|
|
},
|
|
|
|
setCartOverlayNotVisible: function () {
|
|
textCart.visible = false;
|
|
},
|
|
|
|
changeOverlayColor: function () {
|
|
if (overlayInspectRed) {
|
|
//print ("Change color of overlay to green");
|
|
overlayInspectRed = false;
|
|
background.url = GREEN_IMAGE_URL;
|
|
} else {
|
|
//print ("Change color of overlay to red");
|
|
overlayInspectRed = true;
|
|
background.url = RED_IMAGE_URL;
|
|
}
|
|
},
|
|
|
|
//What is done by this methos changes acoordingly to the previous state of the item - onShelf , inCart, inInspect
|
|
startNearGrab: function () {
|
|
|
|
//we have to distinguish if the user who is grabbing has a cart or not
|
|
//if he does it is a buyer, otherwise he probably want to do a review
|
|
var thisItemPosition = Entities.getEntityProperties(_this.entityID).position;
|
|
|
|
// if onShelf is true, this is the first grab
|
|
if (onShelf === true) {
|
|
|
|
var foundEntities = Entities.findEntities(thisItemPosition, 5);
|
|
foundEntities.forEach( function (foundEntityID) {
|
|
var entityName = Entities.getEntityProperties(foundEntityID).name;
|
|
if (entityName == SHOPPING_CART_NAME) {
|
|
var cartOwnerID = getEntityCustomData('ownerKey', foundEntityID, null).ownerID;
|
|
if (cartOwnerID == MyAvatar.sessionUUID) {
|
|
cartID = foundEntityID;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Create a copy of this entity if it is the first grab
|
|
print("creating a copy of the grabbed dentity");
|
|
var entityProperties = Entities.getEntityProperties(_this.entityID);
|
|
|
|
var entityOnShelf = Entities.addEntity({
|
|
type: entityProperties.type,
|
|
name: entityProperties.name,
|
|
position: thisItemPosition,
|
|
dimensions: entityProperties.dimensions,
|
|
rotation: entityProperties.rotation,
|
|
collisionsWillMove: false,
|
|
ignoreForCollisions: true,
|
|
modelURL: entityProperties.modelURL,
|
|
shapeType: entityProperties.shapeType,
|
|
originalTextures: entityProperties.originalTextures,
|
|
script: entityProperties.script,
|
|
userData: entityProperties.userData
|
|
});
|
|
|
|
var tempUserDataObj = JSON.parse(entityProperties.userData);
|
|
var availabilityNumber = tempUserDataObj.infoKey.availability;
|
|
|
|
if (availabilityNumber > 0 && cartID) {
|
|
tempUserDataObj.infoKey.availability = tempUserDataObj.infoKey.availability - 1;
|
|
setEntityCustomData('infoKey', entityOnShelf, tempUserDataObj.infoKey);
|
|
}
|
|
|
|
setEntityCustomData('statusKey', _this.entityID, {
|
|
status: "inHand"
|
|
});
|
|
|
|
onShelf = false;
|
|
setEntityCustomData('ownerKey', _this.entityID, {
|
|
ownerID: MyAvatar.sessionUUID
|
|
});
|
|
originalDimensions = entityProperties.dimensions;
|
|
|
|
}
|
|
|
|
//if cartID is defined the user is a buyer
|
|
if (cartID) {
|
|
// Everytime we grab, we create the inspectEntity and the inspectAreaOverlay in front of the avatar
|
|
if(!inspecting) {
|
|
inspectingEntity = Entities.addEntity({
|
|
type: "Box",
|
|
name: "inspectionEntity",
|
|
dimensions: {x: 0.5, y: 0.5, z: 0.5},
|
|
collisionsWillMove: false,
|
|
ignoreForCollisions: false,
|
|
visible: false,
|
|
script: inspectEntityScript,
|
|
userData: JSON.stringify({
|
|
ownerKey: {
|
|
ownerID: MyAvatar.sessionUUID
|
|
},
|
|
itemKey: {
|
|
itemID: _this.entityID
|
|
},
|
|
grabbableKey: {
|
|
grabbable: false
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
Entities.editEntity(_this.entityID, { ignoreForCollisions: false });
|
|
_this.createInspectOverlay(inspectingEntity);
|
|
_this.createCartOverlay(_this.entityID);
|
|
|
|
if (inspecting === true) {
|
|
inspecting = false;
|
|
Entities.editEntity(_this.entityID, { dimensions: originalDimensions });
|
|
Controller.disableMapping(MAPPING_NAME);
|
|
setEntityCustomData('statusKey', _this.entityID, {
|
|
status: "inHand"
|
|
});
|
|
} else if (inCart === true) {
|
|
inCart = false;
|
|
Entities.editEntity(_this.entityID, { dimensions: originalDimensions });
|
|
setEntityCustomData('statusKey', _this.entityID, {
|
|
status: "inHand"
|
|
});
|
|
var dataJSON = {
|
|
id: _this.entityID
|
|
};
|
|
var dataArray = [JSON.stringify(dataJSON)];
|
|
Entities.callEntityMethod(zoneID, 'refreshCartContent', dataArray);
|
|
}
|
|
}
|
|
},
|
|
|
|
continueNearGrab: function () {
|
|
},
|
|
|
|
//Every time the item is released I have to check what's the role of the user and where the release happens
|
|
releaseGrab: function () {
|
|
|
|
//if cart ID is not defined destroy the item whatever the zone is because the user is a reviewer
|
|
if (!cartID) {
|
|
Entities.deleteEntity(this.entityID);
|
|
return;
|
|
}
|
|
|
|
Entities.editEntity(_this.entityID, { ignoreForCollisions: true });
|
|
// Destroy overlay
|
|
inspectPanel.destroy();
|
|
cartPanel.destroy();
|
|
inspectPanel = cartPanel = null;
|
|
|
|
if (zoneID !== null) {
|
|
var dataJSON = {
|
|
id: _this.entityID
|
|
};
|
|
var dataArray = [JSON.stringify(dataJSON)];
|
|
Entities.callEntityMethod(zoneID, 'doSomething', dataArray);
|
|
|
|
var statusObj = getEntityCustomData('statusKey', _this.entityID, null);
|
|
|
|
//There are two known zones where the relase can happen: inspecting area and cart
|
|
if (statusObj.status == "inInspect") {
|
|
inspecting = true;
|
|
|
|
var mapping = Controller.newMapping(MAPPING_NAME);
|
|
mapping.from(Controller.Standard.LX).to(function (value) {
|
|
deltaLX = value;
|
|
});
|
|
mapping.from(Controller.Standard.LY).to(function (value) {
|
|
deltaLY = value;
|
|
});
|
|
mapping.from(Controller.Standard.RX).to(function (value) {
|
|
deltaRX = value;
|
|
});
|
|
mapping.from(Controller.Standard.RY).to(function (value) {
|
|
deltaRY = value;
|
|
});
|
|
Controller.enableMapping(MAPPING_NAME);
|
|
} else if (statusObj.status == "inCart") {
|
|
Entities.deleteEntity(inspectingEntity);
|
|
inspectingEntity = null;
|
|
|
|
var entityProperties = Entities.getEntityProperties(this.entityID);
|
|
var userDataObj = JSON.parse(entityProperties.userData);
|
|
var availabilityNumber = userDataObj.infoKey.availability;
|
|
//if the item is no more available, destroy it
|
|
if (availabilityNumber == 0) {
|
|
Entities.deleteEntity(this.entityID);
|
|
}
|
|
print("inCart is TRUE");
|
|
inCart = true;
|
|
} else { // any other zone
|
|
//print("------------zoneID is something");
|
|
Entities.deleteEntity(inspectingEntity);
|
|
inspectingEntity = null;
|
|
}
|
|
|
|
} else { // ZoneID is null, released somewhere that is not a zone.
|
|
//print("------------zoneID is null");
|
|
Entities.deleteEntity(inspectingEntity);
|
|
inspectingEntity = null;
|
|
Entities.deleteEntity(this.entityID);
|
|
}
|
|
|
|
},
|
|
|
|
//Analysing the collisions we can define the value of zoneID
|
|
collisionWithEntity: function(myID, otherID, collisionInfo) {
|
|
var penetrationValue = Vec3.length(collisionInfo.penetration);
|
|
if (penetrationValue > PENETRATION_THRESHOLD && zoneID === null) {
|
|
zoneID = otherID;
|
|
print("Item IN: " + Entities.getEntityProperties(zoneID).name);
|
|
} else if (penetrationValue < PENETRATION_THRESHOLD && zoneID !== null && otherID == zoneID) {
|
|
print("Item OUT: " + Entities.getEntityProperties(zoneID).name);
|
|
zoneID = null;
|
|
}
|
|
},
|
|
|
|
//this method handles the rotation and scale of the item while in inspect and also guarantees to keep the item in the proper position. It's done every update
|
|
orientationPositionUpdate: function() {
|
|
//position
|
|
var inspectingEntityPosition = Entities.getEntityProperties(inspectingEntity).position; //at this time inspectingEntity is a valid entity
|
|
var inspectingEntityRotation = Entities.getEntityProperties(inspectingEntity).rotation;
|
|
newPosition = Vec3.sum(inspectingEntityPosition, Vec3.multiply(Quat.getFront(inspectingEntityRotation), -0.2)); //put the item near to the face of the user
|
|
Entities.editEntity(_this.entityID, { position: newPosition });
|
|
//orientation
|
|
var newRotation = Quat.multiply(Entities.getEntityProperties(_this.entityID).rotation, Quat.fromPitchYawRollDegrees(deltaRY*10, deltaRX*10, 0))
|
|
Entities.editEntity(_this.entityID, { rotation: newRotation });
|
|
//zoom
|
|
var oldDimension = Entities.getEntityProperties(_this.entityID).dimensions;
|
|
var scaleFactor = (deltaLY * 0.1) + 1;
|
|
if (!((Vec3.length(oldDimension) < MIN_DIMENSION_THRESHOLD && scaleFactor < 1) || (Vec3.length(oldDimension) > MAX_DIMENSION_THRESHOLD && scaleFactor > 1))) {
|
|
var newDimension = Vec3.multiply(oldDimension, scaleFactor);
|
|
Entities.editEntity(_this.entityID, { dimensions: newDimension });
|
|
}
|
|
},
|
|
|
|
unload: function (entityID) {
|
|
Script.update.disconnect(update);
|
|
}
|
|
};
|
|
|
|
// entity scripts always need to return a newly constructed object of our type
|
|
return new ItemEntity();
|
|
}) |