mirror of
https://github.com/overte-org/overte.git
synced 2025-04-29 18:22:38 +02:00
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();
|
|
}) |