diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 50e25287f1..f3d8ea2344 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -282,7 +282,7 @@ Menu::Menu() { // Navigate > Show Address Bar addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L, - dialogsManager.data(), SLOT(showAddressBar())); + dialogsManager.data(), SLOT(toggleAddressBar())); // Navigate > LocationBookmarks related menus -- Note: the LocationBookmarks class adds its own submenus here. auto locationBookmarks = DependencyManager::get(); diff --git a/interface/src/scripting/WalletScriptingInterface.cpp b/interface/src/scripting/WalletScriptingInterface.cpp index 71a7076bdf..e2158b9fd7 100644 --- a/interface/src/scripting/WalletScriptingInterface.cpp +++ b/interface/src/scripting/WalletScriptingInterface.cpp @@ -16,6 +16,7 @@ CheckoutProxy::CheckoutProxy(QObject* qmlObject, QObject* parent) : QmlWrapper(q } WalletScriptingInterface::WalletScriptingInterface() { + } void WalletScriptingInterface::refreshWalletStatus() { @@ -26,4 +27,19 @@ void WalletScriptingInterface::refreshWalletStatus() { void WalletScriptingInterface::setWalletStatus(const uint& status) { _walletStatus = status; emit DependencyManager::get()->walletStatusResult(status); +} + +void WalletScriptingInterface::proveAvatarEntityOwnershipVerification(const QUuid& entityID) { + QSharedPointer contextOverlayInterface = DependencyManager::get(); + EntityItemProperties entityProperties = DependencyManager::get()->getEntityProperties(entityID, + contextOverlayInterface->getEntityPropertyFlags()); + if (entityProperties.getClientOnly()) { + if (!entityID.isNull() && entityProperties.getCertificateID().length() > 0) { + contextOverlayInterface->requestOwnershipVerification(entityID); + } else { + qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is null or not a certified item"; + } + } else { + qCDebug(entities) << "Failed to prove ownership of:" << entityID << "is not an avatar entity"; + } } \ No newline at end of file diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 5469e732c7..9e40aad087 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -21,6 +21,7 @@ #include #include "Application.h" #include "commerce/Wallet.h" +#include "ui/overlays/ContextOverlayInterface.h" class CheckoutProxy : public QmlWrapper { Q_OBJECT @@ -39,6 +40,7 @@ public: Q_INVOKABLE void refreshWalletStatus(); Q_INVOKABLE uint getWalletStatus() { return _walletStatus; } + Q_INVOKABLE void proveAvatarEntityOwnershipVerification(const QUuid& entityID); // setWalletStatus() should never be made Q_INVOKABLE. If it were, // scripts could cause the Wallet to incorrectly report its status. void setWalletStatus(const uint& status); @@ -46,6 +48,8 @@ public: signals: void walletStatusChanged(); void walletNotSetup(); + void ownershipVerificationSuccess(const QUuid& entityID); + void ownershipVerificationFailed(const QUuid& entityID); private: uint _walletStatus; diff --git a/interface/src/ui/AddressBarDialog.h b/interface/src/ui/AddressBarDialog.h index 66f208ca90..7806436774 100644 --- a/interface/src/ui/AddressBarDialog.h +++ b/interface/src/ui/AddressBarDialog.h @@ -22,7 +22,7 @@ class AddressBarDialog : public OffscreenQmlDialog { Q_PROPERTY(bool backEnabled READ backEnabled NOTIFY backEnabledChanged) Q_PROPERTY(bool forwardEnabled READ forwardEnabled NOTIFY forwardEnabledChanged) Q_PROPERTY(bool useFeed READ useFeed WRITE setUseFeed NOTIFY useFeedChanged) - Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl) + Q_PROPERTY(QString metaverseServerUrl READ metaverseServerUrl CONSTANT) public: AddressBarDialog(QQuickItem* parent = nullptr); diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index ff2d4868df..310a4cc1cd 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -58,7 +58,7 @@ void DialogsManager::showAddressBar() { hmd->openTablet(); } qApp->setKeyboardFocusOverlay(hmd->getCurrentTabletScreenID()); - emit addressBarShown(true); + setAddressBarVisible(true); } void DialogsManager::hideAddressBar() { @@ -71,7 +71,7 @@ void DialogsManager::hideAddressBar() { hmd->closeTablet(); } qApp->setKeyboardFocusOverlay(UNKNOWN_OVERLAY_ID); - emit addressBarShown(false); + setAddressBarVisible(false); } void DialogsManager::showFeed() { @@ -157,6 +157,24 @@ void DialogsManager::hmdToolsClosed() { } } +void DialogsManager::toggleAddressBar() { + auto tabletScriptingInterface = DependencyManager::get(); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + + const bool addressBarLoaded = tablet->isPathLoaded(TABLET_ADDRESS_DIALOG); + + if (_addressBarVisible || addressBarLoaded) { + hideAddressBar(); + } else { + showAddressBar(); + } +} + +void DialogsManager::setAddressBarVisible(bool addressBarVisible) { + _addressBarVisible = addressBarVisible; + emit addressBarShown(_addressBarVisible); +} + void DialogsManager::showTestingResults() { if (!_testingDialog) { _testingDialog = new TestingDialog(qApp->getWindow()); diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 3a40b15a3b..f17ac39a7e 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -39,6 +39,7 @@ public: QPointer getOctreeStatsDialog() const { return _octreeStatsDialog; } QPointer getTestingDialog() const { return _testingDialog; } void emitAddressBarShown(bool visible) { emit addressBarShown(visible); } + void setAddressBarVisible(bool addressBarVisible); public slots: void showAddressBar(); @@ -52,6 +53,7 @@ public slots: void hmdTools(bool showTools); void showDomainConnectionDialog(); void showTestingResults(); + void toggleAddressBar(); // Application Update void showUpdateDialog(); @@ -78,7 +80,7 @@ private: QPointer _octreeStatsDialog; QPointer _testingDialog; QPointer _domainConnectionDialog; - bool _closeAddressBar { false }; + bool _addressBarVisible { false }; }; #endif // hifi_DialogsManager_h diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index d4138941ae..ed7b811fb0 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -266,6 +266,93 @@ void ContextOverlayInterface::contextOverlays_hoverLeaveEntity(const EntityItemI } } +void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID) { + + setLastInspectedEntity(entityID); + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); + + auto nodeList = DependencyManager::get(); + + if (entityProperties.getClientOnly()) { + if (entityProperties.verifyStaticCertificateProperties()) { + SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); + + if (entityServer) { + QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); + QNetworkRequest networkRequest; + networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); + requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); + QJsonObject request; + request["certificate_id"] = entityProperties.getCertificateID(); + networkRequest.setUrl(requestURL); + + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + + if (networkReply->error() == QNetworkReply::NoError) { + if (!jsonObject["invalid_reason"].toString().isEmpty()) { + qCDebug(entities) << "invalid_reason not empty"; + } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { + qCDebug(entities) << "'transfer_status' is 'failed'"; + } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { + qCDebug(entities) << "'transfer_status' is 'pending'"; + } else { + QString ownerKey = jsonObject["transfer_recipient_key"].toString(); + + QByteArray certID = entityProperties.getCertificateID().toUtf8(); + QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); + QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); + + int certIDByteArraySize = certID.length(); + int textByteArraySize = text.length(); + int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); + + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, + certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(textByteArraySize); + challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); + challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(text); + challengeOwnershipPacket->write(nodeToChallengeByteArray); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); + + // Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); + return; + } else { + startChallengeOwnershipTimer(); + } + } + } else { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << + "More info:" << networkReply->readAll(); + } + + networkReply->deleteLater(); + }); + } else { + qCWarning(context_overlay) << "Couldn't get Entity Server!"; + } + } else { + auto ledger = DependencyManager::get(); + _challengeOwnershipTimeoutTimer.stop(); + emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); + emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); + qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; + } + } +} + static const QString INSPECTION_CERTIFICATE_QML_PATH = "hifi/commerce/inspectionCertificate/InspectionCertificate.qml"; void ContextOverlayInterface::openInspectionCertificate() { // lets open the tablet to the inspection certificate QML @@ -275,87 +362,7 @@ void ContextOverlayInterface::openInspectionCertificate() { _hmdScriptingInterface->openTablet(); setLastInspectedEntity(_currentEntityWithContextOverlay); - - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); - - auto nodeList = DependencyManager::get(); - - if (entityProperties.getClientOnly()) { - if (entityProperties.verifyStaticCertificateProperties()) { - SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); - - if (entityServer) { - QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest networkRequest; - networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); - networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL(); - requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/transfer"); - QJsonObject request; - request["certificate_id"] = entityProperties.getCertificateID(); - networkRequest.setUrl(requestURL); - - QNetworkReply* networkReply = NULL; - networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - - connect(networkReply, &QNetworkReply::finished, [=]() { - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); - - if (networkReply->error() == QNetworkReply::NoError) { - if (!jsonObject["invalid_reason"].toString().isEmpty()) { - qCDebug(entities) << "invalid_reason not empty"; - } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { - qCDebug(entities) << "'transfer_status' is 'failed'"; - } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { - qCDebug(entities) << "'transfer_status' is 'pending'"; - } else { - QString ownerKey = jsonObject["transfer_recipient_key"].toString(); - - QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); - QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); - - int certIDByteArraySize = certID.length(); - int textByteArraySize = text.length(); - int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); - - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(textByteArraySize); - challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); - challengeOwnershipPacket->write(certID); - challengeOwnershipPacket->write(text); - challengeOwnershipPacket->write(nodeToChallengeByteArray); - nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); - - // Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); - return; - } else { - startChallengeOwnershipTimer(); - } - } - } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << - "More info:" << networkReply->readAll(); - } - - networkReply->deleteLater(); - }); - } else { - qCWarning(context_overlay) << "Couldn't get Entity Server!"; - } - } else { - auto ledger = DependencyManager::get(); - _challengeOwnershipTimeoutTimer.stop(); - emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); - qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; - } - } + requestOwnershipVerification(_lastInspectedEntity); } } @@ -397,6 +404,7 @@ void ContextOverlayInterface::startChallengeOwnershipTimer() { connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity; emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT)); + emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); }); _challengeOwnershipTimeoutTimer.start(5000); @@ -421,7 +429,9 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); + emit DependencyManager::get()->ownershipVerificationSuccess(_lastInspectedEntity); } else { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); + emit DependencyManager::get()->ownershipVerificationFailed(_lastInspectedEntity); } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index 990a7fe599..6aad2a773b 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -26,6 +26,7 @@ #include "ui/overlays/Overlays.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/SelectionScriptingInterface.h" +#include "scripting/WalletScriptingInterface.h" #include "EntityTree.h" #include "ContextOverlayLogging.h" @@ -33,12 +34,13 @@ /**jsdoc * @namespace ContextOverlay */ -class ContextOverlayInterface : public QObject, public Dependency { +class ContextOverlayInterface : public QObject, public Dependency { Q_OBJECT Q_PROPERTY(QUuid entityWithContextOverlay READ getCurrentEntityWithContextOverlay WRITE setCurrentEntityWithContextOverlay) Q_PROPERTY(bool enabled READ getEnabled WRITE setEnabled) Q_PROPERTY(bool isInMarketplaceInspectionMode READ getIsInMarketplaceInspectionMode WRITE setIsInMarketplaceInspectionMode) + QSharedPointer _entityScriptingInterface; EntityPropertyFlags _entityPropertyFlags; QSharedPointer _hmdScriptingInterface; @@ -47,9 +49,7 @@ class ContextOverlayInterface : public QObject, public Dependency { OverlayID _contextOverlayID { UNKNOWN_OVERLAY_ID }; std::shared_ptr _contextOverlay { nullptr }; public: - ContextOverlayInterface(); - Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; } @@ -57,6 +57,8 @@ public: bool getEnabled() { return _enabled; } bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } void setIsInMarketplaceInspectionMode(bool mode) { _isInMarketplaceInspectionMode = mode; } + void requestOwnershipVerification(const QUuid& entityID); + EntityPropertyFlags getEntityPropertyFlags() { return _entityPropertyFlags; } signals: void contextOverlayClicked(const QUuid& currentEntityWithContextOverlay); @@ -80,8 +82,7 @@ private: enum { MAX_SELECTION_COUNT = 16 }; - - bool _verboseLogging { true }; + bool _verboseLogging{ true }; bool _enabled { true }; EntityItemID _currentEntityWithContextOverlay{}; EntityItemID _lastInspectedEntity{}; diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index 8121b985cb..5fb4d58340 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -72,6 +72,15 @@ Web3DOverlay::Web3DOverlay() { connect(this, &Web3DOverlay::requestWebSurface, this, &Web3DOverlay::buildWebSurface); connect(this, &Web3DOverlay::releaseWebSurface, this, &Web3DOverlay::destroyWebSurface); connect(this, &Web3DOverlay::resizeWebSurface, this, &Web3DOverlay::onResizeWebSurface); + + //need to be intialized before Tablet 1st open + _webSurface = DependencyManager::get()->acquire(_url); + _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); + _webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + _webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); + } Web3DOverlay::Web3DOverlay(const Web3DOverlay* Web3DOverlay) : @@ -192,11 +201,6 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags); _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); - - _webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED - _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED - _webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); - // in Qt 5.10.0 there is already an "Audio" object in the QML context // though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface" _webSurface->getSurfaceContext()->setContextProperty("AudioScriptingInterface", DependencyManager::get().data()); diff --git a/libraries/entities/src/EntityScriptServerLogClient.cpp b/libraries/entities/src/EntityScriptServerLogClient.cpp index 4405af5b1b..5853c9585e 100644 --- a/libraries/entities/src/EntityScriptServerLogClient.cpp +++ b/libraries/entities/src/EntityScriptServerLogClient.cpp @@ -51,9 +51,9 @@ void EntityScriptServerLogClient::enableToEntityServerScriptLog(bool enable) { if (_subscribed != enable) { if (enable) { - emit receivedNewLogLines("====================== Subscribded to the Entity Script Server's log ======================"); + emit receivedNewLogLines("====================== Subscribed to the Entity Script Server's log ======================"); } else { - emit receivedNewLogLines("==================== Unsubscribded from the Entity Script Server's log ===================="); + emit receivedNewLogLines("==================== Unsubscribed from the Entity Script Server's log ===================="); } } _subscribed = enable; diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index a250f77b2e..252f6efa9e 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -10,7 +10,7 @@ getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, Messages, makeDispatcherModuleParameters, makeRunningValues, Settings, entityHasActions, Vec3, Overlays, flatten, Xform, getControllerWorldLocation, ensureDynamic, entityIsCloneable, - cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE + cloneEntity, DISPATCHER_PROPERTIES, TEAR_AWAY_DISTANCE, Uuid */ Script.include("/~/system/libraries/Xform.js"); @@ -269,6 +269,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.grabEntityProps = null; this.shouldSendStart = false; this.equipedWithSecondary = false; + this.handHasBeenRightsideUp = false; this.parameters = makeDispatcherModuleParameters( 300, @@ -486,15 +487,17 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa var grabbedProperties = Entities.getEntityProperties(this.targetEntityID); // if an object is "equipped" and has a predefined offset, use it. - var offsets = getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); - if (offsets) { - this.offsetPosition = offsets[0]; - this.offsetRotation = offsets[1]; - } else { - var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; - if (this.grabbedHotspot.joints[handJointName]) { - this.offsetPosition = this.grabbedHotspot.joints[handJointName][0]; - this.offsetRotation = this.grabbedHotspot.joints[handJointName][1]; + if (this.grabbedHotspot) { + var offsets = getAttachPointForHotspotFromSettings(this.grabbedHotspot, this.hand); + if (offsets) { + this.offsetPosition = offsets[0]; + this.offsetRotation = offsets[1]; + } else { + var handJointName = this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"; + if (this.grabbedHotspot.joints[handJointName]) { + this.offsetPosition = this.grabbedHotspot.joints[handJointName][0]; + this.offsetRotation = this.grabbedHotspot.joints[handJointName][1]; + } } } @@ -549,7 +552,6 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa // 100 ms seems to be sufficient time to force the check even occur after the object has been initialized. Script.setTimeout(grabEquipCheck, 100); } - }; this.endEquipEntity = function () { @@ -624,7 +626,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.grabbedHotspot = potentialEquipHotspot; this.targetEntityID = this.grabbedHotspot.entityID; this.startEquipEntity(controllerData); - this.messageGrabEnity = false; + this.messageGrabEntity = false; this.equipedWithSecondary = this.secondarySmoothedSqueezed(); return makeRunningValues(true, [potentialEquipHotspot.entityID], []); } else { @@ -640,6 +642,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.isReady = function (controllerData, deltaTime) { var timestamp = Date.now(); this.updateInputs(controllerData); + this.handHasBeenRightsideUp = false; return this.checkNearbyHotspots(controllerData, deltaTime, timestamp); }; @@ -671,7 +674,14 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa return makeRunningValues(false, [], []); } - var dropDetected = this.dropGestureProcess(deltaTime); + var handIsUpsideDown = this.dropGestureProcess(deltaTime); + var dropDetected = false; + if (this.handHasBeenRightsideUp) { + dropDetected = handIsUpsideDown; + } + if (!handIsUpsideDown) { + this.handHasBeenRightsideUp = true; + } if (this.triggerSmoothedReleased() || this.secondaryReleased()) { if (this.shouldSendStart) { @@ -692,7 +702,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa } // highlight the grabbed hotspot when the dropGesture is detected. - if (dropDetected) { + if (dropDetected && this.grabbedHotspot) { equipHotspotBuddy.updateHotspot(this.grabbedHotspot, timestamp); equipHotspotBuddy.highlightHotspot(this.grabbedHotspot); } diff --git a/scripts/system/run.js b/scripts/system/run.js index c34271b18d..8d1fb62619 100644 --- a/scripts/system/run.js +++ b/scripts/system/run.js @@ -35,4 +35,5 @@ button.editProperties({isActive: true}); } + Script.scriptEnding.connect(cleanup); }()); diff --git a/scripts/tutorials/createTetherballStick.js b/scripts/tutorials/createTetherballStick.js index 35f5fb0344..1b5bc36932 100644 --- a/scripts/tutorials/createTetherballStick.js +++ b/scripts/tutorials/createTetherballStick.js @@ -23,7 +23,7 @@ var BALL_DENSITY = 1000; var ACTION_DISTANCE = 0.35; var ACTION_TIMESCALE = 0.035; var MAX_DISTANCE_MULTIPLIER = 4; -var STICK_SCRIPT_URL = Script.resolvePath("./entity_scripts/tetherballStick.js?v=" + Date.now()); +var STICK_SCRIPT_URL = Script.resolvePath("./entity_scripts/tetherballStick.js"); var STICK_MODEL_URL = "http://hifi-content.s3.amazonaws.com/caitlyn/production/raveStick/newRaveStick2.fbx"; var COLLISION_SOUND_URL = "http://public.highfidelity.io/sounds/Footsteps/FootstepW3Left-12db.wav";