mirror of
https://github.com/overte-org/overte.git
synced 2025-04-18 00:26:33 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into voxel-paint-opti
This commit is contained in:
commit
3b669089e4
59 changed files with 4227 additions and 191 deletions
|
@ -548,6 +548,7 @@ const float DEFAULT_HMD_TABLET_SCALE_PERCENT = 100.0f;
|
|||
const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;
|
||||
const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
|
||||
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
|
||||
const bool DEFAULT_TABLET_VISIBLE_TO_OTHERS = false;
|
||||
|
||||
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runServer, QString runServerPathOption) :
|
||||
QApplication(argc, argv),
|
||||
|
@ -570,6 +571,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
_desktopTabletScale("desktopTabletScale", DEFAULT_DESKTOP_TABLET_SCALE_PERCENT),
|
||||
_desktopTabletBecomesToolbarSetting("desktopTabletBecomesToolbar", DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR),
|
||||
_hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR),
|
||||
_tabletVisibleToOthersSetting("tabletVisibleToOthers", DEFAULT_TABLET_VISIBLE_TO_OTHERS),
|
||||
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
|
||||
_scaleMirror(1.0f),
|
||||
_rotateMirror(0.0f),
|
||||
|
@ -781,6 +783,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
connect(&domainHandler, SIGNAL(connectedToDomain(const QString&)), SLOT(updateWindowTitle()));
|
||||
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(updateWindowTitle()));
|
||||
connect(&domainHandler, SIGNAL(disconnectedFromDomain()), SLOT(clearDomainOctreeDetails()));
|
||||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, [this]() {
|
||||
getOverlays().deleteOverlay(getTabletScreenID());
|
||||
getOverlays().deleteOverlay(getTabletHomeButtonID());
|
||||
getOverlays().deleteOverlay(getTabletFrameID());
|
||||
});
|
||||
connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &Application::domainConnectionRefused);
|
||||
|
||||
// We could clear ATP assets only when changing domains, but it's possible that the domain you are connected
|
||||
|
@ -2350,6 +2357,11 @@ void Application::setHmdTabletBecomesToolbarSetting(bool value) {
|
|||
updateSystemTabletMode();
|
||||
}
|
||||
|
||||
void Application::setTabletVisibleToOthersSetting(bool value) {
|
||||
_tabletVisibleToOthersSetting.set(value);
|
||||
updateSystemTabletMode();
|
||||
}
|
||||
|
||||
void Application::setSettingConstrainToolbarPosition(bool setting) {
|
||||
_constrainToolbarPosition.set(setting);
|
||||
DependencyManager::get<OffscreenUi>()->setConstrainToolbarToCenterX(setting);
|
||||
|
@ -3095,6 +3107,7 @@ void Application::mouseMoveEvent(QMouseEvent* event) {
|
|||
getOverlays().mouseMoveEvent(&mappedEvent);
|
||||
getEntities()->mouseMoveEvent(&mappedEvent);
|
||||
}
|
||||
|
||||
_controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
|
@ -3127,7 +3140,6 @@ void Application::mousePressEvent(QMouseEvent* event) {
|
|||
|
||||
if (!_aboutToQuit) {
|
||||
getOverlays().mousePressEvent(&mappedEvent);
|
||||
|
||||
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
|
||||
getEntities()->mousePressEvent(&mappedEvent);
|
||||
}
|
||||
|
@ -3431,7 +3443,7 @@ void Application::idle(float nsecsElapsed) {
|
|||
#ifdef Q_OS_WIN
|
||||
static std::once_flag once;
|
||||
std::call_once(once, [] {
|
||||
initCpuUsage();
|
||||
initCpuUsage();
|
||||
});
|
||||
|
||||
vec3 kernelUserAndSystem;
|
||||
|
@ -6904,5 +6916,10 @@ OverlayID Application::getTabletScreenID() const {
|
|||
|
||||
OverlayID Application::getTabletHomeButtonID() const {
|
||||
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
||||
return HMD->getCurrentHomeButtonUUID();
|
||||
return HMD->getCurrentHomeButtonID();
|
||||
}
|
||||
|
||||
QUuid Application::getTabletFrameID() const {
|
||||
auto HMD = DependencyManager::get<HMDScriptingInterface>();
|
||||
return HMD->getCurrentTabletFrameID();
|
||||
}
|
||||
|
|
|
@ -218,6 +218,8 @@ public:
|
|||
void setDesktopTabletBecomesToolbarSetting(bool value);
|
||||
bool getHmdTabletBecomesToolbarSetting() { return _hmdTabletBecomesToolbarSetting.get(); }
|
||||
void setHmdTabletBecomesToolbarSetting(bool value);
|
||||
bool getTabletVisibleToOthersSetting() { return _tabletVisibleToOthersSetting.get(); }
|
||||
void setTabletVisibleToOthersSetting(bool value);
|
||||
|
||||
float getSettingConstrainToolbarPosition() { return _constrainToolbarPosition.get(); }
|
||||
void setSettingConstrainToolbarPosition(bool setting);
|
||||
|
@ -300,6 +302,7 @@ public:
|
|||
|
||||
OverlayID getTabletScreenID() const;
|
||||
OverlayID getTabletHomeButtonID() const;
|
||||
QUuid getTabletFrameID() const; // may be an entity or an overlay
|
||||
|
||||
signals:
|
||||
void svoImportRequested(const QString& url);
|
||||
|
@ -561,6 +564,7 @@ private:
|
|||
Setting::Handle<float> _desktopTabletScale;
|
||||
Setting::Handle<bool> _desktopTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _hmdTabletBecomesToolbarSetting;
|
||||
Setting::Handle<bool> _tabletVisibleToOthersSetting;
|
||||
Setting::Handle<bool> _constrainToolbarPosition;
|
||||
|
||||
float _scaleMirror;
|
||||
|
|
|
@ -933,6 +933,10 @@ glm::vec3 Avatar::getDefaultJointTranslation(int index) const {
|
|||
}
|
||||
|
||||
glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
||||
if (index < 0) {
|
||||
index += numeric_limits<unsigned short>::max() + 1; // 65536
|
||||
}
|
||||
|
||||
switch(index) {
|
||||
case SENSOR_TO_WORLD_MATRIX_INDEX: {
|
||||
glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
|
||||
|
@ -969,6 +973,10 @@ glm::quat Avatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
|||
}
|
||||
|
||||
glm::vec3 Avatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
|
||||
if (index < 0) {
|
||||
index += numeric_limits<unsigned short>::max() + 1; // 65536
|
||||
}
|
||||
|
||||
switch(index) {
|
||||
case SENSOR_TO_WORLD_MATRIX_INDEX: {
|
||||
glm::mat4 sensorToWorldMatrix = getSensorToWorldMatrix();
|
||||
|
|
|
@ -823,7 +823,7 @@ void MyAvatar::saveData() {
|
|||
auto hmdInterface = DependencyManager::get<HMDScriptingInterface>();
|
||||
_avatarEntitiesLock.withReadLock([&] {
|
||||
for (auto entityID : _avatarEntityData.keys()) {
|
||||
if (hmdInterface->getCurrentTabletUIID() == entityID) {
|
||||
if (hmdInterface->getCurrentTabletFrameID() == entityID) {
|
||||
// don't persist the tablet between domains / sessions
|
||||
continue;
|
||||
}
|
||||
|
@ -2410,6 +2410,10 @@ glm::mat4 MyAvatar::computeCameraRelativeHandControllerMatrix(const glm::mat4& c
|
|||
}
|
||||
|
||||
glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
||||
if (index < 0) {
|
||||
index += numeric_limits<unsigned short>::max() + 1; // 65536
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case CONTROLLER_LEFTHAND_INDEX: {
|
||||
return getLeftHandControllerPoseInAvatarFrame().getRotation();
|
||||
|
@ -2443,6 +2447,10 @@ glm::quat MyAvatar::getAbsoluteJointRotationInObjectFrame(int index) const {
|
|||
}
|
||||
|
||||
glm::vec3 MyAvatar::getAbsoluteJointTranslationInObjectFrame(int index) const {
|
||||
if (index < 0) {
|
||||
index += numeric_limits<unsigned short>::max() + 1; // 65536
|
||||
}
|
||||
|
||||
switch (index) {
|
||||
case CONTROLLER_LEFTHAND_INDEX: {
|
||||
return getLeftHandControllerPoseInAvatarFrame().getTranslation();
|
||||
|
|
|
@ -29,8 +29,8 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen
|
|||
Q_PROPERTY(glm::quat orientation READ getOrientation)
|
||||
Q_PROPERTY(bool mounted READ isMounted)
|
||||
Q_PROPERTY(bool showTablet READ getShouldShowTablet)
|
||||
Q_PROPERTY(QUuid tabletID READ getCurrentTabletUIID WRITE setCurrentTabletUIID)
|
||||
Q_PROPERTY(QUuid homeButtonID READ getCurrentHomeButtonUUID WRITE setCurrentHomeButtonUUID)
|
||||
Q_PROPERTY(QUuid tabletID READ getCurrentTabletFrameID WRITE setCurrentTabletFrameID)
|
||||
Q_PROPERTY(QUuid homeButtonID READ getCurrentHomeButtonID WRITE setCurrentHomeButtonID)
|
||||
Q_PROPERTY(QUuid tabletScreenID READ getCurrentTabletScreenID WRITE setCurrentTabletScreenID)
|
||||
|
||||
public:
|
||||
|
@ -90,11 +90,11 @@ public:
|
|||
void setShouldShowTablet(bool value) { _showTablet = value; }
|
||||
bool getShouldShowTablet() const { return _showTablet; }
|
||||
|
||||
void setCurrentTabletUIID(QUuid tabletID) { _tabletUIID = tabletID; }
|
||||
QUuid getCurrentTabletUIID() const { return _tabletUIID; }
|
||||
void setCurrentTabletFrameID(QUuid tabletID) { _tabletUIID = tabletID; }
|
||||
QUuid getCurrentTabletFrameID() const { return _tabletUIID; }
|
||||
|
||||
void setCurrentHomeButtonUUID(QUuid homeButtonID) { _homeButtonID = homeButtonID; }
|
||||
QUuid getCurrentHomeButtonUUID() const { return _homeButtonID; }
|
||||
void setCurrentHomeButtonID(QUuid homeButtonID) { _homeButtonID = homeButtonID; }
|
||||
QUuid getCurrentHomeButtonID() const { return _homeButtonID; }
|
||||
|
||||
void setCurrentTabletScreenID(QUuid tabletID) { _tabletScreenID = tabletID; }
|
||||
QUuid getCurrentTabletScreenID() const { return _tabletScreenID; }
|
||||
|
|
|
@ -102,7 +102,11 @@ void setupPreferences() {
|
|||
auto setter = [](bool value) { qApp->setHmdTabletBecomesToolbarSetting(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "HMD Tablet Becomes Toolbar", getter, setter));
|
||||
}
|
||||
|
||||
{
|
||||
auto getter = []()->bool { return qApp->getTabletVisibleToOthersSetting(); };
|
||||
auto setter = [](bool value) { qApp->setTabletVisibleToOthersSetting(value); };
|
||||
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Tablet Is Visible To Others", getter, setter));
|
||||
}
|
||||
// Snapshots
|
||||
static const QString SNAPSHOTS { "Snapshots" };
|
||||
{
|
||||
|
|
|
@ -39,7 +39,8 @@ Base3DOverlay::Base3DOverlay(const Base3DOverlay* base3DOverlay) :
|
|||
_isDashedLine(base3DOverlay->_isDashedLine),
|
||||
_ignoreRayIntersection(base3DOverlay->_ignoreRayIntersection),
|
||||
_drawInFront(base3DOverlay->_drawInFront),
|
||||
_isAA(base3DOverlay->_isAA)
|
||||
_isAA(base3DOverlay->_isAA),
|
||||
_isGrabbable(base3DOverlay->_isGrabbable)
|
||||
{
|
||||
setTransform(base3DOverlay->getTransform());
|
||||
}
|
||||
|
@ -59,15 +60,19 @@ QVariantMap convertOverlayLocationFromScriptSemantics(const QVariantMap& propert
|
|||
} else if (result["position"].isValid()) {
|
||||
glm::vec3 localPosition = SpatiallyNestable::worldToLocal(vec3FromVariant(result["position"]),
|
||||
parentID, parentJointIndex, success);
|
||||
result["position"] = vec3toVariant(localPosition);
|
||||
if (success) {
|
||||
result["position"] = vec3toVariant(localPosition);
|
||||
}
|
||||
}
|
||||
|
||||
if (result["localOrientation"].isValid()) {
|
||||
result["orientation"] = result["localOrientation"];
|
||||
} else if (result["orientation"].isValid()) {
|
||||
glm::quat localOrientation = SpatiallyNestable::worldToLocal(quatFromVariant(result["orientation"]),
|
||||
parentID, parentJointIndex, success);
|
||||
result["orientation"] = quatToVariant(localOrientation);
|
||||
parentID, parentJointIndex, success);
|
||||
if (success) {
|
||||
result["orientation"] = quatToVariant(localOrientation);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -125,6 +130,11 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) {
|
|||
needRenderItemUpdate = true;
|
||||
}
|
||||
|
||||
auto isGrabbable = properties["grabbable"];
|
||||
if (isGrabbable.isValid()) {
|
||||
setIsGrabbable(isGrabbable.toBool());
|
||||
}
|
||||
|
||||
if (properties["position"].isValid()) {
|
||||
setLocalPosition(vec3FromVariant(properties["position"]));
|
||||
needRenderItemUpdate = true;
|
||||
|
@ -227,6 +237,9 @@ QVariant Base3DOverlay::getProperty(const QString& property) {
|
|||
if (property == "drawInFront") {
|
||||
return _drawInFront;
|
||||
}
|
||||
if (property == "grabbable") {
|
||||
return _isGrabbable;
|
||||
}
|
||||
if (property == "parentID") {
|
||||
return getParentID();
|
||||
}
|
||||
|
@ -246,6 +259,8 @@ bool Base3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::vec3
|
|||
}
|
||||
|
||||
void Base3DOverlay::locationChanged(bool tellPhysics) {
|
||||
SpatiallyNestable::locationChanged(tellPhysics);
|
||||
|
||||
auto itemID = getRenderItemID();
|
||||
if (render::Item::isValidID(itemID)) {
|
||||
render::ScenePointer scene = qApp->getMain3DScene();
|
||||
|
@ -253,8 +268,6 @@ void Base3DOverlay::locationChanged(bool tellPhysics) {
|
|||
pendingChanges.updateItem(itemID);
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
// Overlays can't currently have children.
|
||||
// SpatiallyNestable::locationChanged(tellPhysics); // tell all the children, also
|
||||
}
|
||||
|
||||
void Base3DOverlay::parentDeleted() {
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
bool getIsSolidLine() const { return !_isDashedLine; }
|
||||
bool getIgnoreRayIntersection() const { return _ignoreRayIntersection; }
|
||||
bool getDrawInFront() const { return _drawInFront; }
|
||||
bool getIsGrabbable() const { return _isGrabbable; }
|
||||
|
||||
virtual bool isAA() const { return _isAA; }
|
||||
|
||||
|
@ -47,6 +48,7 @@ public:
|
|||
void setIgnoreRayIntersection(bool value) { _ignoreRayIntersection = value; }
|
||||
void setDrawInFront(bool value) { _drawInFront = value; }
|
||||
void setIsAA(bool value) { _isAA = value; }
|
||||
void setIsGrabbable(bool value) { _isGrabbable = value; }
|
||||
|
||||
virtual AABox getBounds() const override = 0;
|
||||
|
||||
|
@ -71,6 +73,7 @@ protected:
|
|||
bool _ignoreRayIntersection;
|
||||
bool _drawInFront;
|
||||
bool _isAA;
|
||||
bool _isGrabbable { false };
|
||||
};
|
||||
|
||||
#endif // hifi_Base3DOverlay_h
|
||||
|
|
|
@ -341,28 +341,18 @@ OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) {
|
|||
return UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysHUD);
|
||||
i.toBack();
|
||||
|
||||
const float LARGE_NEGATIVE_FLOAT = -9999999;
|
||||
glm::vec3 origin(pointCopy.x, pointCopy.y, LARGE_NEGATIVE_FLOAT);
|
||||
glm::vec3 direction(0, 0, 1);
|
||||
BoxFace thisFace;
|
||||
glm::vec3 thisSurfaceNormal;
|
||||
float distance;
|
||||
unsigned int bestStackOrder = 0;
|
||||
OverlayID bestOverlayID = UNKNOWN_OVERLAY_ID;
|
||||
|
||||
while (i.hasPrevious()) {
|
||||
i.previous();
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
OverlayID thisID = i.key();
|
||||
if (i.value()->is3D()) {
|
||||
auto thisOverlay = std::dynamic_pointer_cast<Base3DOverlay>(i.value());
|
||||
if (thisOverlay && !thisOverlay->getIgnoreRayIntersection()) {
|
||||
if (thisOverlay->findRayIntersection(origin, direction, distance, thisFace, thisSurfaceNormal)) {
|
||||
return thisID;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!i.value()->is3D()) {
|
||||
auto thisOverlay = std::dynamic_pointer_cast<Overlay2D>(i.value());
|
||||
if (thisOverlay && thisOverlay->getVisible() && thisOverlay->isLoaded() &&
|
||||
thisOverlay->getBoundingRect().contains(pointCopy.x, pointCopy.y, false)) {
|
||||
|
@ -406,16 +396,25 @@ RayToOverlayIntersectionResult Overlays::findRayIntersection(const PickRay& ray,
|
|||
const QScriptValue& overlayIDsToInclude,
|
||||
const QScriptValue& overlayIDsToDiscard,
|
||||
bool visibleOnly, bool collidableOnly) {
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
bool bestIsFront = false;
|
||||
const QVector<OverlayID> overlaysToInclude = qVectorOverlayIDFromScriptValue(overlayIDsToInclude);
|
||||
const QVector<OverlayID> overlaysToDiscard = qVectorOverlayIDFromScriptValue(overlayIDsToDiscard);
|
||||
|
||||
return findRayIntersectionInternal(ray, precisionPicking,
|
||||
overlaysToInclude, overlaysToDiscard, visibleOnly, collidableOnly);
|
||||
}
|
||||
|
||||
|
||||
RayToOverlayIntersectionResult Overlays::findRayIntersectionInternal(const PickRay& ray, bool precisionPicking,
|
||||
const QVector<OverlayID>& overlaysToInclude,
|
||||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
bool visibleOnly, bool collidableOnly) {
|
||||
float bestDistance = std::numeric_limits<float>::max();
|
||||
bool bestIsFront = false;
|
||||
|
||||
RayToOverlayIntersectionResult result;
|
||||
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
|
||||
i.toBack();
|
||||
while (i.hasPrevious()) {
|
||||
i.previous();
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
OverlayID thisID = i.key();
|
||||
auto thisOverlay = std::dynamic_pointer_cast<Base3DOverlay>(i.value());
|
||||
|
||||
|
@ -700,8 +699,9 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) {
|
|||
}
|
||||
}
|
||||
|
||||
PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay ray,
|
||||
RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType) {
|
||||
PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay ray,
|
||||
RayToOverlayIntersectionResult rayPickResult, QMouseEvent* event,
|
||||
PointerEvent::EventType eventType) {
|
||||
|
||||
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(overlay);
|
||||
|
||||
|
@ -719,11 +719,41 @@ PointerEvent Overlays::calculatePointerEvent(Overlay::Pointer overlay, PickRay r
|
|||
return pointerEvent;
|
||||
}
|
||||
|
||||
void Overlays::mousePressEvent(QMouseEvent* event) {
|
||||
|
||||
RayToOverlayIntersectionResult Overlays::findRayIntersectionForMouseEvent(PickRay ray) {
|
||||
QVector<OverlayID> overlaysToInclude;
|
||||
QVector<OverlayID> overlaysToDiscard;
|
||||
RayToOverlayIntersectionResult rayPickResult;
|
||||
|
||||
// first priority is tablet screen
|
||||
overlaysToInclude << qApp->getTabletScreenID();
|
||||
rayPickResult = findRayIntersectionInternal(ray, true, overlaysToInclude, overlaysToDiscard);
|
||||
if (rayPickResult.intersects) {
|
||||
return rayPickResult;
|
||||
}
|
||||
// then tablet home button
|
||||
overlaysToInclude.clear();
|
||||
overlaysToInclude << qApp->getTabletHomeButtonID();
|
||||
rayPickResult = findRayIntersectionInternal(ray, true, overlaysToInclude, overlaysToDiscard);
|
||||
if (rayPickResult.intersects) {
|
||||
return rayPickResult;
|
||||
}
|
||||
// then tablet frame
|
||||
overlaysToInclude.clear();
|
||||
overlaysToInclude << OverlayID(qApp->getTabletFrameID());
|
||||
rayPickResult = findRayIntersectionInternal(ray, true, overlaysToInclude, overlaysToDiscard);
|
||||
if (rayPickResult.intersects) {
|
||||
return rayPickResult;
|
||||
}
|
||||
// then whatever
|
||||
return findRayIntersection(ray);
|
||||
}
|
||||
|
||||
bool Overlays::mousePressEvent(QMouseEvent* event) {
|
||||
PerformanceTimer perfTimer("Overlays::mousePressEvent");
|
||||
|
||||
PickRay ray = qApp->computePickRay(event->x(), event->y());
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersection(ray);
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
if (rayPickResult.intersects) {
|
||||
_currentClickingOnOverlayID = rayPickResult.overlayID;
|
||||
|
||||
|
@ -732,19 +762,18 @@ void Overlays::mousePressEvent(QMouseEvent* event) {
|
|||
if (thisOverlay) {
|
||||
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press);
|
||||
emit mousePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
|
||||
} else {
|
||||
emit mousePressOffOverlay();
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
emit mousePressOffOverlay();
|
||||
}
|
||||
emit mousePressOffOverlay();
|
||||
return false;
|
||||
}
|
||||
|
||||
void Overlays::mouseReleaseEvent(QMouseEvent* event) {
|
||||
bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
|
||||
PerformanceTimer perfTimer("Overlays::mouseReleaseEvent");
|
||||
|
||||
PickRay ray = qApp->computePickRay(event->x(), event->y());
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersection(ray);
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
if (rayPickResult.intersects) {
|
||||
|
||||
// Only Web overlays can have focus.
|
||||
|
@ -756,13 +785,14 @@ void Overlays::mouseReleaseEvent(QMouseEvent* event) {
|
|||
}
|
||||
|
||||
_currentClickingOnOverlayID = UNKNOWN_OVERLAY_ID;
|
||||
return false;
|
||||
}
|
||||
|
||||
void Overlays::mouseMoveEvent(QMouseEvent* event) {
|
||||
bool Overlays::mouseMoveEvent(QMouseEvent* event) {
|
||||
PerformanceTimer perfTimer("Overlays::mouseMoveEvent");
|
||||
|
||||
PickRay ray = qApp->computePickRay(event->x(), event->y());
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersection(ray);
|
||||
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
|
||||
if (rayPickResult.intersects) {
|
||||
|
||||
// Only Web overlays can have focus.
|
||||
|
@ -802,4 +832,34 @@ void Overlays::mouseMoveEvent(QMouseEvent* event) {
|
|||
_currentHoverOverOverlayID = UNKNOWN_OVERLAY_ID;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<QUuid> Overlays::findOverlays(const glm::vec3& center, float radius) const {
|
||||
QVector<QUuid> result;
|
||||
|
||||
QMapIterator<OverlayID, Overlay::Pointer> i(_overlaysWorld);
|
||||
int checked = 0;
|
||||
while (i.hasNext()) {
|
||||
checked++;
|
||||
i.next();
|
||||
OverlayID thisID = i.key();
|
||||
auto overlay = std::dynamic_pointer_cast<Volume3DOverlay>(i.value());
|
||||
if (overlay && overlay->getVisible() && !overlay->getIgnoreRayIntersection() && overlay->isLoaded()) {
|
||||
// get AABox in frame of overlay
|
||||
glm::vec3 dimensions = overlay->getDimensions();
|
||||
glm::vec3 low = dimensions * -0.5f;
|
||||
AABox overlayFrameBox(low, dimensions);
|
||||
|
||||
Transform overlayToWorldMatrix = overlay->getTransform();
|
||||
glm::mat4 worldToOverlayMatrix = glm::inverse(overlayToWorldMatrix.getMatrix());
|
||||
glm::vec3 overlayFrameSearchPosition = glm::vec3(worldToOverlayMatrix * glm::vec4(center, 1.0f));
|
||||
glm::vec3 penetration;
|
||||
if (overlayFrameBox.findSpherePenetration(overlayFrameSearchPosition, radius, penetration)) {
|
||||
result.append(thisID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -100,9 +100,9 @@ public:
|
|||
OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); }
|
||||
OverlayID addOverlay(Overlay::Pointer overlay);
|
||||
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
bool mousePressEvent(QMouseEvent* event);
|
||||
bool mouseReleaseEvent(QMouseEvent* event);
|
||||
bool mouseMoveEvent(QMouseEvent* event);
|
||||
|
||||
void cleanupAllOverlays();
|
||||
|
||||
|
@ -206,6 +206,16 @@ public slots:
|
|||
bool visibleOnly = false,
|
||||
bool collidableOnly = false);
|
||||
|
||||
/**jsdoc
|
||||
* Return a list of 3d overlays with bounding boxes that touch the given sphere
|
||||
*
|
||||
* @function Overlays.findOverlays
|
||||
* @param {Vec3} center the point to search from.
|
||||
* @param {float} radius search radius
|
||||
* @return {List of Overlays.OverlayID} list of overlays withing the radius
|
||||
*/
|
||||
QVector<QUuid> findOverlays(const glm::vec3& center, float radius) const;
|
||||
|
||||
/**jsdoc
|
||||
* Check whether an overlay's assets have been loaded. For example, if the
|
||||
* overlay is an "image" overlay, this will indicate whether the its image
|
||||
|
@ -317,6 +327,12 @@ private:
|
|||
|
||||
OverlayID _currentClickingOnOverlayID { UNKNOWN_OVERLAY_ID };
|
||||
OverlayID _currentHoverOverOverlayID { UNKNOWN_OVERLAY_ID };
|
||||
|
||||
RayToOverlayIntersectionResult findRayIntersectionInternal(const PickRay& ray, bool precisionPicking,
|
||||
const QVector<OverlayID>& overlaysToInclude,
|
||||
const QVector<OverlayID>& overlaysToDiscard,
|
||||
bool visibleOnly = false, bool collidableOnly = false);
|
||||
RayToOverlayIntersectionResult findRayIntersectionForMouseEvent(PickRay ray);
|
||||
};
|
||||
|
||||
#endif // hifi_Overlays_h
|
||||
|
|
|
@ -53,7 +53,7 @@ namespace render {
|
|||
return overlay->getBounds();
|
||||
}
|
||||
template <> int payloadGetLayer(const Overlay::Pointer& overlay) {
|
||||
// MAgic number while we are defining the layering mechanism:
|
||||
// Magic number while we are defining the layering mechanism:
|
||||
const int LAYER_NO_AA = 3;
|
||||
const int LAYER_2D = 2;
|
||||
const int LAYER_3D_FRONT = 1;
|
||||
|
|
|
@ -1429,8 +1429,7 @@ QVector<QUuid> EntityScriptingInterface::getChildrenIDsOfJoint(const QUuid& pare
|
|||
return;
|
||||
}
|
||||
parent->forEachChild([&](SpatiallyNestablePointer child) {
|
||||
if (child->getParentJointIndex() == jointIndex &&
|
||||
child->getNestableType() != NestableType::Overlay) {
|
||||
if (child->getParentJointIndex() == jointIndex) {
|
||||
result.push_back(child->getID());
|
||||
}
|
||||
});
|
||||
|
|
|
@ -44,6 +44,8 @@ public:
|
|||
// Mutable, but must retain structure of vector
|
||||
using NetworkMaterials = std::vector<std::shared_ptr<NetworkMaterial>>;
|
||||
|
||||
bool isGeometryLoaded() const { return (bool)_fbxGeometry; }
|
||||
|
||||
const FBXGeometry& getFBXGeometry() const { return *_fbxGeometry; }
|
||||
const GeometryMeshes& getMeshes() const { return *_meshes; }
|
||||
const std::shared_ptr<const NetworkMaterial> getShapeMaterial(int shapeID) const;
|
||||
|
|
|
@ -336,8 +336,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
|
|||
const double pStrength = 2.0;
|
||||
int width = image.width();
|
||||
int height = image.height();
|
||||
// THe end result image for normal map is RGBA32 even though the A is not used
|
||||
QImage result(width, height, QImage::Format_RGBA8888);
|
||||
QImage result(width, height, QImage::Format_RGB888);
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
const int iNextClamped = clampPixelCoordinate(i + 1, width - 1);
|
||||
|
@ -377,21 +376,19 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm
|
|||
glm::normalize(v);
|
||||
|
||||
// convert to rgb from the value obtained computing the filter
|
||||
QRgb qRgbValue = qRgb(mapComponent(v.x), mapComponent(v.y), mapComponent(v.z));
|
||||
QRgb qRgbValue = qRgba(mapComponent(v.x), mapComponent(v.y), mapComponent(v.z), 1.0);
|
||||
result.setPixel(i, j, qRgbValue);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::Texture* theTexture = nullptr;
|
||||
if ((image.width() > 0) && (image.height() > 0)) {
|
||||
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
|
||||
gpu::Element formatGPU = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
gpu::Element formatMip = gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::RGB);
|
||||
|
||||
theTexture = (gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)));
|
||||
theTexture->setSource(srcImageName);
|
||||
theTexture->assignStoredMip(0, formatMip, image.byteCount(), image.constBits());
|
||||
generateMips(theTexture, image, formatMip, true);
|
||||
}
|
||||
|
||||
return theTexture;
|
||||
|
|
|
@ -16,8 +16,8 @@ void UserActivityLoggerScriptingInterface::enabledEdit() {
|
|||
logAction("enabled_edit");
|
||||
}
|
||||
|
||||
void UserActivityLoggerScriptingInterface::openedTablet() {
|
||||
logAction("opened_tablet");
|
||||
void UserActivityLoggerScriptingInterface::openedTablet(bool visibleToOthers) {
|
||||
logAction("opened_tablet", { { "visible_to_others", visibleToOthers } });
|
||||
}
|
||||
|
||||
void UserActivityLoggerScriptingInterface::closedTablet() {
|
||||
|
|
|
@ -21,7 +21,7 @@ class UserActivityLoggerScriptingInterface : public QObject, public Dependency {
|
|||
Q_OBJECT
|
||||
public:
|
||||
Q_INVOKABLE void enabledEdit();
|
||||
Q_INVOKABLE void openedTablet();
|
||||
Q_INVOKABLE void openedTablet(bool visibleToOthers);
|
||||
Q_INVOKABLE void closedTablet();
|
||||
Q_INVOKABLE void openedMarketplace();
|
||||
Q_INVOKABLE void toggledAway(bool isAway);
|
||||
|
|
|
@ -114,7 +114,7 @@ public:
|
|||
void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry,
|
||||
const QVector<glm::vec3>& vertices, const QVector<glm::vec3>& normals);
|
||||
|
||||
bool isLoaded() const { return (bool)_renderGeometry; }
|
||||
bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); }
|
||||
|
||||
void setIsWireframe(bool isWireframe) { _isWireframe = isWireframe; }
|
||||
bool isWireframe() const { return _isWireframe; }
|
||||
|
|
|
@ -331,6 +331,12 @@ Grabber.prototype.pressEvent = function(event) {
|
|||
}
|
||||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
var overlayResult = Overlays.findRayIntersection(pickRay, true, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]);
|
||||
if (overlayResult.intersects) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pickResults = Entities.findRayIntersection(pickRay, true); // accurate picking
|
||||
if (!pickResults.intersects) {
|
||||
// didn't click on anything
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
/* global getEntityCustomData, flatten, Xform, Script, Quat, Vec3, MyAvatar, Entities, Overlays, Settings,
|
||||
Reticle, Controller, Camera, Messages, Mat4, getControllerWorldLocation, getGrabPointSphereOffset, setGrabCommunications,
|
||||
Menu, HMD */
|
||||
Menu, HMD, isInEditMode */
|
||||
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
|
||||
|
||||
(function() { // BEGIN LOCAL_SCOPE
|
||||
|
@ -399,7 +399,7 @@ function entityHasActions(entityID) {
|
|||
|
||||
function findRayIntersection(pickRay, precise, include, exclude) {
|
||||
var entities = Entities.findRayIntersection(pickRay, precise, include, exclude, true);
|
||||
var overlays = Overlays.findRayIntersection(pickRay);
|
||||
var overlays = Overlays.findRayIntersection(pickRay, precise, [], [HMD.tabletID]);
|
||||
if (!overlays.intersects || (entities.intersects && (entities.distance <= overlays.distance))) {
|
||||
return entities;
|
||||
}
|
||||
|
@ -644,6 +644,7 @@ EquipHotspotBuddy.prototype.updateHotspot = function(hotspot, timestamp) {
|
|||
|
||||
// override default sphere with a user specified model, if it exists.
|
||||
overlayInfoSet.overlays.push(Overlays.addOverlay("model", {
|
||||
name: "hotspot overlay",
|
||||
url: hotspot.modelURL ? hotspot.modelURL : DEFAULT_SPHERE_MODEL_URL,
|
||||
position: hotspot.worldPosition,
|
||||
rotation: {
|
||||
|
@ -776,7 +777,7 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.actionID = null; // action this script created...
|
||||
this.grabbedEntity = null; // on this entity.
|
||||
this.grabbedThingID = null; // on this entity.
|
||||
this.grabbedOverlay = null;
|
||||
this.state = STATE_OFF;
|
||||
this.pointer = null; // entity-id of line object
|
||||
|
@ -853,14 +854,19 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.callEntityMethodOnGrabbed = function(entityMethodName) {
|
||||
if (this.grabbedIsOverlay) {
|
||||
return;
|
||||
}
|
||||
var args = [this.hand === RIGHT_HAND ? "right" : "left", MyAvatar.sessionUUID];
|
||||
Entities.callEntityMethod(this.grabbedEntity, entityMethodName, args);
|
||||
Entities.callEntityMethod(this.grabbedThingID, entityMethodName, args);
|
||||
};
|
||||
|
||||
this.setState = function(newState, reason) {
|
||||
if ((isInEditMode() && this.grabbedEntity !== HMD.tabletID )&& (newState !== STATE_OFF &&
|
||||
newState !== STATE_SEARCHING &&
|
||||
newState !== STATE_OVERLAY_STYLUS_TOUCHING)) {
|
||||
if ((isInEditMode() && this.grabbedThingID !== HMD.tabletID) &&
|
||||
(newState !== STATE_OFF &&
|
||||
newState !== STATE_SEARCHING &&
|
||||
newState !== STATE_OVERLAY_STYLUS_TOUCHING &&
|
||||
newState !== STATE_OVERLAY_LASER_TOUCHING)) {
|
||||
return;
|
||||
}
|
||||
setGrabCommunications((newState === STATE_DISTANCE_HOLDING) || (newState === STATE_NEAR_GRABBING));
|
||||
|
@ -903,6 +909,7 @@ function MyController(hand) {
|
|||
|
||||
if (!this.grabPointSphere) {
|
||||
this.grabPointSphere = Overlays.addOverlay("sphere", {
|
||||
name: "grabPointSphere",
|
||||
localPosition: getGrabPointSphereOffset(this.handToController()),
|
||||
localRotation: { x: 0, y: 0, z: 0, w: 1 },
|
||||
dimensions: GRAB_POINT_SPHERE_RADIUS * 2,
|
||||
|
@ -933,6 +940,7 @@ function MyController(hand) {
|
|||
var brightColor = colorPow(color, 0.06);
|
||||
if (this.searchSphere === null) {
|
||||
var sphereProperties = {
|
||||
name: "searchSphere",
|
||||
position: location,
|
||||
rotation: rotation,
|
||||
outerRadius: size * 1.2,
|
||||
|
@ -955,7 +963,8 @@ function MyController(hand) {
|
|||
innerAlpha: 1.0,
|
||||
outerAlpha: 0.0,
|
||||
outerRadius: size * 1.2,
|
||||
visible: true
|
||||
visible: true,
|
||||
ignoreRayIntersection: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -966,6 +975,7 @@ function MyController(hand) {
|
|||
}
|
||||
|
||||
var stylusProperties = {
|
||||
name: "stylus",
|
||||
url: Script.resourcesPath() + "meshes/tablet-stylus-fat.fbx",
|
||||
localPosition: Vec3.sum({ x: 0.0,
|
||||
y: WEB_TOUCH_Y_OFFSET,
|
||||
|
@ -1000,6 +1010,7 @@ function MyController(hand) {
|
|||
this.overlayLineOn = function(closePoint, farPoint, color) {
|
||||
if (this.overlayLine === null) {
|
||||
var lineProperties = {
|
||||
name: "line",
|
||||
glow: 1.0,
|
||||
start: closePoint,
|
||||
end: farPoint,
|
||||
|
@ -1175,6 +1186,13 @@ function MyController(hand) {
|
|||
}
|
||||
}
|
||||
|
||||
var candidateOverlays = Overlays.findOverlays(worldHandPosition, WEB_DISPLAY_STYLUS_DISTANCE);
|
||||
for (var j = 0; j < candidateOverlays.length; j++) {
|
||||
if (this.isTablet(candidateOverlays[j])) {
|
||||
nearWeb = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (nearWeb) {
|
||||
this.showStylus();
|
||||
var rayPickInfo = this.calcRayPickInfo(this.hand);
|
||||
|
@ -1423,7 +1441,7 @@ function MyController(hand) {
|
|||
|
||||
var okToEquipFromOtherHand = ((this.getOtherHandController().state == STATE_NEAR_GRABBING ||
|
||||
this.getOtherHandController().state == STATE_DISTANCE_HOLDING) &&
|
||||
this.getOtherHandController().grabbedEntity == hotspot.entityID);
|
||||
this.getOtherHandController().grabbedThingID == hotspot.entityID);
|
||||
var hasParent = true;
|
||||
if (props.parentID === NULL_UUID) {
|
||||
hasParent = false;
|
||||
|
@ -1581,7 +1599,7 @@ function MyController(hand) {
|
|||
|
||||
var farSearching = this.triggerSmoothedSqueezed() && (Date.now() - this.searchStartTime > FAR_SEARCH_DELAY);
|
||||
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.grabbedOverlay = null;
|
||||
this.isInitialGrab = false;
|
||||
this.preparingHoldRelease = false;
|
||||
|
@ -1589,7 +1607,7 @@ function MyController(hand) {
|
|||
this.checkForUnexpectedChildren();
|
||||
|
||||
if ((this.triggerSmoothedReleased() && this.secondaryReleased())) {
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.setState(STATE_OFF, "trigger released");
|
||||
return;
|
||||
}
|
||||
|
@ -1610,8 +1628,9 @@ function MyController(hand) {
|
|||
if (potentialEquipHotspot) {
|
||||
if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && holdEnabled) {
|
||||
this.grabbedHotspot = potentialEquipHotspot;
|
||||
this.grabbedEntity = potentialEquipHotspot.entityID;
|
||||
this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedEntity).name + "'");
|
||||
this.grabbedThingID = potentialEquipHotspot.entityID;
|
||||
this.grabbedIsOverlay = false;
|
||||
this.setState(STATE_HOLD, "equipping '" + entityPropertiesCache.getProps(this.grabbedThingID).name + "'");
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1622,6 +1641,11 @@ function MyController(hand) {
|
|||
return _this.entityIsNearGrabbable(entity, handPosition, NEAR_GRAB_MAX_DISTANCE);
|
||||
});
|
||||
|
||||
var candidateOverlays = Overlays.findOverlays(handPosition, NEAR_GRAB_RADIUS);
|
||||
var grabbableOverlays = candidateOverlays.filter(function(overlayID) {
|
||||
return Overlays.getProperty(overlayID, "grabbable");
|
||||
});
|
||||
|
||||
if (rayPickInfo.entityID) {
|
||||
this.intersectionDistance = rayPickInfo.distance;
|
||||
if (this.entityIsGrabbable(rayPickInfo.entityID) && rayPickInfo.distance < NEAR_GRAB_PICK_RADIUS) {
|
||||
|
@ -1633,6 +1657,23 @@ function MyController(hand) {
|
|||
this.intersectionDistance = 0;
|
||||
}
|
||||
|
||||
if (grabbableOverlays.length > 0) {
|
||||
grabbableOverlays.sort(function(a, b) {
|
||||
var aPosition = Overlays.getProperty(a, "position");
|
||||
var aDistance = Vec3.distance(aPosition, handPosition);
|
||||
var bPosition = Overlays.getProperty(b, "position");
|
||||
var bDistance = Vec3.distance(bPosition, handPosition);
|
||||
return aDistance - bDistance;
|
||||
});
|
||||
this.grabbedThingID = grabbableOverlays[0];
|
||||
this.grabbedIsOverlay = true;
|
||||
if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && nearGrabEnabled) {
|
||||
this.setState(STATE_NEAR_GRABBING, "near grab overlay '" +
|
||||
Overlays.getProperty(this.grabbedThingID, "name") + "'");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var entity;
|
||||
if (grabbableEntities.length > 0) {
|
||||
// sort by distance
|
||||
|
@ -1643,7 +1684,8 @@ function MyController(hand) {
|
|||
});
|
||||
entity = grabbableEntities[0];
|
||||
name = entityPropertiesCache.getProps(entity).name;
|
||||
this.grabbedEntity = entity;
|
||||
this.grabbedThingID = entity;
|
||||
this.grabbedIsOverlay = false;
|
||||
if (this.entityWantsTrigger(entity)) {
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
this.setState(STATE_NEAR_TRIGGER, "near trigger '" + name + "'");
|
||||
|
@ -1654,7 +1696,7 @@ function MyController(hand) {
|
|||
} else {
|
||||
// If near something grabbable, grab it!
|
||||
if ((this.triggerSmoothedGrab() || this.secondarySqueezed()) && nearGrabEnabled) {
|
||||
this.setState(STATE_NEAR_GRABBING, "near grab '" + name + "'");
|
||||
this.setState(STATE_NEAR_GRABBING, "near grab entity '" + name + "'");
|
||||
return;
|
||||
} else {
|
||||
// potentialNearGrabEntity = entity;
|
||||
|
@ -1677,7 +1719,8 @@ function MyController(hand) {
|
|||
name = entityPropertiesCache.getProps(entity).name;
|
||||
if (this.entityWantsTrigger(entity)) {
|
||||
if (this.triggerSmoothedGrab()) {
|
||||
this.grabbedEntity = entity;
|
||||
this.grabbedThingID = entity;
|
||||
this.grabbedIsOverlay = false;
|
||||
this.setState(STATE_FAR_TRIGGER, "far trigger '" + name + "'");
|
||||
return;
|
||||
} else {
|
||||
|
@ -1685,7 +1728,8 @@ function MyController(hand) {
|
|||
}
|
||||
} else if (this.entityIsDistanceGrabbable(rayPickInfo.entityID, handPosition)) {
|
||||
if (this.triggerSmoothedGrab() && !isEditing() && farGrabEnabled && farSearching) {
|
||||
this.grabbedEntity = entity;
|
||||
this.grabbedThingID = entity;
|
||||
this.grabbedIsOverlay = false;
|
||||
this.grabbedDistance = rayPickInfo.distance;
|
||||
this.setState(STATE_DISTANCE_HOLDING, "distance hold '" + name + "'");
|
||||
return;
|
||||
|
@ -1765,7 +1809,8 @@ function MyController(hand) {
|
|||
Entities.sendHoverOverEntity(entity, pointerEvent);
|
||||
}
|
||||
|
||||
this.grabbedEntity = entity;
|
||||
this.grabbedThingID = entity;
|
||||
this.grabbedIsOverlay = false;
|
||||
this.setState(STATE_ENTITY_STYLUS_TOUCHING, "begin touching entity '" + name + "'");
|
||||
return true;
|
||||
|
||||
|
@ -1893,7 +1938,8 @@ function MyController(hand) {
|
|||
}
|
||||
|
||||
if (this.triggerSmoothedGrab() && (!isEditing() || this.isTablet(entity))) {
|
||||
this.grabbedEntity = entity;
|
||||
this.grabbedThingID = entity;
|
||||
this.grabbedIsOverlay = false;
|
||||
this.setState(STATE_ENTITY_LASER_TOUCHING, "begin touching entity '" + name + "'");
|
||||
return true;
|
||||
}
|
||||
|
@ -2004,7 +2050,7 @@ function MyController(hand) {
|
|||
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
|
||||
var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition);
|
||||
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES);
|
||||
var now = Date.now();
|
||||
|
||||
// add the action and initialize some variables
|
||||
|
@ -2034,7 +2080,7 @@ function MyController(hand) {
|
|||
var timeScale = this.distanceGrabTimescale(this.mass, distanceToObject);
|
||||
|
||||
this.actionID = NULL_UUID;
|
||||
this.actionID = Entities.addAction("spring", this.grabbedEntity, {
|
||||
this.actionID = Entities.addAction("spring", this.grabbedThingID, {
|
||||
targetPosition: this.currentObjectPosition,
|
||||
linearTimeScale: timeScale,
|
||||
targetRotation: this.currentObjectRotation,
|
||||
|
@ -2059,12 +2105,12 @@ function MyController(hand) {
|
|||
this.ensureDynamic = function() {
|
||||
// if we distance hold something and keep it very still before releasing it, it ends up
|
||||
// non-dynamic in bullet. If it's too still, give it a little bounce so it will fall.
|
||||
var props = Entities.getEntityProperties(this.grabbedEntity, ["velocity", "dynamic", "parentID"]);
|
||||
var props = Entities.getEntityProperties(this.grabbedThingID, ["velocity", "dynamic", "parentID"]);
|
||||
if (props.dynamic && props.parentID == NULL_UUID) {
|
||||
var velocity = props.velocity;
|
||||
if (Vec3.length(velocity) < 0.05) { // see EntityMotionState.cpp DYNAMIC_LINEAR_VELOCITY_THRESHOLD
|
||||
velocity = { x: 0.0, y: 0.2, z:0.0 };
|
||||
Entities.editEntity(this.grabbedEntity, { velocity: velocity });
|
||||
Entities.editEntity(this.grabbedThingID, { velocity: velocity });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -2086,7 +2132,7 @@ function MyController(hand) {
|
|||
var worldToSensorMat = Mat4.inverse(MyAvatar.getSensorToWorldMatrix());
|
||||
var roomControllerPosition = Mat4.transformPoint(worldToSensorMat, worldControllerPosition);
|
||||
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES);
|
||||
|
||||
var now = Date.now();
|
||||
var deltaObjectTime = (now - this.currentObjectTime) / MSECS_PER_SEC; // convert to seconds
|
||||
|
@ -2141,7 +2187,7 @@ function MyController(hand) {
|
|||
newTargetPosition = Vec3.sum(newTargetPosition, this.offsetPosition);
|
||||
|
||||
var objectToAvatar = Vec3.subtract(this.currentObjectPosition, MyAvatar.position);
|
||||
var handControllerData = getEntityCustomData('handControllerKey', this.grabbedEntity, defaultMoveWithHeadData);
|
||||
var handControllerData = getEntityCustomData('handControllerKey', this.grabbedThingID, defaultMoveWithHeadData);
|
||||
if (handControllerData.disableMoveWithHead !== true) {
|
||||
// mix in head motion
|
||||
if (MOVE_WITH_HEAD) {
|
||||
|
@ -2170,7 +2216,7 @@ function MyController(hand) {
|
|||
this.overlayLineOn(rayPickInfo.searchRay.origin, Vec3.subtract(grabbedProperties.position, this.offsetPosition), COLORS_GRAB_DISTANCE_HOLD);
|
||||
|
||||
var distanceToObject = Vec3.length(Vec3.subtract(MyAvatar.position, this.currentObjectPosition));
|
||||
var success = Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
var success = Entities.updateAction(this.grabbedThingID, this.actionID, {
|
||||
targetPosition: newTargetPosition,
|
||||
linearTimeScale: this.distanceGrabTimescale(this.mass, distanceToObject),
|
||||
targetRotation: this.currentObjectRotation,
|
||||
|
@ -2187,7 +2233,7 @@ function MyController(hand) {
|
|||
};
|
||||
|
||||
this.setupHoldAction = function() {
|
||||
this.actionID = Entities.addAction("hold", this.grabbedEntity, {
|
||||
this.actionID = Entities.addAction("hold", this.grabbedThingID, {
|
||||
hand: this.hand === RIGHT_HAND ? "right" : "left",
|
||||
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
|
||||
relativePosition: this.offsetPosition,
|
||||
|
@ -2277,17 +2323,30 @@ function MyController(hand) {
|
|||
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
|
||||
|
||||
if (this.entityActivated) {
|
||||
var saveGrabbedID = this.grabbedEntity;
|
||||
var saveGrabbedID = this.grabbedThingID;
|
||||
this.release();
|
||||
this.grabbedEntity = saveGrabbedID;
|
||||
this.grabbedThingID = saveGrabbedID;
|
||||
}
|
||||
|
||||
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
|
||||
if (FORCE_IGNORE_IK) {
|
||||
var grabbedProperties;
|
||||
if (this.grabbedIsOverlay) {
|
||||
grabbedProperties = {
|
||||
position: Overlays.getProperty(this.grabbedThingID, "position"),
|
||||
rotation: Overlays.getProperty(this.grabbedThingID, "rotation"),
|
||||
parentID: Overlays.getProperty(this.grabbedThingID, "parentID"),
|
||||
parentJointIndex: Overlays.getProperty(this.grabbedThingID, "parentJointIndex"),
|
||||
dynamic: false,
|
||||
shapeType: "none"
|
||||
};
|
||||
this.ignoreIK = true;
|
||||
} else {
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
|
||||
this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true;
|
||||
grabbedProperties = Entities.getEntityProperties(this.grabbedThingID, GRABBABLE_PROPERTIES);
|
||||
if (FORCE_IGNORE_IK) {
|
||||
this.ignoreIK = true;
|
||||
} else {
|
||||
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedThingID, DEFAULT_GRABBABLE_DATA);
|
||||
this.ignoreIK = (grabbableData.ignoreIK !== undefined) ? grabbableData.ignoreIK : true;
|
||||
}
|
||||
}
|
||||
|
||||
var handRotation;
|
||||
|
@ -2326,7 +2385,8 @@ function MyController(hand) {
|
|||
this.offsetPosition = Vec3.multiplyQbyV(Quat.inverse(Quat.multiply(handRotation, this.offsetRotation)), offset);
|
||||
}
|
||||
|
||||
var isPhysical = propsArePhysical(grabbedProperties) || entityHasActions(this.grabbedEntity);
|
||||
var isPhysical = propsArePhysical(grabbedProperties) ||
|
||||
(!this.grabbedIsOverlay && entityHasActions(this.grabbedThingID));
|
||||
if (isPhysical && this.state == STATE_NEAR_GRABBING && grabbedProperties.parentID === NULL_UUID) {
|
||||
// grab entity via action
|
||||
if (!this.setupHoldAction()) {
|
||||
|
@ -2334,7 +2394,7 @@ function MyController(hand) {
|
|||
}
|
||||
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
|
||||
action: 'grab',
|
||||
grabbedEntity: this.grabbedEntity,
|
||||
grabbedEntity: this.grabbedThingID,
|
||||
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
|
||||
}));
|
||||
} else {
|
||||
|
@ -2359,29 +2419,36 @@ function MyController(hand) {
|
|||
reparentProps.localPosition = this.offsetPosition;
|
||||
reparentProps.localRotation = this.offsetRotation;
|
||||
}
|
||||
Entities.editEntity(this.grabbedEntity, reparentProps);
|
||||
|
||||
if (this.grabbedIsOverlay) {
|
||||
Overlays.editOverlay(this.grabbedThingID, reparentProps);
|
||||
} else {
|
||||
Entities.editEntity(this.grabbedThingID, reparentProps);
|
||||
}
|
||||
|
||||
if (this.thisHandIsParent(grabbedProperties)) {
|
||||
// this should never happen, but if it does, don't set previous parent to be this hand.
|
||||
// this.previousParentID[this.grabbedEntity] = NULL;
|
||||
// this.previousParentJointIndex[this.grabbedEntity] = -1;
|
||||
// this.previousParentID[this.grabbedThingID] = NULL;
|
||||
// this.previousParentJointIndex[this.grabbedThingID] = -1;
|
||||
} else {
|
||||
this.previousParentID[this.grabbedEntity] = grabbedProperties.parentID;
|
||||
this.previousParentJointIndex[this.grabbedEntity] = grabbedProperties.parentJointIndex;
|
||||
this.previousParentID[this.grabbedThingID] = grabbedProperties.parentID;
|
||||
this.previousParentJointIndex[this.grabbedThingID] = grabbedProperties.parentJointIndex;
|
||||
}
|
||||
|
||||
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
|
||||
action: 'equip',
|
||||
grabbedEntity: this.grabbedEntity,
|
||||
grabbedEntity: this.grabbedThingID,
|
||||
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
|
||||
}));
|
||||
}
|
||||
|
||||
Entities.editEntity(this.grabbedEntity, {
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
angularVelocity: { x: 0, y: 0, z: 0 },
|
||||
// dynamic: false
|
||||
});
|
||||
if (!this.grabbedIsOverlay) {
|
||||
Entities.editEntity(this.grabbedThingID, {
|
||||
velocity: { x: 0, y: 0, z: 0 },
|
||||
angularVelocity: { x: 0, y: 0, z: 0 },
|
||||
// dynamic: false
|
||||
});
|
||||
}
|
||||
|
||||
if (this.state == STATE_NEAR_GRABBING) {
|
||||
this.callEntityMethodOnGrabbed("startNearGrab");
|
||||
|
@ -2447,26 +2514,39 @@ function MyController(hand) {
|
|||
|
||||
if (dropDetected && !this.waitForTriggerRelease && this.triggerSmoothedGrab()) {
|
||||
// store the offset attach points into preferences.
|
||||
if (USE_ATTACH_POINT_SETTINGS && this.grabbedHotspot && this.grabbedEntity) {
|
||||
var prefprops = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "localRotation"]);
|
||||
if (USE_ATTACH_POINT_SETTINGS && this.grabbedHotspot && this.grabbedThingID) {
|
||||
var prefprops = Entities.getEntityProperties(this.grabbedThingID, ["localPosition", "localRotation"]);
|
||||
if (prefprops && prefprops.localPosition && prefprops.localRotation) {
|
||||
storeAttachPointForHotspotInSettings(this.grabbedHotspot, this.hand,
|
||||
prefprops.localPosition, prefprops.localRotation);
|
||||
}
|
||||
}
|
||||
|
||||
var grabbedEntity = this.grabbedEntity;
|
||||
var grabbedEntity = this.grabbedThingID;
|
||||
this.release();
|
||||
this.grabbedEntity = grabbedEntity;
|
||||
this.grabbedThingID = grabbedEntity;
|
||||
this.setState(STATE_NEAR_GRABBING, "drop gesture detected");
|
||||
return;
|
||||
}
|
||||
this.prevDropDetected = dropDetected;
|
||||
}
|
||||
|
||||
var props = Entities.getEntityProperties(this.grabbedEntity, ["localPosition", "parentID", "parentJointIndex",
|
||||
var props;
|
||||
if (this.grabbedIsOverlay) {
|
||||
props = {
|
||||
localPosition: Overlays.getProperty(this.grabbedThingID, "localPosition"),
|
||||
parentID: Overlays.getProperty(this.grabbedThingID, "parentID"),
|
||||
parentJointIndex: Overlays.getProperty(this.grabbedThingID, "parentJointIndex"),
|
||||
position: Overlays.getProperty(this.grabbedThingID, "position"),
|
||||
rotation: Overlays.getProperty(this.grabbedThingID, "rotation"),
|
||||
dimensions: Overlays.getProperty(this.grabbedThingID, "dimensions"),
|
||||
registrationPoint: { x: 0.5, y: 0.5, z: 0.5 }
|
||||
};
|
||||
} else {
|
||||
props = Entities.getEntityProperties(this.grabbedThingID, ["localPosition", "parentID", "parentJointIndex",
|
||||
"position", "rotation", "dimensions",
|
||||
"registrationPoint"]);
|
||||
}
|
||||
if (!props.position) {
|
||||
// server may have reset, taking our equipped entity with it. move back to "off" state
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
|
@ -2478,7 +2558,7 @@ function MyController(hand) {
|
|||
// someone took it from us or otherwise edited the parentID. end the grab. We don't do this
|
||||
// for equipped things so that they can be adjusted while equipped.
|
||||
this.callEntityMethodOnGrabbed("releaseGrab");
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.setState(STATE_OFF, "someone took it");
|
||||
return;
|
||||
}
|
||||
|
@ -2560,7 +2640,7 @@ function MyController(hand) {
|
|||
|
||||
if (this.actionID && this.actionTimeout - now < ACTION_TTL_REFRESH * MSECS_PER_SEC) {
|
||||
// if less than a 5 seconds left, refresh the actions ttl
|
||||
var success = Entities.updateAction(this.grabbedEntity, this.actionID, {
|
||||
var success = Entities.updateAction(this.grabbedThingID, this.actionID, {
|
||||
hand: this.hand === RIGHT_HAND ? "right" : "left",
|
||||
timeScale: NEAR_GRABBING_ACTION_TIMEFRAME,
|
||||
relativePosition: this.offsetPosition,
|
||||
|
@ -2574,14 +2654,14 @@ function MyController(hand) {
|
|||
this.actionTimeout = now + (ACTION_TTL * MSECS_PER_SEC);
|
||||
} else {
|
||||
print("continueNearGrabbing -- updateAction failed");
|
||||
Entities.deleteAction(this.grabbedEntity, this.actionID);
|
||||
Entities.deleteAction(this.grabbedThingID, this.actionID);
|
||||
this.setupHoldAction();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.maybeScale = function(props) {
|
||||
if (!objectScalingEnabled || this.isTablet(this.grabbedEntity)) {
|
||||
if (!objectScalingEnabled || this.isTablet(this.grabbedThingID) || this.grabbedIsOverlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2603,7 +2683,7 @@ function MyController(hand) {
|
|||
this.getOtherHandController().getHandPosition()));
|
||||
var currentRescale = scalingCurrentDistance / this.scalingStartDistance;
|
||||
var newDimensions = Vec3.multiply(currentRescale, this.scalingStartDimensions);
|
||||
Entities.editEntity(this.grabbedEntity, { dimensions: newDimensions });
|
||||
Entities.editEntity(this.grabbedThingID, { dimensions: newDimensions });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2654,7 +2734,7 @@ function MyController(hand) {
|
|||
this.nearTrigger = function(deltaTime, timestamp) {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.callEntityMethodOnGrabbed("stopNearTrigger");
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.setState(STATE_OFF, "trigger released");
|
||||
return;
|
||||
}
|
||||
|
@ -2664,7 +2744,7 @@ function MyController(hand) {
|
|||
this.farTrigger = function(deltaTime, timestamp) {
|
||||
if (this.triggerSmoothedReleased()) {
|
||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.setState(STATE_OFF, "trigger released");
|
||||
return;
|
||||
}
|
||||
|
@ -2679,9 +2759,9 @@ function MyController(hand) {
|
|||
var intersection = findRayIntersection(pickRay, true, [], [], true);
|
||||
if (intersection.accurate || intersection.overlayID) {
|
||||
this.lastPickTime = now;
|
||||
if (intersection.entityID != this.grabbedEntity) {
|
||||
if (intersection.entityID != this.grabbedThingID) {
|
||||
this.callEntityMethodOnGrabbed("stopFarTrigger");
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.setState(STATE_OFF, "laser moved off of entity");
|
||||
return;
|
||||
}
|
||||
|
@ -2703,13 +2783,13 @@ function MyController(hand) {
|
|||
|
||||
this.entityTouchingEnter = function() {
|
||||
// test for intersection between controller laser and web entity plane.
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedEntity,
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedThingID,
|
||||
getControllerWorldLocation(this.handToController(), true));
|
||||
if (intersectInfo) {
|
||||
var pointerEvent = {
|
||||
type: "Press",
|
||||
id: this.hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point),
|
||||
pos2D: projectOntoEntityXYPlane(this.grabbedThingID, intersectInfo.point),
|
||||
pos3D: intersectInfo.point,
|
||||
normal: intersectInfo.normal,
|
||||
direction: intersectInfo.searchRay.direction,
|
||||
|
@ -2717,8 +2797,8 @@ function MyController(hand) {
|
|||
isPrimaryHeld: true
|
||||
};
|
||||
|
||||
Entities.sendMousePressOnEntity(this.grabbedEntity, pointerEvent);
|
||||
Entities.sendClickDownOnEntity(this.grabbedEntity, pointerEvent);
|
||||
Entities.sendMousePressOnEntity(this.grabbedThingID, pointerEvent);
|
||||
Entities.sendClickDownOnEntity(this.grabbedThingID, pointerEvent);
|
||||
|
||||
this.touchingEnterTimer = 0;
|
||||
this.touchingEnterPointerEvent = pointerEvent;
|
||||
|
@ -2740,7 +2820,7 @@ function MyController(hand) {
|
|||
|
||||
this.entityTouchingExit = function() {
|
||||
// test for intersection between controller laser and web entity plane.
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedEntity,
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedThingID,
|
||||
getControllerWorldLocation(this.handToController(), true));
|
||||
if (intersectInfo) {
|
||||
var pointerEvent;
|
||||
|
@ -2748,7 +2828,7 @@ function MyController(hand) {
|
|||
pointerEvent = {
|
||||
type: "Release",
|
||||
id: this.hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point),
|
||||
pos2D: projectOntoEntityXYPlane(this.grabbedThingID, intersectInfo.point),
|
||||
pos3D: intersectInfo.point,
|
||||
normal: intersectInfo.normal,
|
||||
direction: intersectInfo.searchRay.direction,
|
||||
|
@ -2761,11 +2841,11 @@ function MyController(hand) {
|
|||
pointerEvent.isPrimaryHeld = false;
|
||||
}
|
||||
|
||||
Entities.sendMouseReleaseOnEntity(this.grabbedEntity, pointerEvent);
|
||||
Entities.sendClickReleaseOnEntity(this.grabbedEntity, pointerEvent);
|
||||
Entities.sendHoverLeaveEntity(this.grabbedEntity, pointerEvent);
|
||||
Entities.sendMouseReleaseOnEntity(this.grabbedThingID, pointerEvent);
|
||||
Entities.sendClickReleaseOnEntity(this.grabbedThingID, pointerEvent);
|
||||
Entities.sendHoverLeaveEntity(this.grabbedThingID, pointerEvent);
|
||||
}
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.grabbedOverlay = null;
|
||||
};
|
||||
|
||||
|
@ -2773,7 +2853,7 @@ function MyController(hand) {
|
|||
|
||||
this.touchingEnterTimer += dt;
|
||||
|
||||
entityPropertiesCache.addEntity(this.grabbedEntity);
|
||||
entityPropertiesCache.addEntity(this.grabbedThingID);
|
||||
|
||||
if (this.state == STATE_ENTITY_LASER_TOUCHING && !this.triggerSmoothedGrab()) {
|
||||
this.setState(STATE_OFF, "released trigger");
|
||||
|
@ -2781,7 +2861,7 @@ function MyController(hand) {
|
|||
}
|
||||
|
||||
// test for intersection between controller laser and web entity plane.
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedEntity,
|
||||
var intersectInfo = handLaserIntersectEntity(this.grabbedThingID,
|
||||
getControllerWorldLocation(this.handToController(), true));
|
||||
if (intersectInfo) {
|
||||
|
||||
|
@ -2791,15 +2871,15 @@ function MyController(hand) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (Entities.keyboardFocusEntity != this.grabbedEntity) {
|
||||
if (Entities.keyboardFocusEntity != this.grabbedThingID) {
|
||||
Overlays.keyboardFocusOverlay = 0;
|
||||
Entities.keyboardFocusEntity = this.grabbedEntity;
|
||||
Entities.keyboardFocusEntity = this.grabbedThingID;
|
||||
}
|
||||
|
||||
var pointerEvent = {
|
||||
type: "Move",
|
||||
id: this.hand + 1, // 0 is reserved for hardware mouse
|
||||
pos2D: projectOntoEntityXYPlane(this.grabbedEntity, intersectInfo.point),
|
||||
pos2D: projectOntoEntityXYPlane(this.grabbedThingID, intersectInfo.point),
|
||||
pos3D: intersectInfo.point,
|
||||
normal: intersectInfo.normal,
|
||||
direction: intersectInfo.searchRay.direction,
|
||||
|
@ -2810,8 +2890,8 @@ function MyController(hand) {
|
|||
var POINTER_PRESS_TO_MOVE_DELAY = 0.25; // seconds
|
||||
if (this.deadspotExpired || this.touchingEnterTimer > POINTER_PRESS_TO_MOVE_DELAY ||
|
||||
Vec3.distance(intersectInfo.point, this.touchingEnterPointerEvent.pos3D) > this.deadspotRadius) {
|
||||
Entities.sendMouseMoveOnEntity(this.grabbedEntity, pointerEvent);
|
||||
Entities.sendHoldingClickOnEntity(this.grabbedEntity, pointerEvent);
|
||||
Entities.sendMouseMoveOnEntity(this.grabbedThingID, pointerEvent);
|
||||
Entities.sendHoldingClickOnEntity(this.grabbedThingID, pointerEvent);
|
||||
this.deadspotExpired = true;
|
||||
}
|
||||
|
||||
|
@ -2821,7 +2901,7 @@ function MyController(hand) {
|
|||
}
|
||||
Reticle.setVisible(false);
|
||||
} else {
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.setState(STATE_OFF, "grabbed entity was destroyed");
|
||||
return;
|
||||
}
|
||||
|
@ -2906,7 +2986,7 @@ function MyController(hand) {
|
|||
Overlays.sendMouseReleaseOnOverlay(this.grabbedOverlay, pointerEvent);
|
||||
Overlays.sendHoverLeaveOverlay(this.grabbedOverlay, pointerEvent);
|
||||
}
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.grabbedOverlay = null;
|
||||
};
|
||||
|
||||
|
@ -2929,7 +3009,7 @@ function MyController(hand) {
|
|||
|
||||
if (this.state == STATE_OVERLAY_STYLUS_TOUCHING &&
|
||||
intersectInfo.distance > WEB_STYLUS_LENGTH / 2.0 + WEB_TOUCH_Y_OFFSET + WEB_TOUCH_Y_TOUCH_DEADZONE_SIZE) {
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.setState(STATE_OFF, "pulled away from overlay");
|
||||
return;
|
||||
}
|
||||
|
@ -2986,7 +3066,7 @@ function MyController(hand) {
|
|||
}
|
||||
Reticle.setVisible(false);
|
||||
} else {
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.setState(STATE_OFF, "grabbed overlay was destroyed");
|
||||
return;
|
||||
}
|
||||
|
@ -2995,7 +3075,7 @@ function MyController(hand) {
|
|||
this.release = function() {
|
||||
this.turnOffVisualizations();
|
||||
|
||||
if (this.grabbedEntity !== null) {
|
||||
if (this.grabbedThingID !== null) {
|
||||
if (this.state === STATE_HOLD) {
|
||||
this.callEntityMethodOnGrabbed("releaseEquip");
|
||||
}
|
||||
|
@ -3003,35 +3083,49 @@ function MyController(hand) {
|
|||
// Make a small release haptic pulse if we really were holding something
|
||||
Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand);
|
||||
if (this.actionID !== null) {
|
||||
Entities.deleteAction(this.grabbedEntity, this.actionID);
|
||||
Entities.deleteAction(this.grabbedThingID, this.actionID);
|
||||
} else {
|
||||
// no action, so it's a parenting grab
|
||||
if (this.previousParentID[this.grabbedEntity] === NULL_UUID) {
|
||||
Entities.editEntity(this.grabbedEntity, {
|
||||
parentID: this.previousParentID[this.grabbedEntity],
|
||||
parentJointIndex: this.previousParentJointIndex[this.grabbedEntity]
|
||||
});
|
||||
this.ensureDynamic();
|
||||
if (this.previousParentID[this.grabbedThingID] === NULL_UUID) {
|
||||
if (this.grabbedIsOverlay) {
|
||||
Overlays.editOverlay(this.grabbedThingID, {
|
||||
parentID: NULL_UUID,
|
||||
parentJointIndex: -1
|
||||
});
|
||||
} else {
|
||||
Entities.editEntity(this.grabbedThingID, {
|
||||
parentID: this.previousParentID[this.grabbedThingID],
|
||||
parentJointIndex: this.previousParentJointIndex[this.grabbedThingID]
|
||||
});
|
||||
this.ensureDynamic();
|
||||
}
|
||||
} else {
|
||||
// we're putting this back as a child of some other parent, so zero its velocity
|
||||
Entities.editEntity(this.grabbedEntity, {
|
||||
parentID: this.previousParentID[this.grabbedEntity],
|
||||
parentJointIndex: this.previousParentJointIndex[this.grabbedEntity],
|
||||
velocity: {x: 0, y: 0, z: 0},
|
||||
angularVelocity: {x: 0, y: 0, z: 0}
|
||||
});
|
||||
if (this.grabbedIsOverlay) {
|
||||
Overlays.editOverlay(this.grabbedThingID, {
|
||||
parentID: this.previousParentID[this.grabbedThingID],
|
||||
parentJointIndex: this.previousParentJointIndex[this.grabbedThingID],
|
||||
});
|
||||
} else {
|
||||
// we're putting this back as a child of some other parent, so zero its velocity
|
||||
Entities.editEntity(this.grabbedThingID, {
|
||||
parentID: this.previousParentID[this.grabbedThingID],
|
||||
parentJointIndex: this.previousParentJointIndex[this.grabbedThingID],
|
||||
velocity: {x: 0, y: 0, z: 0},
|
||||
angularVelocity: {x: 0, y: 0, z: 0}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Messages.sendMessage('Hifi-Object-Manipulation', JSON.stringify({
|
||||
action: 'release',
|
||||
grabbedEntity: this.grabbedEntity,
|
||||
grabbedEntity: this.grabbedThingID,
|
||||
joint: this.hand === RIGHT_HAND ? "RightHand" : "LeftHand"
|
||||
}));
|
||||
}
|
||||
|
||||
this.actionID = null;
|
||||
this.grabbedEntity = null;
|
||||
this.grabbedThingID = null;
|
||||
this.grabbedOverlay = null;
|
||||
this.grabbedHotspot = null;
|
||||
|
||||
|
@ -3119,9 +3213,13 @@ function MyController(hand) {
|
|||
}
|
||||
_this.previouslyUnhooked[childID] = now;
|
||||
|
||||
// we don't know if it's an entity or an overlay
|
||||
Entities.editEntity(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex });
|
||||
Overlays.editOverlay(childID, { parentID: previousParentID, parentJointIndex: previousParentJointIndex });
|
||||
|
||||
} else {
|
||||
Entities.editEntity(childID, { parentID: NULL_UUID });
|
||||
Overlays.editOverlay(childID, { parentID: NULL_UUID });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -3240,8 +3338,8 @@ var handleHandMessages = function(channel, message, sender) {
|
|||
selectedController.release();
|
||||
var wearableEntity = data.entityID;
|
||||
entityPropertiesCache.addEntity(wearableEntity);
|
||||
selectedController.grabbedEntity = wearableEntity;
|
||||
var hotspots = selectedController.collectEquipHotspots(selectedController.grabbedEntity);
|
||||
selectedController.grabbedThingID = wearableEntity;
|
||||
var hotspots = selectedController.collectEquipHotspots(selectedController.grabbedThingID);
|
||||
if (hotspots.length > 0) {
|
||||
if (hotspotIndex >= hotspots.length) {
|
||||
hotspotIndex = 0;
|
||||
|
|
|
@ -564,6 +564,11 @@ function findClickedEntity(event) {
|
|||
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
|
||||
var overlayResult = Overlays.findRayIntersection(pickRay, true, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]);
|
||||
if (overlayResult.intersects) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking
|
||||
var lightResult = lightOverlayManager.findRayIntersection(pickRay);
|
||||
lightResult.accurate = true;
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
/* global getControllerWorldLocation, setEntityCustomData, Tablet, WebTablet:true, HMD, Settings, Script,
|
||||
Vec3, Quat, MyAvatar, Entities, Overlays, Camera, Messages, Xform, clamp */
|
||||
/* global getControllerWorldLocation, Tablet, WebTablet:true, HMD, Settings, Script,
|
||||
Vec3, Quat, MyAvatar, Entities, Overlays, Camera, Messages, Xform, clamp, Controller, Mat4 */
|
||||
|
||||
Script.include(Script.resolvePath("../libraries/utils.js"));
|
||||
Script.include(Script.resolvePath("../libraries/controllers.js"));
|
||||
|
@ -34,7 +34,7 @@ var TABLET_NATURAL_DIMENSIONS = {x: 33.797, y: 50.129, z: 2.269};
|
|||
var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png";
|
||||
// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-close.png";
|
||||
var TABLET_MODEL_PATH = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx";
|
||||
// var TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx";
|
||||
var LOCAL_TABLET_MODEL_PATH = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx";
|
||||
|
||||
// returns object with two fields:
|
||||
// * position - position in front of the user
|
||||
|
@ -112,10 +112,19 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
|
|||
this.dpi = DEFAULT_DPI * (DEFAULT_WIDTH / this.width);
|
||||
}
|
||||
|
||||
var modelURL;
|
||||
if (Settings.getValue("tabletVisibleToOthers")) {
|
||||
modelURL = TABLET_MODEL_PATH;
|
||||
} else {
|
||||
modelURL = LOCAL_TABLET_MODEL_PATH;
|
||||
}
|
||||
|
||||
var tabletProperties = {
|
||||
name: "WebTablet Tablet",
|
||||
type: "Model",
|
||||
modelURL: TABLET_MODEL_PATH,
|
||||
modelURL: modelURL,
|
||||
url: modelURL, // for overlay
|
||||
grabbable: true, // for overlay
|
||||
userData: JSON.stringify({
|
||||
"grabbableKey": {"grabbable": true}
|
||||
}),
|
||||
|
@ -127,7 +136,14 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
|
|||
this.calculateTabletAttachmentProperties(hand, true, tabletProperties);
|
||||
|
||||
this.cleanUpOldTablets();
|
||||
this.tabletEntityID = Entities.addEntity(tabletProperties, clientOnly);
|
||||
|
||||
if (Settings.getValue("tabletVisibleToOthers")) {
|
||||
this.tabletEntityID = Entities.addEntity(tabletProperties, clientOnly);
|
||||
this.tabletIsOverlay = false;
|
||||
} else {
|
||||
this.tabletEntityID = Overlays.addOverlay("model", tabletProperties);
|
||||
this.tabletIsOverlay = true;
|
||||
}
|
||||
|
||||
if (this.webOverlayID) {
|
||||
Overlays.deleteOverlay(this.webOverlayID);
|
||||
|
@ -151,12 +167,12 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
|
|||
isAA: HMD.active
|
||||
});
|
||||
|
||||
var HOME_BUTTON_Y_OFFSET = (this.height / 2) - 0.035;
|
||||
this.homeButtonEntity = Overlays.addOverlay("sphere", {
|
||||
var HOME_BUTTON_Y_OFFSET = (this.height / 2) - 0.009;
|
||||
this.homeButtonID = Overlays.addOverlay("sphere", {
|
||||
name: "homeButton",
|
||||
localPosition: {x: 0.0, y: -HOME_BUTTON_Y_OFFSET, z: -0.01},
|
||||
localPosition: {x: -0.001, y: -HOME_BUTTON_Y_OFFSET, z: 0.0},
|
||||
localRotation: Quat.angleAxis(0, Y_AXIS),
|
||||
dimensions: { x: 0.04, y: 0.04, z: 0.02},
|
||||
dimensions: { x: 4 * tabletScaleFactor, y: 4 * tabletScaleFactor, z: 4 * tabletScaleFactor},
|
||||
alpha: 0.0,
|
||||
visible: true,
|
||||
drawInFront: false,
|
||||
|
@ -165,7 +181,7 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
|
|||
});
|
||||
|
||||
this.receive = function (channel, senderID, senderUUID, localOnly) {
|
||||
if (_this.homeButtonEntity == senderID) {
|
||||
if (_this.homeButtonID == senderID) {
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var onHomeScreen = tablet.onHomeScreen();
|
||||
if (onHomeScreen) {
|
||||
|
@ -184,7 +200,16 @@ WebTablet = function (url, width, dpi, hand, clientOnly) {
|
|||
};
|
||||
|
||||
this.getLocation = function() {
|
||||
return Entities.getEntityProperties(_this.tabletEntityID, ["localPosition", "localRotation"]);
|
||||
if (this.tabletIsOverlay) {
|
||||
var location = Overlays.getProperty(this.tabletEntityID, "localPosition");
|
||||
var orientation = Overlays.getProperty(this.tabletEntityID, "localOrientation");
|
||||
return {
|
||||
localPosition: location,
|
||||
localRotation: orientation
|
||||
};
|
||||
} else {
|
||||
return Entities.getEntityProperties(_this.tabletEntityID, ["localPosition", "localRotation"]);
|
||||
}
|
||||
};
|
||||
this.clicked = false;
|
||||
|
||||
|
@ -242,8 +267,12 @@ WebTablet.prototype.getOverlayObject = function () {
|
|||
|
||||
WebTablet.prototype.destroy = function () {
|
||||
Overlays.deleteOverlay(this.webOverlayID);
|
||||
Entities.deleteEntity(this.tabletEntityID);
|
||||
Overlays.deleteOverlay(this.homeButtonEntity);
|
||||
if (this.tabletIsOverlay) {
|
||||
Overlays.deleteOverlay(this.tabletEntityID);
|
||||
} else {
|
||||
Entities.deleteEntity(this.tabletEntityID);
|
||||
}
|
||||
Overlays.deleteOverlay(this.homeButtonID);
|
||||
HMD.displayModeChanged.disconnect(this.myOnHmdChanged);
|
||||
|
||||
Controller.mousePressEvent.disconnect(this.myMousePressEvent);
|
||||
|
@ -426,10 +455,16 @@ WebTablet.prototype.getPosition = function () {
|
|||
|
||||
WebTablet.prototype.mousePressEvent = function (event) {
|
||||
var pickRay = Camera.computePickRay(event.x, event.y);
|
||||
var entityPickResults = Entities.findRayIntersection(pickRay, true, [this.tabletEntityID]); // non-accurate picking
|
||||
if (entityPickResults.intersects && entityPickResults.entityID === this.tabletEntityID) {
|
||||
var overlayPickResults = Overlays.findRayIntersection(pickRay);
|
||||
if (overlayPickResults.intersects && overlayPickResults.overlayID === HMD.homeButtonID) {
|
||||
var entityPickResults;
|
||||
if (this.tabletIsOverlay) {
|
||||
entityPickResults = Overlays.findRayIntersection(pickRay, true, [this.tabletEntityID]);
|
||||
} else {
|
||||
entityPickResults = Entities.findRayIntersection(pickRay, true, [this.tabletEntityID]);
|
||||
}
|
||||
if (entityPickResults.intersects && (entityPickResults.entityID === this.tabletEntityID ||
|
||||
entityPickResults.overlayID === this.tabletEntityID)) {
|
||||
var overlayPickResults = Overlays.findRayIntersection(pickRay, true, [this.webOverlayID, this.homeButtonID], []);
|
||||
if (overlayPickResults.intersects && overlayPickResults.overlayID === this.homeButtonID) {
|
||||
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
|
||||
var onHomeScreen = tablet.onHomeScreen();
|
||||
if (onHomeScreen) {
|
||||
|
@ -438,11 +473,15 @@ WebTablet.prototype.mousePressEvent = function (event) {
|
|||
tablet.gotoHomeScreen();
|
||||
this.setHomeButtonTexture();
|
||||
}
|
||||
} else if (!HMD.active && (!overlayPickResults.intersects || !overlayPickResults.overlayID === this.webOverlayID)) {
|
||||
} else if (!HMD.active && (!overlayPickResults.intersects || overlayPickResults.overlayID !== this.webOverlayID)) {
|
||||
this.dragging = true;
|
||||
var invCameraXform = new Xform(Camera.orientation, Camera.position).inv();
|
||||
this.initialLocalIntersectionPoint = invCameraXform.xformPoint(entityPickResults.intersection);
|
||||
this.initialLocalPosition = Entities.getEntityProperties(this.tabletEntityID, ["localPosition"]).localPosition;
|
||||
if (this.tabletIsOverlay) {
|
||||
this.initialLocalPosition = Overlays.getProperty(this.tabletEntityID, "localPosition");
|
||||
} else {
|
||||
this.initialLocalPosition = Entities.getEntityProperties(this.tabletEntityID, ["localPosition"]).localPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -488,9 +527,15 @@ WebTablet.prototype.mouseMoveEvent = function (event) {
|
|||
var localIntersectionPoint = Vec3.sum(localPickRay.origin, Vec3.multiply(localPickRay.direction, result.distance));
|
||||
var localOffset = Vec3.subtract(localIntersectionPoint, this.initialLocalIntersectionPoint);
|
||||
var localPosition = Vec3.sum(this.initialLocalPosition, localOffset);
|
||||
Entities.editEntity(this.tabletEntityID, {
|
||||
localPosition: localPosition
|
||||
});
|
||||
if (this.tabletIsOverlay) {
|
||||
Overlays.editOverlay(this.tabletEntityID, {
|
||||
localPosition: localPosition
|
||||
});
|
||||
} else {
|
||||
Entities.editEntity(this.tabletEntityID, {
|
||||
localPosition: localPosition
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -3866,6 +3866,12 @@ SelectionDisplay = (function() {
|
|||
var somethingClicked = false;
|
||||
var pickRay = generalComputePickRay(event.x, event.y);
|
||||
|
||||
var result = Overlays.findRayIntersection(pickRay, true, [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID]);
|
||||
if (result.intersects) {
|
||||
// mouse clicks on the tablet should override the edit affordances
|
||||
return false;
|
||||
}
|
||||
|
||||
// before we do a ray test for grabbers, disable the ray intersection for our selection box
|
||||
Overlays.editOverlay(selectionBox, {
|
||||
ignoreRayIntersection: true
|
||||
|
|
|
@ -26,12 +26,19 @@
|
|||
print("show tablet-ui");
|
||||
|
||||
var DEFAULT_WIDTH = 0.4375;
|
||||
var DEFAULT_HMD_TABLET_SCALE = 100;
|
||||
var HMD_TABLET_SCALE = Settings.getValue("hmdTabletScale") || DEFAULT_HMD_TABLET_SCALE;
|
||||
UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", DEFAULT_WIDTH * (HMD_TABLET_SCALE / 100), null, activeHand, true);
|
||||
var DEFAULT_TABLET_SCALE = 100;
|
||||
var toolbarMode = Tablet.getTablet("com.highfidelity.interface.tablet.system").toolbarMode;
|
||||
var TABLET_SCALE = DEFAULT_TABLET_SCALE;
|
||||
if (toolbarMode) {
|
||||
TABLET_SCALE = Settings.getValue("desktopTabletScale") || DEFAULT_TABLET_SCALE;
|
||||
} else {
|
||||
TABLET_SCALE = Settings.getValue("hmdTabletScale") || DEFAULT_TABLET_SCALE;
|
||||
}
|
||||
UIWebTablet = new WebTablet("qml/hifi/tablet/TabletRoot.qml", DEFAULT_WIDTH * (TABLET_SCALE / 100), null, activeHand, true);
|
||||
UIWebTablet.register();
|
||||
HMD.tabletID = UIWebTablet.tabletEntityID;
|
||||
HMD.homeButtonID = UIWebTablet.homeButtonEntity;
|
||||
HMD.homeButtonID = UIWebTablet.homeButtonID;
|
||||
HMD.tabletScreenID = UIWebTablet.webOverlayID;
|
||||
}
|
||||
|
||||
function hideTabletUI() {
|
||||
|
@ -48,6 +55,7 @@
|
|||
UIWebTablet = null;
|
||||
HMD.tabletID = null;
|
||||
HMD.homeButtonID = null;
|
||||
HMD.tabletScreenID = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -77,7 +85,7 @@
|
|||
hideTabletUI();
|
||||
HMD.closeTablet();
|
||||
} else if (HMD.showTablet && !tabletShown && !toolbarMode) {
|
||||
UserActivityLogger.openedTablet();
|
||||
UserActivityLogger.openedTablet(Settings.getValue("tabletVisibleToOthers"));
|
||||
showTabletUI();
|
||||
} else if (!HMD.showTablet && tabletShown) {
|
||||
UserActivityLogger.closedTablet();
|
||||
|
@ -126,5 +134,6 @@
|
|||
Entities.deleteEntity(HMD.tabletID);
|
||||
HMD.tabletID = null;
|
||||
HMD.homeButtonID = null;
|
||||
HMD.tabletScreenID = null;
|
||||
});
|
||||
}()); // END LOCAL_SCOPE
|
||||
|
|
111
unpublishedScripts/marketplace/shortbow/README.md
Normal file
111
unpublishedScripts/marketplace/shortbow/README.md
Normal file
|
@ -0,0 +1,111 @@
|
|||
# Shortbow
|
||||
|
||||
Shortbow is a wave-based archery game.
|
||||
|
||||
## Notes
|
||||
|
||||
There are several design decisions that are based on certain characteristics of the High Fidelity platform,
|
||||
and in particular, [server entity scripts](https://wiki.highfidelity.com/wiki/Creating_Entity_Server_Scripts),
|
||||
which are touched on below.
|
||||
It is recommended that you understand the basics of client entity scripts and server entity scripts (and their
|
||||
differences) if you plan on digging into the shortbow code.
|
||||
|
||||
* Client entity scripts end in `ClientEntity.js` and server entity scripts end in `ServerEntity.js`.
|
||||
* Server entity scripts are not guaranteed to have access to *other* entities, and should not rely on it.
|
||||
* You should not rely on using `Entities.getEntityProperties` to access the properties of entities
|
||||
other than the entity that the server entity script is running on. This also applies to other
|
||||
functions like `Entities.findEntities`. This means that something like the `ShortGameManager` (described below)
|
||||
will not know the entity IDs of entities like the start button or scoreboard text entities, so it
|
||||
has to find ways to work around that limitation.
|
||||
* You can, however, use `Entities.editEntity` to edit other entities.
|
||||
* *NOTE*: It is likely that this will change in the future, and server entity scripts will be able to
|
||||
query the existence of other entities in some way. This will obviate the need for some of the workarounds
|
||||
used in shortbow.
|
||||
* The Entity Script Server (where server entity scripts) does not run a physics simulation
|
||||
* Server entity scripts do not generate collision events like would be used with
|
||||
`Script.addEventHandler(entityID, "collisionWithEntity", collideHandler)`.
|
||||
* High Fidelity distributes its physics simulation to clients, so collisions need to be handled
|
||||
there. In the case of enemies in shortbow, for instance, the collisions are handled in the
|
||||
client entity script `enemeyClientEntity.js`.
|
||||
* If no client is present to run the physics simulation, entities will not physically interact with other
|
||||
entities.
|
||||
* But, entities will still apply their basic physical properties.
|
||||
|
||||
## Shortbow Game Manager
|
||||
|
||||
This section describes both `shortbowServerEntity.js` and `shortbowGameManager.js`. The `shortbowServerEntity.js` script
|
||||
exists on the main arena model entity, and is the parent of all of the other parts of the game, other than the
|
||||
enemies and bows that are spawned during gameplay.
|
||||
|
||||
The `shortbowServerEntity.js` script is a server entity script that runs the shortbow game. The actual logic for
|
||||
the game is stored inside `shortbowGameManager.js`, in the `ShortbowGameManager` prototype.
|
||||
|
||||
## Enemy Scripts
|
||||
|
||||
These scripts exist on each enemy that is spawned.
|
||||
|
||||
### enemyClientEntity.js
|
||||
|
||||
This script handles collision events on the client. There are two collisions events that it is interested in:
|
||||
|
||||
1. Collisions with arrows
|
||||
1. Arrow entities have "projectile" in their name
|
||||
1. Any other entity with "projectile" in its name could be used to destroy the enemy
|
||||
1. Collisions with the gate that the enemies roll towards
|
||||
1. The gate entity has "GateCollider" in its name
|
||||
|
||||
### enemyServerEntity.js
|
||||
|
||||
This script acts as a fail-safe to work around some of the limitations that are mentioned under [Notes](#notes).
|
||||
In particular, if no client is running the physics simulation of an enemy entity, it may fall through the floor
|
||||
of the arena or never have a collision event generated against the gate. A server entity script also
|
||||
cannot access the properties of another entity, so it can't know if the entity has moved far away from
|
||||
the arena.
|
||||
|
||||
To handle this, this script is used to periodically send heartbeats to the [ShortbowGameManager](#shortbow-game-manager)
|
||||
that runs the game. The heartbeats include the position of the enemy. If the script that received the
|
||||
heartbeats hasn't heard from `enemyServerEntity.js` in too long, or the entity has moved too far away, it
|
||||
will remove it from it's local list of enemies that still need to be destroyed. This ensure that the game
|
||||
doesn't get into a "hung" state where it's waiting for an enemy that will never escape through the gate or be
|
||||
destroyed.
|
||||
|
||||
## Start Button
|
||||
|
||||
These scripts exist on the start button.
|
||||
|
||||
### startGameButtonClientEntity.js
|
||||
|
||||
This script sends a message to the [ShortbowGameManager](#shortbow-game-manager) to request that the game be started.
|
||||
|
||||
### startGameButtonServerEntity.js
|
||||
|
||||
When the shortbow game starts the start button is hidden, and when the shortbow game ends it is shown again.
|
||||
As described in the [Notes](#notes), server entity scripts cannot access other entities, including their IDs.
|
||||
Because of this, the [ShortbowGameManager](#shortbow-game-manager) has no way of knowing the id of the start button,
|
||||
and thus being able to use `Entities.editEntity` to set its `visible` property to `true` or `false`. One way around
|
||||
this, and is what is used here, is to use `Messages` on a common channel to send messages to a server entity script
|
||||
on the start button to request that it be shown or hidden.
|
||||
|
||||
## Display (Scoreboard)
|
||||
|
||||
This script exists on each text entity that scoreboard is composed of. The scoreboard area is composed of a text entity for each of the following: Score, High Score, Wave, Lives.
|
||||
|
||||
### displayServerEntity.js
|
||||
|
||||
The same problem that exists for [startGameButtonServerEntity.js](#startgamebuttonserverentityjs) exists for
|
||||
the text entities on the scoreboard. This script works around the problem in the same way, but instead of
|
||||
receiving a message that tells it whether to hide or show itself, it receives a message that contains the
|
||||
text to update the text entity with. For intance, the "lives" display entity will receive a message each
|
||||
time a life is lost with the current number of lives.
|
||||
|
||||
## How is the "common channel" determined that is used in some of the client and server entity scripts?
|
||||
|
||||
Imagine that there are two instances of shortbow next to each. If the channel being used is always the same,
|
||||
and not differentiated in some way between the two instances, a "start game" message sent from [startGameButtonClientEntity.js](#startgamebuttoncliententityjs)
|
||||
on one game will be received and handled by both games, causing both of them to start. We need a way to create
|
||||
a channel that is unique to the scripts that are running for a particular instance of shortbow.
|
||||
|
||||
All of the entities in shortbow, besides the enemy entities, are children of the main arena entity that
|
||||
runs the game. All of the scripts on these entities can access their parentID, so they can use
|
||||
a prefix plus the parentID to generate a unique channel for that instance of shortbow.
|
||||
|
BIN
unpublishedScripts/marketplace/shortbow/bow/Arrow_impact1.L.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/bow/Arrow_impact1.L.wav
Normal file
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/bow/Bow_draw.1.L.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/bow/Bow_draw.1.L.wav
Normal file
Binary file not shown.
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/bow/arrow-sparkle.png
Normal file
BIN
unpublishedScripts/marketplace/shortbow/bow/arrow-sparkle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
BIN
unpublishedScripts/marketplace/shortbow/bow/arrow.fbx
Normal file
BIN
unpublishedScripts/marketplace/shortbow/bow/arrow.fbx
Normal file
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/bow/bow-deadly.fbx
Normal file
BIN
unpublishedScripts/marketplace/shortbow/bow/bow-deadly.fbx
Normal file
Binary file not shown.
671
unpublishedScripts/marketplace/shortbow/bow/bow.js
Normal file
671
unpublishedScripts/marketplace/shortbow/bow/bow.js
Normal file
|
@ -0,0 +1,671 @@
|
|||
//
|
||||
// Created by Seth Alves on 2016-9-7
|
||||
// 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
|
||||
/* global MyAvatar, Vec3, Controller, Quat */
|
||||
|
||||
var GRAB_COMMUNICATIONS_SETTING = "io.highfidelity.isFarGrabbing";
|
||||
setGrabCommunications = function setFarGrabCommunications(on) {
|
||||
Settings.setValue(GRAB_COMMUNICATIONS_SETTING, on ? "on" : "");
|
||||
}
|
||||
getGrabCommunications = function getFarGrabCommunications() {
|
||||
return !!Settings.getValue(GRAB_COMMUNICATIONS_SETTING, "");
|
||||
}
|
||||
|
||||
// this offset needs to match the one in libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp:378
|
||||
var GRAB_POINT_SPHERE_OFFSET = { x: 0.04, y: 0.13, z: 0.039 }; // x = upward, y = forward, z = lateral
|
||||
|
||||
getGrabPointSphereOffset = function(handController) {
|
||||
if (handController === Controller.Standard.RightHand) {
|
||||
return GRAB_POINT_SPHERE_OFFSET;
|
||||
}
|
||||
return {
|
||||
x: GRAB_POINT_SPHERE_OFFSET.x * -1,
|
||||
y: GRAB_POINT_SPHERE_OFFSET.y,
|
||||
z: GRAB_POINT_SPHERE_OFFSET.z
|
||||
};
|
||||
};
|
||||
|
||||
// controllerWorldLocation is where the controller would be, in-world, with an added offset
|
||||
getControllerWorldLocation = function (handController, doOffset) {
|
||||
var orientation;
|
||||
var position;
|
||||
var pose = Controller.getPoseValue(handController);
|
||||
var valid = pose.valid;
|
||||
var controllerJointIndex;
|
||||
if (pose.valid) {
|
||||
if (handController === Controller.Standard.RightHand) {
|
||||
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_RIGHTHAND");
|
||||
} else {
|
||||
controllerJointIndex = MyAvatar.getJointIndex("_CAMERA_RELATIVE_CONTROLLER_LEFTHAND");
|
||||
}
|
||||
orientation = Quat.multiply(MyAvatar.orientation, MyAvatar.getAbsoluteJointRotationInObjectFrame(controllerJointIndex));
|
||||
position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, MyAvatar.getAbsoluteJointTranslationInObjectFrame(controllerJointIndex)));
|
||||
|
||||
// add to the real position so the grab-point is out in front of the hand, a bit
|
||||
if (doOffset) {
|
||||
var offset = getGrabPointSphereOffset(handController);
|
||||
position = Vec3.sum(position, Vec3.multiplyQbyV(orientation, offset));
|
||||
}
|
||||
|
||||
} else if (!HMD.isHandControllerAvailable()) {
|
||||
// NOTE: keep this offset in sync with scripts/system/controllers/handControllerPointer.js:493
|
||||
var VERTICAL_HEAD_LASER_OFFSET = 0.1;
|
||||
position = Vec3.sum(Camera.position, Vec3.multiplyQbyV(Camera.orientation, {x: 0, y: VERTICAL_HEAD_LASER_OFFSET, z: 0}));
|
||||
orientation = Quat.multiply(Camera.orientation, Quat.angleAxis(-90, { x: 1, y: 0, z: 0 }));
|
||||
valid = true;
|
||||
}
|
||||
|
||||
return {position: position,
|
||||
translation: position,
|
||||
orientation: orientation,
|
||||
rotation: orientation,
|
||||
valid: valid};
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// bow.js
|
||||
//
|
||||
// This script attaches to a bow that you can pick up with a hand controller.
|
||||
// Created by James B. Pollack @imgntn on 10/19/2015
|
||||
// Copyright 2015 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 Script, Controller, SoundCache, Entities, getEntityCustomData, setEntityCustomData, MyAvatar, Vec3, Quat, Messages */
|
||||
|
||||
|
||||
function getControllerLocation(controllerHand) {
|
||||
var standardControllerValue =
|
||||
(controllerHand === "right") ? Controller.Standard.RightHand : Controller.Standard.LeftHand;
|
||||
return getControllerWorldLocation(standardControllerValue, true);
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
||||
Script.include("/~/system/libraries/utils.js");
|
||||
|
||||
const NULL_UUID = "{00000000-0000-0000-0000-000000000000}";
|
||||
|
||||
const NOTCH_ARROW_SOUND_URL = Script.resolvePath('notch.wav');
|
||||
const SHOOT_ARROW_SOUND_URL = Script.resolvePath('String_release2.L.wav');
|
||||
const STRING_PULL_SOUND_URL = Script.resolvePath('Bow_draw.1.L.wav');
|
||||
const ARROW_HIT_SOUND_URL = Script.resolvePath('Arrow_impact1.L.wav');
|
||||
|
||||
const ARROW_MODEL_URL = Script.resolvePath('arrow.fbx');
|
||||
const ARROW_DIMENSIONS = {
|
||||
x: 0.20,
|
||||
y: 0.19,
|
||||
z: 0.93
|
||||
};
|
||||
|
||||
const MIN_ARROW_SPEED = 3.0;
|
||||
const MAX_ARROW_SPEED = 30.0;
|
||||
|
||||
const ARROW_TIP_OFFSET = 0.47;
|
||||
const ARROW_GRAVITY = {
|
||||
x: 0,
|
||||
y: -9.8,
|
||||
z: 0
|
||||
};
|
||||
|
||||
|
||||
const ARROW_LIFETIME = 15; // seconds
|
||||
const ARROW_PARTICLE_LIFESPAN = 2; // seconds
|
||||
|
||||
const TOP_NOTCH_OFFSET = 0.6;
|
||||
const BOTTOM_NOTCH_OFFSET = 0.6;
|
||||
|
||||
const LINE_DIMENSIONS = {
|
||||
x: 5.0,
|
||||
y: 5.0,
|
||||
z: 5.0
|
||||
};
|
||||
|
||||
const DRAW_STRING_THRESHOLD = 0.80;
|
||||
const DRAW_STRING_PULL_DELTA_HAPTIC_PULSE = 0.09;
|
||||
const DRAW_STRING_MAX_DRAW = 0.7;
|
||||
|
||||
|
||||
const MIN_ARROW_DISTANCE_FROM_BOW_REST = 0.2;
|
||||
const MAX_ARROW_DISTANCE_FROM_BOW_REST = ARROW_DIMENSIONS.z - 0.2;
|
||||
const MIN_HAND_DISTANCE_FROM_BOW_TO_KNOCK_ARROW = 0.25;
|
||||
const MIN_ARROW_DISTANCE_FROM_BOW_REST_TO_SHOOT = 0.30;
|
||||
|
||||
const NOTCH_OFFSET_FORWARD = 0.08;
|
||||
const NOTCH_OFFSET_UP = 0.035;
|
||||
|
||||
const SHOT_SCALE = {
|
||||
min1: 0.0,
|
||||
max1: 0.6,
|
||||
min2: 1.0,
|
||||
max2: 15.0
|
||||
};
|
||||
|
||||
const USE_DEBOUNCE = false;
|
||||
|
||||
const TRIGGER_CONTROLS = [
|
||||
Controller.Standard.LT,
|
||||
Controller.Standard.RT,
|
||||
];
|
||||
|
||||
function interval() {
|
||||
var lastTime = new Date().getTime();
|
||||
|
||||
return function getInterval() {
|
||||
var newTime = new Date().getTime();
|
||||
var delta = newTime - lastTime;
|
||||
lastTime = newTime;
|
||||
return delta;
|
||||
};
|
||||
}
|
||||
|
||||
var checkInterval = interval();
|
||||
|
||||
var _this;
|
||||
|
||||
function Bow() {
|
||||
_this = this;
|
||||
return;
|
||||
}
|
||||
|
||||
const STRING_NAME = 'Hifi-Bow-String';
|
||||
const ARROW_NAME = 'Hifi-Arrow-projectile';
|
||||
|
||||
const STATE_IDLE = 0;
|
||||
const STATE_ARROW_GRABBED = 1;
|
||||
|
||||
Bow.prototype = {
|
||||
topString: null,
|
||||
aiming: false,
|
||||
arrowTipPosition: null,
|
||||
preNotchString: null,
|
||||
stringID: null,
|
||||
arrow: null,
|
||||
stringData: {
|
||||
currentColor: {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255
|
||||
}
|
||||
},
|
||||
|
||||
state: STATE_IDLE,
|
||||
sinceLastUpdate: 0,
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.stringPullSound = SoundCache.getSound(STRING_PULL_SOUND_URL);
|
||||
this.shootArrowSound = SoundCache.getSound(SHOOT_ARROW_SOUND_URL);
|
||||
this.arrowHitSound = SoundCache.getSound(ARROW_HIT_SOUND_URL);
|
||||
this.arrowNotchSound = SoundCache.getSound(NOTCH_ARROW_SOUND_URL);
|
||||
var userData = Entities.getEntityProperties(this.entityID, ["userData"]).userData;
|
||||
print(userData);
|
||||
this.userData = JSON.parse(userData);
|
||||
this.stringID = null;
|
||||
},
|
||||
|
||||
unload: function() {
|
||||
Messages.sendLocalMessage('Hifi-Hand-Disabler', "none");
|
||||
Entities.deleteEntity(this.arrow);
|
||||
},
|
||||
|
||||
startEquip: function(entityID, args) {
|
||||
this.hand = args[0];
|
||||
this.bowHand = args[0];
|
||||
this.stringHand = this.bowHand === "right" ? "left" : "right";
|
||||
|
||||
Entities.editEntity(_this.entityID, {
|
||||
collidesWith: "",
|
||||
});
|
||||
|
||||
var data = getEntityCustomData('grabbableKey', this.entityID, {});
|
||||
data.grabbable = false;
|
||||
setEntityCustomData('grabbableKey', this.entityID, data);
|
||||
|
||||
this.initString();
|
||||
|
||||
var self = this;
|
||||
this.updateIntervalID = Script.setInterval(function() { self.update(); }, 11);
|
||||
},
|
||||
|
||||
getStringHandPosition: function() {
|
||||
return getControllerLocation(this.stringHand).position;
|
||||
},
|
||||
|
||||
releaseEquip: function(entityID, args) {
|
||||
Script.clearInterval(this.updateIntervalID);
|
||||
this.updateIntervalID = null;
|
||||
|
||||
Messages.sendLocalMessage('Hifi-Hand-Disabler', "none");
|
||||
|
||||
var data = getEntityCustomData('grabbableKey', this.entityID, {});
|
||||
data.grabbable = true;
|
||||
setEntityCustomData('grabbableKey', this.entityID, data);
|
||||
Entities.deleteEntity(this.arrow);
|
||||
this.resetStringToIdlePosition();
|
||||
this.destroyArrow();
|
||||
Entities.editEntity(_this.entityID, {
|
||||
collidesWith: "static,dynamic,kinematic,otherAvatar,myAvatar"
|
||||
});
|
||||
},
|
||||
|
||||
update: function(entityID) {
|
||||
var self = this;
|
||||
self.deltaTime = checkInterval();
|
||||
//debounce during debugging -- maybe we're updating too fast?
|
||||
if (USE_DEBOUNCE === true) {
|
||||
self.sinceLastUpdate = self.sinceLastUpdate + self.deltaTime;
|
||||
|
||||
if (self.sinceLastUpdate > 60) {
|
||||
self.sinceLastUpdate = 0;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//invert the hands because our string will be held with the opposite hand of the first one we pick up the bow with
|
||||
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[(this.hand === 'left') ? 1 : 0]);
|
||||
|
||||
this.bowProperties = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
|
||||
var notchPosition = this.getNotchPosition(this.bowProperties);
|
||||
var stringHandPosition = this.getStringHandPosition();
|
||||
var handToNotch = Vec3.subtract(notchPosition, stringHandPosition);
|
||||
var pullBackDistance = Vec3.length(handToNotch);
|
||||
|
||||
if (this.state === STATE_IDLE) {
|
||||
this.pullBackDistance = 0;
|
||||
|
||||
this.resetStringToIdlePosition();
|
||||
if (this.triggerValue >= DRAW_STRING_THRESHOLD && pullBackDistance < MIN_HAND_DISTANCE_FROM_BOW_TO_KNOCK_ARROW && !this.backHandBusy) {
|
||||
//the first time aiming the arrow
|
||||
var handToDisable = (this.hand === 'right' ? 'left' : 'right');
|
||||
this.state = STATE_ARROW_GRABBED;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.state === STATE_ARROW_GRABBED) {
|
||||
if (!this.arrow) {
|
||||
var handToDisable = (this.hand === 'right' ? 'left' : 'right');
|
||||
Messages.sendLocalMessage('Hifi-Hand-Disabler', handToDisable);
|
||||
this.playArrowNotchSound();
|
||||
this.arrow = this.createArrow();
|
||||
this.playStringPullSound();
|
||||
}
|
||||
|
||||
if (this.triggerValue < DRAW_STRING_THRESHOLD) {
|
||||
if (pullBackDistance >= (MIN_ARROW_DISTANCE_FROM_BOW_REST_TO_SHOOT)) {
|
||||
// The arrow has been pulled far enough back that we can release it
|
||||
Messages.sendLocalMessage('Hifi-Hand-Disabler', "none");
|
||||
this.updateArrowPositionInNotch(true, true);
|
||||
this.arrow = null;
|
||||
this.state = STATE_IDLE;
|
||||
this.resetStringToIdlePosition();
|
||||
} else {
|
||||
// The arrow has not been pulled far enough back so we just remove the arrow
|
||||
Messages.sendLocalMessage('Hifi-Hand-Disabler', "none");
|
||||
Entities.deleteEntity(this.arrow);
|
||||
this.arrow = null;
|
||||
this.state = STATE_IDLE;
|
||||
this.resetStringToIdlePosition();
|
||||
}
|
||||
} else {
|
||||
this.updateArrowPositionInNotch(false, true);
|
||||
this.updateString();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
destroyArrow: function() {
|
||||
var children = Entities.getChildrenIDs(this.entityID);
|
||||
children.forEach(function(childID) {
|
||||
var childName = Entities.getEntityProperties(childID, ["name"]).name;
|
||||
if (childName == ARROW_NAME) {
|
||||
Entities.deleteEntity(childID);
|
||||
// Continue iterating through children in case we've ended up in
|
||||
// a bad state where there are multiple arrows.
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
createArrow: function() {
|
||||
this.playArrowNotchSound();
|
||||
|
||||
var arrow = Entities.addEntity({
|
||||
name: ARROW_NAME,
|
||||
type: 'Model',
|
||||
modelURL: ARROW_MODEL_URL,
|
||||
shapeType: 'simple-compound',
|
||||
dimensions: ARROW_DIMENSIONS,
|
||||
position: this.bowProperties.position,
|
||||
parentID: this.entityID,
|
||||
dynamic: false,
|
||||
collisionless: true,
|
||||
collisionSoundURL: ARROW_HIT_SOUND_URL,
|
||||
damping: 0.01,
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
},
|
||||
creatorSessionUUID: MyAvatar.sessionUUID
|
||||
})
|
||||
});
|
||||
|
||||
var makeArrowStick = function(entityA, entityB, collision) {
|
||||
Entities.editEntity(entityA, {
|
||||
localAngularVelocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
localVelocity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
gravity: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
},
|
||||
parentID: entityB,
|
||||
dynamic: false,
|
||||
collisionless: true,
|
||||
collidesWith: ""
|
||||
});
|
||||
Script.removeEventHandler(arrow, "collisionWithEntity", makeArrowStick);
|
||||
};
|
||||
|
||||
Script.addEventHandler(arrow, "collisionWithEntity", makeArrowStick);
|
||||
|
||||
return arrow;
|
||||
},
|
||||
|
||||
initString: function() {
|
||||
// Check for existence of string
|
||||
var children = Entities.getChildrenIDs(this.entityID);
|
||||
children.forEach(function(childID) {
|
||||
var childName = Entities.getEntityProperties(childID, ["name"]).name;
|
||||
if (childName == STRING_NAME) {
|
||||
this.stringID = childID;
|
||||
}
|
||||
});
|
||||
|
||||
// If thie string wasn't found, create it
|
||||
if (this.stringID === null) {
|
||||
this.stringID = Entities.addEntity({
|
||||
collisionless: true,
|
||||
dimensions: { "x": 5, "y": 5, "z": 5 },
|
||||
ignoreForCollisions: 1,
|
||||
linePoints: [ { "x": 0, "y": 0, "z": 0 }, { "x": 0, "y": -1.2, "z": 0 } ],
|
||||
lineWidth: 5,
|
||||
color: { red: 153, green: 102, blue: 51 },
|
||||
name: STRING_NAME,
|
||||
parentID: this.entityID,
|
||||
localPosition: { "x": 0, "y": 0.6, "z": 0.1 },
|
||||
localRotation: { "w": 1, "x": 0, "y": 0, "z": 0 },
|
||||
type: 'Line',
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
this.resetStringToIdlePosition();
|
||||
},
|
||||
|
||||
// This resets the string to a straight line
|
||||
resetStringToIdlePosition: function() {
|
||||
Entities.editEntity(this.stringID, {
|
||||
linePoints: [ { "x": 0, "y": 0, "z": 0 }, { "x": 0, "y": -1.2, "z": 0 } ],
|
||||
lineWidth: 5,
|
||||
localPosition: { "x": 0, "y": 0.6, "z": 0.1 },
|
||||
localRotation: { "w": 1, "x": 0, "y": 0, "z": 0 },
|
||||
});
|
||||
},
|
||||
|
||||
updateString: function() {
|
||||
var upVector = Quat.getUp(this.bowProperties.rotation);
|
||||
var upOffset = Vec3.multiply(upVector, TOP_NOTCH_OFFSET);
|
||||
var downVector = Vec3.multiply(-1, Quat.getUp(this.bowProperties.rotation));
|
||||
var downOffset = Vec3.multiply(downVector, BOTTOM_NOTCH_OFFSET);
|
||||
var backOffset = Vec3.multiply(-0.1, Quat.getFront(this.bowProperties.rotation));
|
||||
|
||||
var topStringPosition = Vec3.sum(this.bowProperties.position, upOffset);
|
||||
this.topStringPosition = Vec3.sum(topStringPosition, backOffset);
|
||||
var bottomStringPosition = Vec3.sum(this.bowProperties.position, downOffset);
|
||||
this.bottomStringPosition = Vec3.sum(bottomStringPosition, backOffset);
|
||||
|
||||
var stringProps = Entities.getEntityProperties(this.stringID, ['position', 'rotation']);
|
||||
var handPositionLocal = Vec3.subtract(this.arrowRearPosition, stringProps.position);
|
||||
handPositionLocal = Vec3.multiplyQbyV(Quat.inverse(stringProps.rotation), handPositionLocal);
|
||||
|
||||
var linePoints = [
|
||||
{ x: 0, y: 0, z: 0 },
|
||||
handPositionLocal,
|
||||
{ x: 0, y: -1.2, z: 0 },
|
||||
];
|
||||
|
||||
Entities.editEntity(this.stringID, {
|
||||
linePoints: linePoints,
|
||||
});
|
||||
},
|
||||
|
||||
getNotchPosition: function(bowProperties) {
|
||||
var frontVector = Quat.getFront(bowProperties.rotation);
|
||||
var notchVectorForward = Vec3.multiply(frontVector, NOTCH_OFFSET_FORWARD);
|
||||
var upVector = Quat.getUp(bowProperties.rotation);
|
||||
var notchVectorUp = Vec3.multiply(upVector, NOTCH_OFFSET_UP);
|
||||
var notchPosition = Vec3.sum(bowProperties.position, notchVectorForward);
|
||||
notchPosition = Vec3.sum(notchPosition, notchVectorUp);
|
||||
return notchPosition;
|
||||
},
|
||||
|
||||
updateArrowPositionInNotch: function(shouldReleaseArrow, doHapticPulses) {
|
||||
//set the notch that the arrow should go through
|
||||
var notchPosition = this.getNotchPosition(this.bowProperties);
|
||||
//set the arrow rotation to be between the notch and other hand
|
||||
var stringHandPosition = this.getStringHandPosition();
|
||||
var handToNotch = Vec3.subtract(notchPosition, stringHandPosition);
|
||||
var arrowRotation = Quat.rotationBetween(Vec3.FRONT, handToNotch);
|
||||
|
||||
var backHand = this.hand === 'left' ? 1 : 0;
|
||||
var pullBackDistance = Vec3.length(handToNotch);
|
||||
// pulse as arrow is drawn
|
||||
if (doHapticPulses &&
|
||||
Math.abs(pullBackDistance - this.pullBackDistance) > DRAW_STRING_PULL_DELTA_HAPTIC_PULSE) {
|
||||
Controller.triggerHapticPulse(1, 20, backHand);
|
||||
this.pullBackDistance = pullBackDistance;
|
||||
}
|
||||
|
||||
if (pullBackDistance > DRAW_STRING_MAX_DRAW) {
|
||||
pullBackDistance = DRAW_STRING_MAX_DRAW;
|
||||
}
|
||||
|
||||
var handToNotchDistance = Vec3.length(handToNotch);
|
||||
var stringToNotchDistance = Math.max(MIN_ARROW_DISTANCE_FROM_BOW_REST, Math.min(MAX_ARROW_DISTANCE_FROM_BOW_REST, handToNotchDistance));
|
||||
var halfArrowVec = Vec3.multiply(Vec3.normalize(handToNotch), ARROW_DIMENSIONS.z / 2.0);
|
||||
var offset = Vec3.subtract(notchPosition, Vec3.multiply(Vec3.normalize(handToNotch), stringToNotchDistance - ARROW_DIMENSIONS.z / 2.0));
|
||||
|
||||
var arrowPosition = offset;
|
||||
|
||||
// Set arrow rear position
|
||||
var frontVector = Quat.getFront(arrowRotation);
|
||||
var frontOffset = Vec3.multiply(frontVector, -ARROW_TIP_OFFSET);
|
||||
var arrorRearPosition = Vec3.sum(arrowPosition, frontOffset);
|
||||
this.arrowRearPosition = arrorRearPosition;
|
||||
|
||||
//if we're not shooting, we're updating the arrow's orientation
|
||||
if (shouldReleaseArrow !== true) {
|
||||
Entities.editEntity(this.arrow, {
|
||||
position: arrowPosition,
|
||||
rotation: arrowRotation
|
||||
});
|
||||
} else {
|
||||
//shoot the arrow
|
||||
var arrowAge = Entities.getEntityProperties(this.arrow, ["age"]).age;
|
||||
|
||||
//scale the shot strength by the distance you've pulled the arrow back and set its release velocity to be
|
||||
// in the direction of the v
|
||||
var arrowForce = this.scaleArrowShotStrength(stringToNotchDistance);
|
||||
var handToNotchNorm = Vec3.normalize(handToNotch);
|
||||
|
||||
var releaseVelocity = Vec3.multiply(handToNotchNorm, arrowForce);
|
||||
|
||||
//make the arrow physical, give it gravity, a lifetime, and set our velocity
|
||||
var arrowProperties = {
|
||||
dynamic: true,
|
||||
collisionless: false,
|
||||
collidesWith: "static,dynamic,otherAvatar", // workaround: not with kinematic --> no collision with bow
|
||||
velocity: releaseVelocity,
|
||||
parentID: NULL_UUID,
|
||||
gravity: ARROW_GRAVITY,
|
||||
lifetime: arrowAge + ARROW_LIFETIME,
|
||||
};
|
||||
|
||||
// add a particle effect to make the arrow easier to see as it flies
|
||||
var arrowParticleProperties = {
|
||||
accelerationSpread: { x: 0, y: 0, z: 0 },
|
||||
alpha: 1,
|
||||
alphaFinish: 0,
|
||||
alphaSpread: 0,
|
||||
alphaStart: 0.3,
|
||||
azimuthFinish: 3.1,
|
||||
azimuthStart: -3.14159,
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
colorFinish: { red: 255, green: 255, blue: 255 },
|
||||
colorSpread: { red: 0, green: 0, blue: 0 },
|
||||
colorStart: { red: 255, green: 255, blue: 255 },
|
||||
emitAcceleration: { x: 0, y: 0, z: 0 },
|
||||
emitDimensions: { x: 0, y: 0, z: 0 },
|
||||
emitOrientation: { x: -0.7, y: 0.0, z: 0.0, w: 0.7 },
|
||||
emitRate: 0.01,
|
||||
emitSpeed: 0,
|
||||
emitterShouldTrail: 0,
|
||||
isEmitting: 1,
|
||||
lifespan: ARROW_PARTICLE_LIFESPAN,
|
||||
lifetime: ARROW_PARTICLE_LIFESPAN + 1,
|
||||
maxParticles: 1000,
|
||||
name: 'arrow-particles',
|
||||
parentID: this.arrow,
|
||||
particleRadius: 0.132,
|
||||
polarFinish: 0,
|
||||
polarStart: 0,
|
||||
radiusFinish: 0.35,
|
||||
radiusSpread: 0,
|
||||
radiusStart: 0.132,
|
||||
speedSpread: 0,
|
||||
textures: Script.resolvePath('arrow-sparkle.png'),
|
||||
type: 'ParticleEffect'
|
||||
};
|
||||
|
||||
Entities.addEntity(arrowParticleProperties);
|
||||
|
||||
// actually shoot the arrow
|
||||
Entities.editEntity(this.arrow, arrowProperties);
|
||||
|
||||
// play the sound of a shooting arrow
|
||||
this.playShootArrowSound();
|
||||
|
||||
Entities.addAction("travel-oriented", this.arrow, {
|
||||
forward: { x: 0, y: 0, z: -1 },
|
||||
angularTimeScale: 0.1,
|
||||
tag: "arrow from hifi-bow",
|
||||
ttl: ARROW_LIFETIME
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
scaleArrowShotStrength: function(value) {
|
||||
var percentage = (value - MIN_ARROW_DISTANCE_FROM_BOW_REST)
|
||||
/ (MAX_ARROW_DISTANCE_FROM_BOW_REST - MIN_ARROW_DISTANCE_FROM_BOW_REST);
|
||||
return MIN_ARROW_SPEED + (percentage * (MAX_ARROW_SPEED - MIN_ARROW_SPEED)) ;
|
||||
},
|
||||
|
||||
playStringPullSound: function() {
|
||||
var audioProperties = {
|
||||
volume: 0.10,
|
||||
position: this.bowProperties.position
|
||||
};
|
||||
this.stringPullInjector = Audio.playSound(this.stringPullSound, audioProperties);
|
||||
},
|
||||
|
||||
playShootArrowSound: function(sound) {
|
||||
var audioProperties = {
|
||||
volume: 0.15,
|
||||
position: this.bowProperties.position
|
||||
};
|
||||
Audio.playSound(this.shootArrowSound, audioProperties);
|
||||
},
|
||||
|
||||
playArrowNotchSound: function() {
|
||||
var audioProperties = {
|
||||
volume: 0.15,
|
||||
position: this.bowProperties.position
|
||||
};
|
||||
Audio.playSound(this.arrowNotchSound, audioProperties);
|
||||
},
|
||||
|
||||
changeStringPullSoundVolume: function(pullBackDistance) {
|
||||
var audioProperties = {
|
||||
volume: this.scaleSoundVolume(pullBackDistance),
|
||||
position: this.bowProperties.position
|
||||
};
|
||||
|
||||
this.stringPullInjector.options = audioProperties;
|
||||
},
|
||||
|
||||
scaleSoundVolume: function(value) {
|
||||
var min1 = SHOT_SCALE.min1;
|
||||
var max1 = SHOT_SCALE.max1;
|
||||
var min2 = 0;
|
||||
var max2 = 0.2;
|
||||
return min2 + (max2 - min2) * ((value - min1) / (max1 - min1));
|
||||
},
|
||||
|
||||
handleMessages: function(channel, message, sender) {
|
||||
if (sender !== MyAvatar.sessionUUID) {
|
||||
return;
|
||||
}
|
||||
if (channel !== 'Hifi-Object-Manipulation') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
var data = JSON.parse(message);
|
||||
var action = data.action;
|
||||
var hand = data.joint;
|
||||
var isBackHand = ((_this.hand == "left" && hand == "RightHand") ||
|
||||
(_this.hand == "right" && hand == "LeftHand"));
|
||||
if ((action == "equip" || action == "grab") && isBackHand) {
|
||||
_this.backHandBusy = true;
|
||||
}
|
||||
if (action == "release" && isBackHand) {
|
||||
_this.backHandBusy = false;
|
||||
}
|
||||
} catch (e) {
|
||||
print("WARNING: bow.js -- error parsing Hifi-Object-Manipulation message: " + message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var bow = new Bow();
|
||||
|
||||
Messages.subscribe('Hifi-Object-Manipulation');
|
||||
Messages.messageReceived.connect(bow.handleMessages);
|
||||
|
||||
return bow;
|
||||
});
|
44
unpublishedScripts/marketplace/shortbow/bow/bow.json
Normal file
44
unpublishedScripts/marketplace/shortbow/bow/bow.json
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"Entities": [
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collisionsWillMove": 1,
|
||||
"compoundShapeURL": "http://mpassets.highfidelity.com/a9221d01-95eb-4b2e-85e5-d9e0970a7f51-v1/bow_collision_hull.obj",
|
||||
"created": "2017-02-14T18:54:38Z",
|
||||
"dimensions": {
|
||||
"x": 0.039999999105930328,
|
||||
"y": 1.2999999523162842,
|
||||
"z": 0.20000000298023224
|
||||
},
|
||||
"dynamic": 1,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": -9.8000001907348633,
|
||||
"z": 0
|
||||
},
|
||||
"id": "{73954924-2e18-4787-91a7-092c2afb6242}",
|
||||
"lastEdited": 1487098438422164,
|
||||
"lastEditedBy": "{d2da5e17-9125-414d-ac4e-cd7fba6c22f8}",
|
||||
"modelURL": "http://mpassets.highfidelity.com/a9221d01-95eb-4b2e-85e5-d9e0970a7f51-v1/bow-deadly.fbx",
|
||||
"name": "WG.Hifi-Bow",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"queryAACube": {
|
||||
"scale": 1.3159027099609375,
|
||||
"x": -0.65795135498046875,
|
||||
"y": -0.65795135498046875,
|
||||
"z": -0.65795135498046875
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.9717707633972168,
|
||||
"x": 0.15437555313110352,
|
||||
"y": -0.10472267866134644,
|
||||
"z": -0.14421302080154419
|
||||
},
|
||||
"script": "http://mpassets.highfidelity.com/a9221d01-95eb-4b2e-85e5-d9e0970a7f51-v1/bow.js",
|
||||
"shapeType": "compound",
|
||||
"type": "Model",
|
||||
"userData": "{\"grabbableKey\":{\"grabbable\":true},\"wearable\":{\"joints\":{\"RightHand\":[{\"x\":0.0813,\"y\":0.0452,\"z\":0.0095},{\"x\":-0.3946,\"y\":-0.6604,\"z\":0.4748,\"w\":-0.4275}],\"LeftHand\":[{\"x\":-0.0881,\"y\":0.0259,\"z\":0.0159},{\"x\":0.4427,\"y\":-0.6519,\"z\":0.4592,\"w\":0.4099}]}}}"
|
||||
}
|
||||
],
|
||||
"Version": 67
|
||||
}
|
32
unpublishedScripts/marketplace/shortbow/bow/bow.svo.json
Normal file
32
unpublishedScripts/marketplace/shortbow/bow/bow.svo.json
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"Entities": [ {
|
||||
"collisionsWillMove": 1,
|
||||
"compoundShapeURL": "http://hifi-content.s3.amazonaws.com/caitlyn/production/bow/bow_collision_hull.obj",
|
||||
"created": "2016-09-01T23:57:55Z",
|
||||
"dimensions": {
|
||||
"x": 0.039999999105930328,
|
||||
"y": 1.2999999523162842,
|
||||
"z": 0.20000000298023224
|
||||
},
|
||||
"dynamic": 1,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": -1,
|
||||
"z": 0
|
||||
},
|
||||
"modelURL": "http://hifi-content.s3.amazonaws.com/caitlyn/production/bow/bow-deadly.fbx",
|
||||
"name": "Hifi-Bow",
|
||||
"rotation": {
|
||||
"w": 0.9718012809753418,
|
||||
"x": 0.15440607070922852,
|
||||
"y": -0.10469216108322144,
|
||||
"z": -0.14418250322341919
|
||||
},
|
||||
"script": "http://hifi-content.s3.amazonaws.com/caitlyn/production/bow/bow.js",
|
||||
"shapeType": "compound",
|
||||
"type": "Model",
|
||||
"userData": "{\"grabbableKey\":{\"grabbable\":true},\"wearable\":{\"joints\":{\"RightHand\":[{\"x\":0.0813,\"y\":0.0452,\"z\":0.0095},{\"x\":-0.3946,\"y\":-0.6604,\"z\":0.4748,\"w\":-0.4275}],\"LeftHand\":[{\"x\":-0.0881,\"y\":0.0259,\"z\":0.0159},{\"x\":0.4427,\"y\":-0.6519,\"z\":0.4592,\"w\":0.4099}]}}}"
|
||||
}
|
||||
],
|
||||
"Version": 57
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
v -0.016461 -0.431491 -0.033447
|
||||
v -0.007624 0.437384 -0.046243
|
||||
v 0.011984 -0.424659 -0.03691
|
||||
v 0.015514 0.425913 -0.028648
|
||||
v -0.010788 -0.421429 0.093711
|
||||
v 0.007135 -0.423115 0.098735
|
||||
v -0.010208 0.425558 0.096005
|
||||
v 0.006734 0.43913 0.088902
|
||||
|
||||
f 1 2 3
|
||||
f 3 2 4
|
||||
f 5 6 7
|
||||
f 7 6 8
|
||||
f 1 5 2
|
||||
f 2 5 7
|
||||
f 3 4 6
|
||||
f 6 4 8
|
||||
f 1 3 5
|
||||
f 5 3 6
|
||||
f 2 7 4
|
||||
f 4 7 8
|
BIN
unpublishedScripts/marketplace/shortbow/bow/notch.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/bow/notch.wav
Normal file
Binary file not shown.
67
unpublishedScripts/marketplace/shortbow/bow/spawnBow.js
Normal file
67
unpublishedScripts/marketplace/shortbow/bow/spawnBow.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 1/10/2017
|
||||
// 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
|
||||
//
|
||||
|
||||
|
||||
var leftHandPosition = {
|
||||
"x": 0,
|
||||
"y": 0.0559,
|
||||
"z": 0.0159
|
||||
};
|
||||
var leftHandRotation = Quat.fromPitchYawRollDegrees(90, -90, 0);
|
||||
var rightHandPosition = Vec3.multiplyVbyV(leftHandPosition, { x: -1, y: 0, z: 0 });
|
||||
var rightHandRotation = Quat.fromPitchYawRollDegrees(90, 90, 0);
|
||||
|
||||
var userData = {
|
||||
"grabbableKey": {
|
||||
"grabbable": true
|
||||
},
|
||||
"wearable": {
|
||||
"joints": {
|
||||
"LeftHand": [
|
||||
leftHandPosition,
|
||||
leftHandRotation
|
||||
],
|
||||
"RightHand": [
|
||||
rightHandPosition,
|
||||
rightHandRotation
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var id = Entities.addEntity({
|
||||
"position": MyAvatar.position,
|
||||
"collisionsWillMove": 1,
|
||||
"compoundShapeURL": Script.resolvePath("bow_collision_hull.obj"),
|
||||
"created": "2016-09-01T23:57:55Z",
|
||||
"dimensions": {
|
||||
"x": 0.039999999105930328,
|
||||
"y": 1.2999999523162842,
|
||||
"z": 0.20000000298023224
|
||||
},
|
||||
"dynamic": 1,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": -9.8,
|
||||
"z": 0
|
||||
},
|
||||
"modelURL": Script.resolvePath("bow-deadly.fbx"),
|
||||
"name": "Hifi-Bow",
|
||||
"rotation": {
|
||||
"w": 0.9718012809753418,
|
||||
"x": 0.15440607070922852,
|
||||
"y": -0.10469216108322144,
|
||||
"z": -0.14418250322341919
|
||||
},
|
||||
"script": Script.resolvePath("bow.js"),
|
||||
"shapeType": "compound",
|
||||
"type": "Model",
|
||||
"userData": "{\"grabbableKey\":{\"grabbable\":true},\"wearable\":{\"joints\":{\"RightHand\":[{\"x\":0.0813,\"y\":0.0452,\"z\":0.0095},{\"x\":-0.3946,\"y\":-0.6604,\"z\":0.4748,\"w\":-0.4275}],\"LeftHand\":[{\"x\":-0.0881,\"y\":0.0259,\"z\":0.0159},{\"x\":0.4427,\"y\":-0.6519,\"z\":0.4592,\"w\":0.4099}]}}}",
|
||||
"lifetime": 600
|
||||
});
|
||||
print("Created bow:", id);
|
61
unpublishedScripts/marketplace/shortbow/enemyClientEntity.js
Normal file
61
unpublishedScripts/marketplace/shortbow/enemyClientEntity.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 1/10/2017
|
||||
// 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
|
||||
//
|
||||
|
||||
/* globals utils */
|
||||
|
||||
(function() {
|
||||
Script.include('utils.js');
|
||||
|
||||
function Enemy() {
|
||||
}
|
||||
Enemy.prototype = {
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
|
||||
// To avoid sending extraneous messages and checking entities that we've already
|
||||
// seen, we keep track of which entities we've collided with previously.
|
||||
this.entityIDsThatHaveCollidedWithMe = [];
|
||||
|
||||
Script.addEventHandler(entityID, "collisionWithEntity", this.onCollide.bind(this));
|
||||
|
||||
var userData = Entities.getEntityProperties(this.entityID, 'userData').userData;
|
||||
var data = utils.parseJSON(userData);
|
||||
if (data !== undefined && data.gameChannel !== undefined) {
|
||||
this.gameChannel = data.gameChannel;
|
||||
} else {
|
||||
print("enemyEntity.js | ERROR: userData does not contain a game channel and/or team number");
|
||||
}
|
||||
},
|
||||
onCollide: function(entityA, entityB, collision) {
|
||||
if (this.entityIDsThatHaveCollidedWithMe.indexOf(entityB) > -1) {
|
||||
return;
|
||||
}
|
||||
this.entityIDsThatHaveCollidedWithMe.push(entityB);
|
||||
|
||||
var colliderName = Entities.getEntityProperties(entityB, 'name').name;
|
||||
|
||||
if (colliderName.indexOf("projectile") > -1) {
|
||||
Messages.sendMessage(this.gameChannel, JSON.stringify({
|
||||
type: "enemy-killed",
|
||||
entityID: this.entityID,
|
||||
position: Entities.getEntityProperties(this.entityID, 'position').position
|
||||
}));
|
||||
Entities.deleteEntity(this.entityID);
|
||||
} else if (colliderName.indexOf("GateCollider") > -1) {
|
||||
Messages.sendMessage(this.gameChannel, JSON.stringify({
|
||||
type: "enemy-escaped",
|
||||
entityID: this.entityID,
|
||||
position: Entities.getEntityProperties(this.entityID, 'position').position
|
||||
}));
|
||||
Entities.deleteEntity(this.entityID);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new Enemy();
|
||||
});
|
41
unpublishedScripts/marketplace/shortbow/enemyServerEntity.js
Normal file
41
unpublishedScripts/marketplace/shortbow/enemyServerEntity.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 1/10/2017
|
||||
// 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
|
||||
//
|
||||
|
||||
/* globals utils */
|
||||
|
||||
(function() {
|
||||
Script.include('utils.js');
|
||||
|
||||
function Enemy() {
|
||||
}
|
||||
Enemy.prototype = {
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
var userData = Entities.getEntityProperties(this.entityID, 'userData').userData;
|
||||
var data = utils.parseJSON(userData);
|
||||
if (data !== undefined && data.gameChannel !== undefined) {
|
||||
this.gameChannel = data.gameChannel;
|
||||
} else {
|
||||
print("enemyServerEntity.js | ERROR: userData does not contain a game channel and/or team number");
|
||||
}
|
||||
var self = this;
|
||||
this.heartbeatTimerID = Script.setInterval(function() {
|
||||
Messages.sendMessage(self.gameChannel, JSON.stringify({
|
||||
type: "enemy-heartbeat",
|
||||
entityID: self.entityID,
|
||||
position: Entities.getEntityProperties(self.entityID, 'position').position
|
||||
}));
|
||||
}, 1000);
|
||||
},
|
||||
unload: function() {
|
||||
Script.clearInterval(this.heartbeatTimerID);
|
||||
}
|
||||
};
|
||||
|
||||
return new Enemy();
|
||||
});
|
BIN
unpublishedScripts/marketplace/shortbow/models/Amber.fbx
Normal file
BIN
unpublishedScripts/marketplace/shortbow/models/Amber.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
877
unpublishedScripts/marketplace/shortbow/shortbow.js
Normal file
877
unpublishedScripts/marketplace/shortbow/shortbow.js
Normal file
|
@ -0,0 +1,877 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 1/10/2017
|
||||
// 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
|
||||
//
|
||||
|
||||
/* globals SHORTBOW_ENTITIES:true */
|
||||
|
||||
// This is a copy of the data in shortbow.json, which is an export of the shortbow
|
||||
// scene.
|
||||
//
|
||||
// Because .json can't be Script.include'd directly, the contents are copied over
|
||||
// to here and exposed as a global variable.
|
||||
//
|
||||
|
||||
SHORTBOW_ENTITIES =
|
||||
{
|
||||
"Entities": [
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{02f39515-cab4-41d5-b315-5fb41613f844}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708750446,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -5.1684012413024902,
|
||||
"y": 0.54034698009490967,
|
||||
"z": -11.257695198059082
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": -1.3604279756546021,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"backgroundColor": {
|
||||
"blue": 65,
|
||||
"green": 78,
|
||||
"red": 82
|
||||
},
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 2,
|
||||
"y": 0.69999998807907104,
|
||||
"z": 0.0099999997764825821
|
||||
},
|
||||
"id": "{3eae601e-3c6e-49ab-8f40-dedee32f7573}",
|
||||
"lastEdited": 1487894036038423,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"lineHeight": 0.5,
|
||||
"name": "SB.DisplayScore",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -8.0707607269287109,
|
||||
"y": 1.5265679359436035,
|
||||
"z": -9.5913219451904297
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 2.118985652923584,
|
||||
"x": -5.1109838485717773,
|
||||
"y": -1803.69189453125,
|
||||
"z": -26.774648666381836
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.70708787441253662,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": 0.70708787441253662,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"text": "0",
|
||||
"type": "Text",
|
||||
"userData": "{\"displayType\":\"score\"}"
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.71920669078826904,
|
||||
"y": 3.3160061836242676,
|
||||
"z": 2.2217941284179688
|
||||
},
|
||||
"id": "{04288f77-64df-4323-ac38-9c1960a393a5}",
|
||||
"lastEdited": 1487893058314990,
|
||||
"lastEditedBy": "{fce8028a-4bac-43e8-96ff-4c7286ea4ab3}",
|
||||
"modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-button.fbx",
|
||||
"name": "SB.StartButton",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -9.8358345031738281,
|
||||
"y": 0.45674961805343628,
|
||||
"z": -13.044205665588379
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 4.0558013916015625,
|
||||
"x": -7.844393253326416,
|
||||
"y": -1805.730224609375,
|
||||
"z": -31.195960998535156
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": 1.52587890625e-05,
|
||||
"y": 1.52587890625e-05,
|
||||
"z": 1.52587890625e-05
|
||||
},
|
||||
"script": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/startGameButtonClientEntity.js",
|
||||
"shapeType": "static-mesh",
|
||||
"type": "Model",
|
||||
"userData": "{\"grabbableKey\":{\"wantsTrigger\":true}}"
|
||||
},
|
||||
{
|
||||
"backgroundColor": {
|
||||
"blue": 65,
|
||||
"green": 78,
|
||||
"red": 82
|
||||
},
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 2,
|
||||
"y": 0.69999998807907104,
|
||||
"z": 0.0099999997764825821
|
||||
},
|
||||
"id": "{1196096f-bcc9-4b19-970d-605113474c1b}",
|
||||
"lastEdited": 1487894037900323,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"lineHeight": 0.5,
|
||||
"name": "SB.DisplayHighScore",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -8.0707607269287109,
|
||||
"y": 0.26189804077148438,
|
||||
"z": -9.5913219451904297
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 2.118985652923584,
|
||||
"x": -5.11102294921875,
|
||||
"y": -1804.95654296875,
|
||||
"z": -26.77461051940918
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.70708787441253662,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": 0.70708787441253662,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"text": "0",
|
||||
"type": "Text",
|
||||
"userData": "{\"displayType\":\"highscore\"}"
|
||||
},
|
||||
{
|
||||
"backgroundColor": {
|
||||
"blue": 65,
|
||||
"green": 78,
|
||||
"red": 82
|
||||
},
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 1.4120937585830688,
|
||||
"y": 0.71569448709487915,
|
||||
"z": 0.0099999997764825821
|
||||
},
|
||||
"id": "{293c294d-1df5-461e-82a3-66abee852d44}",
|
||||
"lastEdited": 1487894033695485,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"lineHeight": 0.5,
|
||||
"name": "SB.DisplayWave",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -8.0707607269287109,
|
||||
"y": 1.5265679359436035,
|
||||
"z": -7.2889409065246582
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.5831384658813477,
|
||||
"x": -4.8431310653686523,
|
||||
"y": -1803.4239501953125,
|
||||
"z": -24.204343795776367
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.70708787441253662,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": 0.70708787441253662,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"text": "0",
|
||||
"type": "Text",
|
||||
"userData": "{\"displayType\":\"wave\"}"
|
||||
},
|
||||
{
|
||||
"backgroundColor": {
|
||||
"blue": 65,
|
||||
"green": 78,
|
||||
"red": 82
|
||||
},
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 1.4120937585830688,
|
||||
"y": 0.71569448709487915,
|
||||
"z": 0.0099999997764825821
|
||||
},
|
||||
"id": "{379afa7b-c668-4c4e-b290-e5c37fb3440c}",
|
||||
"lastEdited": 1487893055310428,
|
||||
"lastEditedBy": "{fce8028a-4bac-43e8-96ff-4c7286ea4ab3}",
|
||||
"lineHeight": 0.5,
|
||||
"name": "SB.DisplayLives",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -8.0707607269287109,
|
||||
"y": 0.26189804077148438,
|
||||
"z": -7.2889409065246582
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.5831384658813477,
|
||||
"x": -4.8431692123413086,
|
||||
"y": -1804.6885986328125,
|
||||
"z": -24.204303741455078
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.70708787441253662,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": 0.70708787441253662,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"text": "0",
|
||||
"type": "Text",
|
||||
"userData": "{\"displayType\":\"lives\"}"
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{760e81a1-a804-4f5e-9769-393d021fc8fe}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440234633,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -1.89238440990448,
|
||||
"y": -5.3368110656738281,
|
||||
"z": 11.512755393981934
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 1.9147146940231323,
|
||||
"y": -1809.7066650390625,
|
||||
"z": -4.8219971656799316
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{0a76d0ac-6353-467b-8edc-56417d5a987c}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440235124,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 3.6569130420684814,
|
||||
"y": -5.3365960121154785,
|
||||
"z": 10.01292610168457
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 7.4640579223632812,
|
||||
"y": -1809.7066650390625,
|
||||
"z": -6.3216567039489746
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{f8549c8a-e646-4feb-bbaf-70e7d5be755a}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440235339,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 8.8902750015258789,
|
||||
"y": -5.3364419937133789,
|
||||
"z": 10.195274353027344
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 12.697414398193359,
|
||||
"y": -1809.7066650390625,
|
||||
"z": -6.1391491889953613
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{f3aea4ae-4445-4a2d-8d61-e9fd72f04008}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708751269,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -2.5027251243591309,
|
||||
"y": 0.54042834043502808,
|
||||
"z": -11.257777214050293
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 1.3052481412887573,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{cc1ac907-124b-4372-8c4c-82d175546725}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708751135,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 2.7972855567932129,
|
||||
"y": 0.54059004783630371,
|
||||
"z": -11.257938385009766
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 6.6052589416503906,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{e25ce690-e267-4c51-80a0-f63a72474b8a}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708751527,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 0.17114110291004181,
|
||||
"y": 0.54050993919372559,
|
||||
"z": -11.257858276367188
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 3.979114294052124,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{91ee2285-38f8-4795-b6bc-7abc8dcde07c}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708750806,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 5.4656705856323242,
|
||||
"y": 0.54067152738571167,
|
||||
"z": -11.258020401000977
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 9.2736434936523438,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{d81e5fae-8a8d-4186-bbd2-0c3ae737b0f2}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892552671000,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 9.6099967956542969,
|
||||
"y": 0.64012420177459717,
|
||||
"z": -9.9802846908569336
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 13.417934417724609,
|
||||
"y": -1803.730712890625,
|
||||
"z": -26.314868927001953
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.22495110332965851,
|
||||
"x": -2.9734959753113799e-05,
|
||||
"y": 0.97437006235122681,
|
||||
"z": 2.9735869247815572e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{7056e21e-bce6-4c4b-bbca-36bea1dce303}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708750993,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 8.1799373626708984,
|
||||
"y": 0.54075431823730469,
|
||||
"z": -11.258102416992188
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 11.987911224365234,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{ed073620-e304-4b8e-b12a-5371b595bbf6}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440234415,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -2.3618791103363037,
|
||||
"y": -2.0691573619842529,
|
||||
"z": 11.254574775695801
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 1.4453276395797729,
|
||||
"y": -1806.43896484375,
|
||||
"z": -5.0802912712097168
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{32ed7820-c386-4da1-b676-7e63762861a3}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440234854,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 0.64757472276687622,
|
||||
"y": -2.5217375755310059,
|
||||
"z": 10.08248233795166
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 4.454803466796875,
|
||||
"y": -1806.8917236328125,
|
||||
"z": -6.2522788047790527
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 26.619264602661133,
|
||||
"y": 14.24090576171875,
|
||||
"z": 39.351066589355469
|
||||
},
|
||||
"id": "{d4c8f577-944d-4d50-ac85-e56387c0ef0a}",
|
||||
"lastEdited": 1487892440231278,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.fbx",
|
||||
"name": "SB.Platform",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 0.097909502685070038,
|
||||
"y": -1.0163799524307251,
|
||||
"z": 2.0321114063262939
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 49.597328186035156,
|
||||
"x": -20.681917190551758,
|
||||
"y": -1829.9739990234375,
|
||||
"z": -38.890060424804688
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shapeType": "static-mesh",
|
||||
"type": "Model"
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 23.341892242431641,
|
||||
"y": 12.223045349121094,
|
||||
"z": 32.012016296386719
|
||||
},
|
||||
"friction": 1,
|
||||
"id": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"lastEdited": 1487892440231832,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.fbx",
|
||||
"name": "SB.Scoreboard",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"queryAACube": {
|
||||
"scale": 41.461017608642578,
|
||||
"x": -20.730508804321289,
|
||||
"y": -20.730508804321289,
|
||||
"z": -20.730508804321289
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"serverScripts": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/shortbowServerEntity.js",
|
||||
"shapeType": "static-mesh",
|
||||
"type": "Model"
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 15.710711479187012,
|
||||
"y": 4.7783288955688477,
|
||||
"z": 1.6129581928253174
|
||||
},
|
||||
"id": "{84cdff6e-a68d-4bbf-8660-2d6a8c2f1fd0}",
|
||||
"lastEdited": 1487892440231522,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.GateCollider",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 0.31728419661521912,
|
||||
"y": -4.3002614974975586,
|
||||
"z": -12.531644821166992
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 16.50031852722168,
|
||||
"x": -3.913693904876709,
|
||||
"y": -1816.709716796875,
|
||||
"z": -36.905204772949219
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
}
|
||||
],
|
||||
"Version": 68
|
||||
};
|
||||
|
||||
// Add LocalPosition to entity data if parent properties are available
|
||||
var entities = SHORTBOW_ENTITIES.Entities;
|
||||
var entitiesByID = {};
|
||||
var i, entity;
|
||||
for (i = 0; i < entities.length; ++i) {
|
||||
entity = entities[i];
|
||||
entitiesByID[entity.id] = entity;
|
||||
}
|
||||
for (i = 0; i < entities.length; ++i) {
|
||||
entity = entities[i];
|
||||
if (entity.parentID !== undefined) {
|
||||
var parent = entitiesByID[entity.parentID];
|
||||
if (parent !== undefined) {
|
||||
entity.localPosition = Vec3.subtract(entity.position, parent.position);
|
||||
delete entity.position;
|
||||
}
|
||||
}
|
||||
}
|
840
unpublishedScripts/marketplace/shortbow/shortbow.json
Normal file
840
unpublishedScripts/marketplace/shortbow/shortbow.json
Normal file
|
@ -0,0 +1,840 @@
|
|||
{
|
||||
"Entities": [
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{02f39515-cab4-41d5-b315-5fb41613f844}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708750446,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -5.1684012413024902,
|
||||
"y": 0.54034698009490967,
|
||||
"z": -11.257695198059082
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": -1.3604279756546021,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"backgroundColor": {
|
||||
"blue": 65,
|
||||
"green": 78,
|
||||
"red": 82
|
||||
},
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 2,
|
||||
"y": 0.69999998807907104,
|
||||
"z": 0.0099999997764825821
|
||||
},
|
||||
"id": "{3eae601e-3c6e-49ab-8f40-dedee32f7573}",
|
||||
"lastEdited": 1487894036038423,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"lineHeight": 0.5,
|
||||
"name": "SB.DisplayScore",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -8.0707607269287109,
|
||||
"y": 1.5265679359436035,
|
||||
"z": -9.5913219451904297
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 2.118985652923584,
|
||||
"x": -5.1109838485717773,
|
||||
"y": -1803.69189453125,
|
||||
"z": -26.774648666381836
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.70708787441253662,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": 0.70708787441253662,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"text": "0",
|
||||
"type": "Text",
|
||||
"userData": "{\"displayType\":\"score\"}"
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.71920669078826904,
|
||||
"y": 3.3160061836242676,
|
||||
"z": 2.2217941284179688
|
||||
},
|
||||
"id": "{04288f77-64df-4323-ac38-9c1960a393a5}",
|
||||
"lastEdited": 1487893058314990,
|
||||
"lastEditedBy": "{fce8028a-4bac-43e8-96ff-4c7286ea4ab3}",
|
||||
"modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-button.fbx",
|
||||
"name": "SB.StartButton",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -9.8358345031738281,
|
||||
"y": 0.45674961805343628,
|
||||
"z": -13.044205665588379
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 4.0558013916015625,
|
||||
"x": -7.844393253326416,
|
||||
"y": -1805.730224609375,
|
||||
"z": -31.195960998535156
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": 1.52587890625e-05,
|
||||
"y": 1.52587890625e-05,
|
||||
"z": 1.52587890625e-05
|
||||
},
|
||||
"script": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/startGameButtonClientEntity.js",
|
||||
"shapeType": "static-mesh",
|
||||
"type": "Model",
|
||||
"userData": "{\"grabbableKey\":{\"wantsTrigger\":true}}"
|
||||
},
|
||||
{
|
||||
"backgroundColor": {
|
||||
"blue": 65,
|
||||
"green": 78,
|
||||
"red": 82
|
||||
},
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 2,
|
||||
"y": 0.69999998807907104,
|
||||
"z": 0.0099999997764825821
|
||||
},
|
||||
"id": "{1196096f-bcc9-4b19-970d-605113474c1b}",
|
||||
"lastEdited": 1487894037900323,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"lineHeight": 0.5,
|
||||
"name": "SB.DisplayHighScore",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -8.0707607269287109,
|
||||
"y": 0.26189804077148438,
|
||||
"z": -9.5913219451904297
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 2.118985652923584,
|
||||
"x": -5.11102294921875,
|
||||
"y": -1804.95654296875,
|
||||
"z": -26.77461051940918
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.70708787441253662,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": 0.70708787441253662,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"text": "0",
|
||||
"type": "Text",
|
||||
"userData": "{\"displayType\":\"highscore\"}"
|
||||
},
|
||||
{
|
||||
"backgroundColor": {
|
||||
"blue": 65,
|
||||
"green": 78,
|
||||
"red": 82
|
||||
},
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 1.4120937585830688,
|
||||
"y": 0.71569448709487915,
|
||||
"z": 0.0099999997764825821
|
||||
},
|
||||
"id": "{293c294d-1df5-461e-82a3-66abee852d44}",
|
||||
"lastEdited": 1487894033695485,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"lineHeight": 0.5,
|
||||
"name": "SB.DisplayWave",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -8.0707607269287109,
|
||||
"y": 1.5265679359436035,
|
||||
"z": -7.2889409065246582
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.5831384658813477,
|
||||
"x": -4.8431310653686523,
|
||||
"y": -1803.4239501953125,
|
||||
"z": -24.204343795776367
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.70708787441253662,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": 0.70708787441253662,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"text": "0",
|
||||
"type": "Text",
|
||||
"userData": "{\"displayType\":\"wave\"}"
|
||||
},
|
||||
{
|
||||
"backgroundColor": {
|
||||
"blue": 65,
|
||||
"green": 78,
|
||||
"red": 82
|
||||
},
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 1.4120937585830688,
|
||||
"y": 0.71569448709487915,
|
||||
"z": 0.0099999997764825821
|
||||
},
|
||||
"id": "{379afa7b-c668-4c4e-b290-e5c37fb3440c}",
|
||||
"lastEdited": 1487893055310428,
|
||||
"lastEditedBy": "{fce8028a-4bac-43e8-96ff-4c7286ea4ab3}",
|
||||
"lineHeight": 0.5,
|
||||
"name": "SB.DisplayLives",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -8.0707607269287109,
|
||||
"y": 0.26189804077148438,
|
||||
"z": -7.2889409065246582
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 1.5831384658813477,
|
||||
"x": -4.8431692123413086,
|
||||
"y": -1804.6885986328125,
|
||||
"z": -24.204303741455078
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.70708787441253662,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": 0.70708787441253662,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"text": "0",
|
||||
"type": "Text",
|
||||
"userData": "{\"displayType\":\"lives\"}"
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{760e81a1-a804-4f5e-9769-393d021fc8fe}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440234633,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -1.89238440990448,
|
||||
"y": -5.3368110656738281,
|
||||
"z": 11.512755393981934
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 1.9147146940231323,
|
||||
"y": -1809.7066650390625,
|
||||
"z": -4.8219971656799316
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{0a76d0ac-6353-467b-8edc-56417d5a987c}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440235124,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 3.6569130420684814,
|
||||
"y": -5.3365960121154785,
|
||||
"z": 10.01292610168457
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 7.4640579223632812,
|
||||
"y": -1809.7066650390625,
|
||||
"z": -6.3216567039489746
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{f8549c8a-e646-4feb-bbaf-70e7d5be755a}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440235339,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 8.8902750015258789,
|
||||
"y": -5.3364419937133789,
|
||||
"z": 10.195274353027344
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 12.697414398193359,
|
||||
"y": -1809.7066650390625,
|
||||
"z": -6.1391491889953613
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{f3aea4ae-4445-4a2d-8d61-e9fd72f04008}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708751269,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -2.5027251243591309,
|
||||
"y": 0.54042834043502808,
|
||||
"z": -11.257777214050293
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 1.3052481412887573,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{cc1ac907-124b-4372-8c4c-82d175546725}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708751135,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 2.7972855567932129,
|
||||
"y": 0.54059004783630371,
|
||||
"z": -11.257938385009766
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 6.6052589416503906,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{e25ce690-e267-4c51-80a0-f63a72474b8a}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708751527,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 0.17114110291004181,
|
||||
"y": 0.54050993919372559,
|
||||
"z": -11.257858276367188
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 3.979114294052124,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{91ee2285-38f8-4795-b6bc-7abc8dcde07c}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708750806,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 5.4656705856323242,
|
||||
"y": 0.54067152738571167,
|
||||
"z": -11.258020401000977
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 9.2736434936523438,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{d81e5fae-8a8d-4186-bbd2-0c3ae737b0f2}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892552671000,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 9.6099967956542969,
|
||||
"y": 0.64012420177459717,
|
||||
"z": -9.9802846908569336
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 13.417934417724609,
|
||||
"y": -1803.730712890625,
|
||||
"z": -26.314868927001953
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.22495110332965851,
|
||||
"x": -2.9734959753113799e-05,
|
||||
"y": 0.97437006235122681,
|
||||
"z": 2.9735869247815572e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{7056e21e-bce6-4c4b-bbca-36bea1dce303}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892708750993,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.BowSpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 8.1799373626708984,
|
||||
"y": 0.54075431823730469,
|
||||
"z": -11.258102416992188
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 11.987911224365234,
|
||||
"y": -1803.830078125,
|
||||
"z": -27.592727661132812
|
||||
},
|
||||
"rotation": {
|
||||
"w": 0.17366324365139008,
|
||||
"x": 4.9033405957743526e-07,
|
||||
"y": -0.98480510711669922,
|
||||
"z": -2.9563087082351558e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{ed073620-e304-4b8e-b12a-5371b595bbf6}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440234415,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": -2.3618791103363037,
|
||||
"y": -2.0691573619842529,
|
||||
"z": 11.254574775695801
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 1.4453276395797729,
|
||||
"y": -1806.43896484375,
|
||||
"z": -5.0802912712097168
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"collidesWith": "",
|
||||
"collisionMask": 0,
|
||||
"collisionless": 1,
|
||||
"color": {
|
||||
"blue": 171,
|
||||
"green": 50,
|
||||
"red": 62
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 0.24400754272937775,
|
||||
"y": 0.24400754272937775,
|
||||
"z": 0.24400754272937775
|
||||
},
|
||||
"id": "{32ed7820-c386-4da1-b676-7e63762861a3}",
|
||||
"ignoreForCollisions": 1,
|
||||
"lastEdited": 1487892440234854,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.EnemySpawn",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 0.64757472276687622,
|
||||
"y": -2.5217375755310059,
|
||||
"z": 10.08248233795166
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 0.42263346910476685,
|
||||
"x": 4.454803466796875,
|
||||
"y": -1806.8917236328125,
|
||||
"z": -6.2522788047790527
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 26.619264602661133,
|
||||
"y": 14.24090576171875,
|
||||
"z": 39.351066589355469
|
||||
},
|
||||
"id": "{d4c8f577-944d-4d50-ac85-e56387c0ef0a}",
|
||||
"lastEdited": 1487892440231278,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.fbx",
|
||||
"name": "SB.Platform",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 0.097909502685070038,
|
||||
"y": -1.0163799524307251,
|
||||
"z": 2.0321114063262939
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 49.597328186035156,
|
||||
"x": -20.681917190551758,
|
||||
"y": -1829.9739990234375,
|
||||
"z": -38.890060424804688
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shapeType": "static-mesh",
|
||||
"type": "Model"
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 23.341892242431641,
|
||||
"y": 12.223045349121094,
|
||||
"z": 32.012016296386719
|
||||
},
|
||||
"friction": 1,
|
||||
"id": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"lastEdited": 1487892440231832,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"modelURL": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.fbx",
|
||||
"name": "SB.Scoreboard",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"queryAACube": {
|
||||
"scale": 41.461017608642578,
|
||||
"x": -20.730508804321289,
|
||||
"y": -20.730508804321289,
|
||||
"z": -20.730508804321289
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"serverScripts": "file:///c:/Users/ryanh/dev/hifi/unpublishedScripts/marketplace/shortbow/shortbowServerEntity.js",
|
||||
"shapeType": "static-mesh",
|
||||
"type": "Model"
|
||||
},
|
||||
{
|
||||
"clientOnly": 0,
|
||||
"color": {
|
||||
"blue": 0,
|
||||
"green": 0,
|
||||
"red": 255
|
||||
},
|
||||
"created": "2017-02-23T23:28:32Z",
|
||||
"dimensions": {
|
||||
"x": 15.710711479187012,
|
||||
"y": 4.7783288955688477,
|
||||
"z": 1.6129581928253174
|
||||
},
|
||||
"id": "{84cdff6e-a68d-4bbf-8660-2d6a8c2f1fd0}",
|
||||
"lastEdited": 1487892440231522,
|
||||
"lastEditedBy": "{91f193dd-829a-4b33-ab27-e9a26160634a}",
|
||||
"name": "SB.GateCollider",
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"parentID": "{0cd1f1f7-53b9-4c15-bf25-42c0760d16f0}",
|
||||
"position": {
|
||||
"x": 0.31728419661521912,
|
||||
"y": -4.3002614974975586,
|
||||
"z": -12.531644821166992
|
||||
},
|
||||
"queryAACube": {
|
||||
"scale": 16.50031852722168,
|
||||
"x": -3.913693904876709,
|
||||
"y": -1816.709716796875,
|
||||
"z": -36.905204772949219
|
||||
},
|
||||
"rotation": {
|
||||
"w": 1,
|
||||
"x": -1.52587890625e-05,
|
||||
"y": -1.52587890625e-05,
|
||||
"z": -1.52587890625e-05
|
||||
},
|
||||
"shape": "Cube",
|
||||
"type": "Box",
|
||||
"visible": 0
|
||||
}
|
||||
],
|
||||
"Version": 68
|
||||
}
|
621
unpublishedScripts/marketplace/shortbow/shortbowGameManager.js
Normal file
621
unpublishedScripts/marketplace/shortbow/shortbowGameManager.js
Normal file
|
@ -0,0 +1,621 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 1/10/2017
|
||||
// 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
|
||||
//
|
||||
|
||||
/* globals ShortbowGameManager:true, utils */
|
||||
|
||||
Script.include('utils.js');
|
||||
|
||||
// +--------+ +-----------+ +-----------------+
|
||||
// | | | |<-----+ |
|
||||
// | IDLE +----->| PLAYING | | BETWEEN_WAVES |
|
||||
// | | | +----->| |
|
||||
// +--------+ +-----+-----+ +-----------------+
|
||||
// ^ |
|
||||
// | v
|
||||
// | +-------------+
|
||||
// | | |
|
||||
// +---------+ GAME_OVER |
|
||||
// | |
|
||||
// +-------------+
|
||||
var GAME_STATES = {
|
||||
IDLE: 0,
|
||||
PLAYING: 1,
|
||||
BETWEEN_WAVES: 2,
|
||||
GAME_OVER: 3
|
||||
};
|
||||
|
||||
// Load the sounds that we will be using in the game so they are ready to be
|
||||
// used when we need them.
|
||||
var BEGIN_BUILDING_SOUND = SoundCache.getSound(Script.resolvePath("sounds/gameOn.wav"));
|
||||
var GAME_OVER_SOUND = SoundCache.getSound(Script.resolvePath("sounds/gameOver.wav"));
|
||||
var WAVE_COMPLETE_SOUND = SoundCache.getSound(Script.resolvePath("sounds/waveComplete.wav"));
|
||||
var EXPLOSION_SOUND = SoundCache.getSound(Script.resolvePath("sounds/explosion.wav"));
|
||||
var TARGET_HIT_SOUND = SoundCache.getSound(Script.resolvePath("sounds/targetHit.wav"));
|
||||
var ESCAPE_SOUND = SoundCache.getSound(Script.resolvePath("sounds/escape.wav"));
|
||||
|
||||
const STARTING_NUMBER_OF_LIVES = 6;
|
||||
const ENEMIES_PER_WAVE_MULTIPLIER = 2;
|
||||
const POINTS_PER_KILL = 100;
|
||||
const ENEMY_SPEED = 3.0;
|
||||
|
||||
// Encode a set of key-value pairs into a param string. Does NOT do any URL escaping.
|
||||
function encodeURLParams(params) {
|
||||
var paramPairs = [];
|
||||
for (var key in params) {
|
||||
paramPairs.push(key + "=" + params[key]);
|
||||
}
|
||||
return paramPairs.join("&");
|
||||
}
|
||||
|
||||
function sendAndUpdateHighScore(entityID, score, wave, numPlayers, onResposeReceived) {
|
||||
const URL = 'https://script.google.com/macros/s/AKfycbwbjCm9mGd1d5BzfAHmVT_XKmWyUYRkjCEqDOKm1368oM8nqWni/exec';
|
||||
print("Sending high score");
|
||||
|
||||
const paramString = encodeURLParams({
|
||||
entityID: entityID,
|
||||
score: score,
|
||||
wave: wave,
|
||||
numPlayers: numPlayers
|
||||
});
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.onreadystatechange = function() {
|
||||
print("ready state: ", request.readyState, request.status, request.readyState === request.DONE, request.response);
|
||||
if (request.readyState === request.DONE && request.status === 200) {
|
||||
print("Got response for high score: ", request.response);
|
||||
var response = JSON.parse(request.responseText);
|
||||
if (response.highScore !== undefined) {
|
||||
onResposeReceived(response.highScore);
|
||||
}
|
||||
}
|
||||
};
|
||||
request.open('GET', URL + "?" + paramString);
|
||||
request.timeout = 10000;
|
||||
request.send();
|
||||
}
|
||||
|
||||
function findChildrenWithName(parentID, name) {
|
||||
var childrenIDs = Entities.getChildrenIDs(parentID);
|
||||
var matchingIDs = [];
|
||||
for (var i = 0; i < childrenIDs.length; ++i) {
|
||||
var id = childrenIDs[i];
|
||||
var childName = Entities.getEntityProperties(id, 'name').name;
|
||||
if (childName === name) {
|
||||
matchingIDs.push(id);
|
||||
}
|
||||
}
|
||||
return matchingIDs;
|
||||
}
|
||||
|
||||
function getPropertiesForEntities(entityIDs, desiredProperties) {
|
||||
var properties = [];
|
||||
for (var i = 0; i < entityIDs.length; ++i) {
|
||||
properties.push(Entities.getEntityProperties(entityIDs[i], desiredProperties));
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
var baseEnemyProperties = {
|
||||
"name": "SB.Enemy",
|
||||
"damping": 0,
|
||||
"linearDamping": 0,
|
||||
"angularDamping": 0,
|
||||
"acceleration": {
|
||||
"x": 0,
|
||||
"y": -9,
|
||||
"z": 0
|
||||
},
|
||||
"angularVelocity": {
|
||||
"x": -0.058330666273832321,
|
||||
"y": -0.77943277359008789,
|
||||
"z": -2.1163818836212158
|
||||
},
|
||||
"clientOnly": 0,
|
||||
"collisionsWillMove": 1,
|
||||
"dimensions": {
|
||||
"x": 0.63503998517990112,
|
||||
"y": 0.63503998517990112,
|
||||
"z": 0.63503998517990112
|
||||
},
|
||||
"dynamic": 1,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": -15,
|
||||
"z": 0
|
||||
},
|
||||
"lifetime": 30,
|
||||
"id": "{ed8f7339-8bbd-4750-968e-c3ceb9d64721}",
|
||||
"modelURL": Script.resolvePath("models/Amber.fbx"),
|
||||
"owningAvatarID": "{00000000-0000-0000-0000-000000000000}",
|
||||
"queryAACube": {
|
||||
"scale": 1.0999215841293335,
|
||||
"x": -0.54996079206466675,
|
||||
"y": -0.54996079206466675,
|
||||
"z": -0.54996079206466675
|
||||
},
|
||||
"shapeType": "sphere",
|
||||
"type": "Model",
|
||||
"script": Script.resolvePath('enemyClientEntity.js'),
|
||||
"serverScripts": Script.resolvePath('enemyServerEntity.js')
|
||||
};
|
||||
|
||||
function searchForChildren(parentID, names, callback, timeoutMs) {
|
||||
// Map from name to entity ID for the children that have been found
|
||||
var foundEntities = {};
|
||||
for (var i = 0; i < names.length; ++i) {
|
||||
foundEntities[names[i]] = null;
|
||||
}
|
||||
|
||||
const CHECK_EVERY_MS = 500;
|
||||
const maxChecks = Math.ceil(timeoutMs / CHECK_EVERY_MS);
|
||||
|
||||
var check = 0;
|
||||
var intervalID = Script.setInterval(function() {
|
||||
check++;
|
||||
|
||||
var childrenIDs = Entities.getChildrenIDs(parentID);
|
||||
print("\tNumber of children:", childrenIDs.length);
|
||||
|
||||
for (var i = 0; i < childrenIDs.length; ++i) {
|
||||
print("\t\t" + i + ".", Entities.getEntityProperties(childrenIDs[i]).name);
|
||||
var id = childrenIDs[i];
|
||||
var name = Entities.getEntityProperties(id, 'name').name;
|
||||
var idx = names.indexOf(name);
|
||||
if (idx > -1) {
|
||||
foundEntities[name] = id;
|
||||
print(name, id);
|
||||
names.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (names.length === 0 || check >= maxChecks) {
|
||||
Script.clearInterval(intervalID);
|
||||
callback(foundEntities);
|
||||
}
|
||||
}, CHECK_EVERY_MS);
|
||||
}
|
||||
|
||||
ShortbowGameManager = function(rootEntityID, bowPositions, spawnPositions) {
|
||||
print("Starting game manager");
|
||||
var self = this;
|
||||
|
||||
this.gameState = GAME_STATES.IDLE;
|
||||
|
||||
this.rootEntityID = rootEntityID;
|
||||
this.bowPositions = bowPositions;
|
||||
this.rootPosition = null;
|
||||
this.spawnPositions = spawnPositions;
|
||||
|
||||
this.loadedChildren = false;
|
||||
|
||||
const START_BUTTON_NAME = 'SB.StartButton';
|
||||
const WAVE_DISPLAY_NAME = 'SB.DisplayWave';
|
||||
const SCORE_DISPLAY_NAME = 'SB.DisplayScore';
|
||||
const LIVES_DISPLAY_NAME = 'SB.DisplayLives';
|
||||
const HIGH_SCORE_DISPLAY_NAME = 'SB.DisplayHighScore';
|
||||
|
||||
const SEARCH_FOR_CHILDREN_TIMEOUT = 5000;
|
||||
|
||||
searchForChildren(rootEntityID, [
|
||||
START_BUTTON_NAME,
|
||||
WAVE_DISPLAY_NAME,
|
||||
SCORE_DISPLAY_NAME,
|
||||
LIVES_DISPLAY_NAME,
|
||||
HIGH_SCORE_DISPLAY_NAME
|
||||
], function(children) {
|
||||
self.loadedChildren = true;
|
||||
self.startButtonID = children[START_BUTTON_NAME];
|
||||
self.waveDisplayID = children[WAVE_DISPLAY_NAME];
|
||||
self.scoreDisplayID = children[SCORE_DISPLAY_NAME];
|
||||
self.livesDisplayID = children[LIVES_DISPLAY_NAME];
|
||||
self.highScoreDisplayID = children[HIGH_SCORE_DISPLAY_NAME];
|
||||
|
||||
sendAndUpdateHighScore(self.rootEntityID, self.score, self.waveNumber, 1, self.setHighScore.bind(self));
|
||||
|
||||
self.reset();
|
||||
}, SEARCH_FOR_CHILDREN_TIMEOUT);
|
||||
|
||||
// Gameplay state
|
||||
this.waveNumber = 0;
|
||||
this.livesLeft = STARTING_NUMBER_OF_LIVES;
|
||||
this.score = 0;
|
||||
this.nextWaveTimer = null;
|
||||
this.spawnEnemyTimers = [];
|
||||
this.remainingEnemies = [];
|
||||
this.bowIDs = [];
|
||||
|
||||
this.startButtonChannelName = 'button-' + this.rootEntityID;
|
||||
|
||||
// Entity client and server scripts will send messages to this channel
|
||||
this.commChannelName = "shortbow-" + this.rootEntityID;
|
||||
Messages.subscribe(this.commChannelName);
|
||||
Messages.messageReceived.connect(this, this.onReceivedMessage);
|
||||
print("Listening on: ", this.commChannelName);
|
||||
Messages.sendMessage(this.commChannelName, 'hi');
|
||||
};
|
||||
ShortbowGameManager.prototype = {
|
||||
reset: function() {
|
||||
Entities.editEntity(this.startButtonID, { visible: true });
|
||||
},
|
||||
cleanup: function() {
|
||||
Messages.unsubscribe(this.commChannelName);
|
||||
Messages.messageReceived.disconnect(this, this.onReceivedMessage);
|
||||
|
||||
if (this.checkEnemiesTimer) {
|
||||
Script.clearInterval(this.checkEnemiesTimer);
|
||||
this.checkEnemiesTimer = null;
|
||||
}
|
||||
|
||||
for (var i = this.bowIDs.length - 1; i >= 0; i--) {
|
||||
Entities.deleteEntity(this.bowIDs[i]);
|
||||
}
|
||||
this.bowIDs = [];
|
||||
for (i = 0; i < this.remainingEnemies.length; i++) {
|
||||
Entities.deleteEntity(this.remainingEnemies[i].id);
|
||||
}
|
||||
this.remainingEnemies = [];
|
||||
|
||||
this.gameState = GAME_STATES.IDLE;
|
||||
},
|
||||
startGame: function() {
|
||||
if (this.gameState !== GAME_STATES.IDLE) {
|
||||
print("shortbowGameManagerManager.js | Error, trying to start game when not in idle state");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.loadedChildren === false) {
|
||||
print('shortbowGameManager.js | Children have not loaded, not allowing game to start');
|
||||
return;
|
||||
}
|
||||
|
||||
print("Game started!!");
|
||||
|
||||
this.rootPosition = Entities.getEntityProperties(this.rootEntityID, 'position').position;
|
||||
|
||||
Entities.editEntity(this.startButtonID, { visible: false });
|
||||
|
||||
// Spawn bows
|
||||
var bowSpawnEntityIDs = findChildrenWithName(this.rootEntityID, 'SB.BowSpawn');
|
||||
var bowSpawnProperties = getPropertiesForEntities(bowSpawnEntityIDs, ['position', 'rotation']);
|
||||
for (var i = 0; i < bowSpawnProperties.length; ++i) {
|
||||
const props = bowSpawnProperties[i];
|
||||
Vec3.print("Creating bow: " + i, props.position);
|
||||
this.bowIDs.push(Entities.addEntity({
|
||||
"position": props.position,
|
||||
"rotation": props.rotation,
|
||||
"collisionsWillMove": 1,
|
||||
"compoundShapeURL": Script.resolvePath("bow/bow_collision_hull.obj"),
|
||||
"created": "2016-09-01T23:57:55Z",
|
||||
"dimensions": {
|
||||
"x": 0.039999999105930328,
|
||||
"y": 1.2999999523162842,
|
||||
"z": 0.20000000298023224
|
||||
},
|
||||
"dynamic": 1,
|
||||
"gravity": {
|
||||
"x": 0,
|
||||
"y": -9.8,
|
||||
"z": 0
|
||||
},
|
||||
"modelURL": Script.resolvePath("bow/bow-deadly.fbx"),
|
||||
"name": "WG.Hifi-Bow",
|
||||
"script": Script.resolvePath("bow/bow.js"),
|
||||
"shapeType": "compound",
|
||||
"type": "Model",
|
||||
"userData": "{\"grabbableKey\":{\"grabbable\":true},\"wearable\":{\"joints\":{\"RightHand\":[{\"x\":0.0813,\"y\":0.0452,\"z\":0.0095},{\"x\":-0.3946,\"y\":-0.6604,\"z\":0.4748,\"w\":-0.4275}],\"LeftHand\":[{\"x\":-0.0881,\"y\":0.0259,\"z\":0.0159},{\"x\":0.4427,\"y\":-0.6519,\"z\":0.4592,\"w\":0.4099}]}}}"
|
||||
}));
|
||||
}
|
||||
|
||||
// Initialize game state
|
||||
this.waveNumber = 0;
|
||||
this.setScore(0);
|
||||
this.setLivesLeft(STARTING_NUMBER_OF_LIVES);
|
||||
|
||||
this.nextWaveTimer = Script.setTimeout(this.startNextWave.bind(this), 100);
|
||||
this.spawnEnemyTimers = [];
|
||||
this.checkEnemiesTimer = null;
|
||||
this.remainingEnemies = [];
|
||||
|
||||
// SpawnQueue is a list of enemies left to spawn. Each entry looks like:
|
||||
//
|
||||
// { spawnAt: 1000, position: { x: 0, y: 0, z: 0 } }
|
||||
//
|
||||
// where spawnAt is the number of millseconds after the start of the wave
|
||||
// to spawn the enemy. The list is sorted by spawnAt, ascending.
|
||||
this.spawnQueue = [];
|
||||
|
||||
this.gameState = GAME_STATES.BETWEEN_WAVES;
|
||||
|
||||
Audio.playSound(BEGIN_BUILDING_SOUND, {
|
||||
volume: 1.0,
|
||||
position: this.rootPosition
|
||||
});
|
||||
},
|
||||
startNextWave: function() {
|
||||
if (this.gameState !== GAME_STATES.BETWEEN_WAVES) {
|
||||
return;
|
||||
}
|
||||
|
||||
print("Starting next wave");
|
||||
this.gameState = GAME_STATES.PLAYING;
|
||||
this.waveNumber++;
|
||||
this.remainingEnemies= [];
|
||||
this.spawnQueue = [];
|
||||
this.spawnStartTime = Date.now();
|
||||
|
||||
Entities.editEntity(this.waveDisplayID, { text: this.waveNumber});
|
||||
|
||||
var numberOfEnemiesLeftToSpawn = this.waveNumber * ENEMIES_PER_WAVE_MULTIPLIER;
|
||||
var delayBetweenSpawns = 2000 / Math.max(1, Math.log(this.waveNumber));
|
||||
var currentDelay = 2000;
|
||||
|
||||
print("Number of enemies:", numberOfEnemiesLeftToSpawn);
|
||||
this.checkEnemiesTimer = Script.setInterval(this.checkEnemies.bind(this), 100);
|
||||
|
||||
var enemySpawnEntityIDs = findChildrenWithName(this.rootEntityID, 'SB.EnemySpawn');
|
||||
var enemySpawnProperties = getPropertiesForEntities(enemySpawnEntityIDs, ['position', 'rotation']);
|
||||
|
||||
for (var i = 0; i < numberOfEnemiesLeftToSpawn; ++i) {
|
||||
print("Adding enemy");
|
||||
var idx = Math.floor(Math.random() * enemySpawnProperties.length);
|
||||
var props = enemySpawnProperties[idx];
|
||||
this.spawnQueue.push({
|
||||
spawnAt: currentDelay,
|
||||
position: props.position,
|
||||
rotation: props.rotation,
|
||||
velocity: Vec3.multiply(ENEMY_SPEED, Quat.getFront(props.rotation))
|
||||
|
||||
});
|
||||
currentDelay += delayBetweenSpawns;
|
||||
}
|
||||
|
||||
print("Starting wave", this.waveNumber);
|
||||
|
||||
},
|
||||
checkWaveComplete: function() {
|
||||
if (this.gameState !== GAME_STATES.PLAYING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.spawnQueue.length === 0 && this.remainingEnemies.length === 0) {
|
||||
this.gameState = GAME_STATES.BETWEEN_WAVES;
|
||||
Script.setTimeout(this.startNextWave.bind(this), 5000);
|
||||
|
||||
Script.clearInterval(this.checkEnemiesTimer);
|
||||
this.checkEnemiesTimer = null;
|
||||
|
||||
// Play after 1.5s to let other sounds finish playing
|
||||
var self = this;
|
||||
Script.setTimeout(function() {
|
||||
Audio.playSound(WAVE_COMPLETE_SOUND, {
|
||||
volume: 1.0,
|
||||
position: self.rootPosition
|
||||
});
|
||||
}, 1500);
|
||||
}
|
||||
},
|
||||
setHighScore: function(highScore) {
|
||||
print("Setting high score to:", this.highScoreDisplayID, highScore);
|
||||
Entities.editEntity(this.highScoreDisplayID, { text: highScore });
|
||||
},
|
||||
setLivesLeft: function(lives) {
|
||||
lives = Math.max(0, lives);
|
||||
this.livesLeft = lives;
|
||||
Entities.editEntity(this.livesDisplayID, { text: this.livesLeft });
|
||||
},
|
||||
setScore: function(score) {
|
||||
this.score = score;
|
||||
Entities.editEntity(this.scoreDisplayID, { text: this.score });
|
||||
},
|
||||
checkEnemies: function() {
|
||||
if (this.gameState !== GAME_STATES.PLAYING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the spawn queueu to see if there are any enemies that need to
|
||||
// be spawned
|
||||
var waveElapsedTime = Date.now() - this.spawnStartTime;
|
||||
while (this.spawnQueue.length > 0 && waveElapsedTime > this.spawnQueue[0].spawnAt) {
|
||||
baseEnemyProperties.position = this.spawnQueue[0].position;
|
||||
baseEnemyProperties.rotation = this.spawnQueue[0].rotation;
|
||||
baseEnemyProperties.velocity= this.spawnQueue[0].velocity;
|
||||
|
||||
baseEnemyProperties.userData = JSON.stringify({
|
||||
gameChannel: this.commChannelName,
|
||||
grabbableKey: {
|
||||
grabbable: false
|
||||
}
|
||||
});
|
||||
|
||||
var entityID = Entities.addEntity(baseEnemyProperties);
|
||||
this.remainingEnemies.push({
|
||||
id: entityID,
|
||||
lastKnownPosition: baseEnemyProperties.position,
|
||||
lastHeartbeat: Date.now()
|
||||
});
|
||||
this.spawnQueue.splice(0, 1);
|
||||
Script.setTimeout(function() {
|
||||
const JUMP_SPEED = 5.0;
|
||||
var velocity = Entities.getEntityProperties(entityID, 'velocity').velocity;
|
||||
velocity.y += JUMP_SPEED;
|
||||
Entities.editEntity(entityID, { velocity: velocity });
|
||||
|
||||
}, 500 + Math.random() * 4000);
|
||||
}
|
||||
|
||||
// Check the list of remaining enemies to see if any are too far away
|
||||
// or haven't been heard from in awhile - if so, delete them.
|
||||
var enemiesEscaped = false;
|
||||
const MAX_UNHEARD_TIME_BEFORE_DESTROYING_ENTITY_MS = 5000;
|
||||
const MAX_DISTANCE_FROM_GAME_BEFORE_DESTROYING_ENTITY = 200;
|
||||
for (var i = this.remainingEnemies.length - 1; i >= 0; --i) {
|
||||
var enemy = this.remainingEnemies[i];
|
||||
var timeSinceLastHeartbeat = Date.now() - enemy.lastHeartbeat;
|
||||
var distance = Vec3.distance(enemy.lastKnownPosition, this.rootPosition);
|
||||
if (timeSinceLastHeartbeat > MAX_UNHEARD_TIME_BEFORE_DESTROYING_ENTITY_MS
|
||||
|| distance > MAX_DISTANCE_FROM_GAME_BEFORE_DESTROYING_ENTITY) {
|
||||
|
||||
print("EXPIRING: ", enemy.id);
|
||||
Entities.deleteEntity(enemy.id);
|
||||
this.remainingEnemies.splice(i, 1);
|
||||
Audio.playSound(TARGET_HIT_SOUND, {
|
||||
volume: 1.0,
|
||||
position: this.rootPosition
|
||||
});
|
||||
this.setScore(this.score + POINTS_PER_KILL);
|
||||
enemiesEscaped = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (enemiesEscaped) {
|
||||
this.checkWaveComplete();
|
||||
}
|
||||
},
|
||||
endGame: function() {
|
||||
if (this.gameState !== GAME_STATES.PLAYING) {
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
Script.setTimeout(function() {
|
||||
Audio.playSound(GAME_OVER_SOUND, {
|
||||
volume: 1.0,
|
||||
position: self.rootPosition
|
||||
});
|
||||
}, 1500);
|
||||
|
||||
this.gameState = GAME_STATES.GAME_OVER;
|
||||
print("GAME OVER");
|
||||
|
||||
// Update high score
|
||||
sendAndUpdateHighScore(this.rootEntityID, this.score, this.waveNumber, 1, this.setHighScore.bind(this));
|
||||
|
||||
// Cleanup
|
||||
Script.clearTimeout(this.nextWaveTimer);
|
||||
this.nextWaveTimer = null;
|
||||
var i;
|
||||
for (i = 0; i < this.spawnEnemyTimers.length; ++i) {
|
||||
Script.clearTimeout(this.spawnEnemyTimers[i]);
|
||||
}
|
||||
this.spawnEnemyTimers = [];
|
||||
|
||||
Script.clearInterval(this.checkEnemiesTimer);
|
||||
this.checkEnemiesTimer = null;
|
||||
|
||||
|
||||
for (i = this.bowIDs.length - 1; i >= 0; i--) {
|
||||
var id = this.bowIDs[i];
|
||||
print("Checking bow: ", id);
|
||||
var userData = utils.parseJSON(Entities.getEntityProperties(id, 'userData').userData);
|
||||
var bowIsHeld = userData.grabKey !== undefined && userData.grabKey !== undefined && userData.grabKey.refCount > 0;
|
||||
print("Held: ", bowIsHeld);
|
||||
if (!bowIsHeld) {
|
||||
Entities.deleteEntity(id);
|
||||
this.bowIDs.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < this.remainingEnemies.length; i++) {
|
||||
Entities.deleteEntity(this.remainingEnemies[i].id);
|
||||
}
|
||||
this.remainingEnemies = [];
|
||||
|
||||
// Wait a short time before showing the start button so that any current sounds
|
||||
// can finish playing.
|
||||
const WAIT_TO_REENABLE_GAME_TIMEOUT_MS = 3000;
|
||||
Script.setTimeout(function() {
|
||||
Entities.editEntity(this.startButtonID, { visible: true });
|
||||
this.gameState = GAME_STATES.IDLE;
|
||||
}.bind(this), WAIT_TO_REENABLE_GAME_TIMEOUT_MS);
|
||||
},
|
||||
onReceivedMessage: function(channel, messageJSON, senderID) {
|
||||
if (channel === this.commChannelName) {
|
||||
var message = utils.parseJSON(messageJSON);
|
||||
if (message === undefined) {
|
||||
print("shortbowGameManager.js | Received non-json message:", JSON.stringify(messageJSON));
|
||||
return;
|
||||
}
|
||||
switch (message.type) {
|
||||
case 'start-game':
|
||||
this.startGame();
|
||||
break;
|
||||
case 'enemy-killed':
|
||||
this.onEnemyKilled(message.entityID, message.position);
|
||||
break;
|
||||
case 'enemy-escaped':
|
||||
this.onEnemyEscaped(message.entityID);
|
||||
break;
|
||||
case 'enemy-heartbeat':
|
||||
this.onEnemyHeartbeat(message.entityID, message.position);
|
||||
break;
|
||||
default:
|
||||
print("shortbowGameManager.js | Ignoring unknown message type: ", message.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
onEnemyKilled: function(entityID, position) {
|
||||
if (this.gameState !== GAME_STATES.PLAYING) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = this.remainingEnemies.length - 1; i >= 0; --i) {
|
||||
var enemy = this.remainingEnemies[i];
|
||||
if (enemy.id === entityID) {
|
||||
this.remainingEnemies.splice(i, 1);
|
||||
Audio.playSound(TARGET_HIT_SOUND, {
|
||||
volume: 1.0,
|
||||
position: this.rootPosition
|
||||
});
|
||||
|
||||
// Update score
|
||||
this.setScore(this.score + POINTS_PER_KILL);
|
||||
print("SCORE: ", this.score);
|
||||
|
||||
this.checkWaveComplete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
onEnemyEscaped: function(entityID, position) {
|
||||
if (this.gameState !== GAME_STATES.PLAYING) {
|
||||
return;
|
||||
}
|
||||
|
||||
var enemiesEscaped = false;
|
||||
for (var i = this.remainingEnemies.length - 1; i >= 0; --i) {
|
||||
var enemy = this.remainingEnemies[i];
|
||||
if (enemy.id === entityID) {
|
||||
Entities.deleteEntity(enemy.id);
|
||||
this.remainingEnemies.splice(i, 1);
|
||||
this.setLivesLeft(this.livesLeft - 1);
|
||||
Audio.playSound(ESCAPE_SOUND, {
|
||||
volume: 1.0,
|
||||
position: this.rootPosition
|
||||
});
|
||||
enemiesEscaped = true;
|
||||
}
|
||||
}
|
||||
if (this.livesLeft <= 0) {
|
||||
this.endGame();
|
||||
} else if (enemiesEscaped) {
|
||||
this.checkWaveComplete();
|
||||
}
|
||||
},
|
||||
onEnemyHeartbeat: function(entityID, position) {
|
||||
for (var i = 0; i < this.remainingEnemies.length; i++) {
|
||||
var enemy = this.remainingEnemies[i];
|
||||
if (enemy.id === entityID) {
|
||||
enemy.lastHeartbeat = Date.now();
|
||||
enemy.lastKnownPosition = position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 1/10/2017
|
||||
// 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
|
||||
//
|
||||
|
||||
/* globals TEMPLATES:true, SHORTBOW_ENTITIES, ShortbowGameManager */
|
||||
|
||||
(function() {
|
||||
Script.include('utils.js');
|
||||
Script.include('shortbow.js');
|
||||
Script.include('shortbowGameManager.js');
|
||||
|
||||
TEMPLATES = SHORTBOW_ENTITIES.Entities;
|
||||
|
||||
this.entityID = null;
|
||||
var gameManager = null;
|
||||
this.preload = function(entityID) {
|
||||
this.entityID = entityID;
|
||||
|
||||
var bowPositions = [];
|
||||
var spawnPositions = [];
|
||||
for (var i = 0; i < TEMPLATES.length; ++i) {
|
||||
var template = TEMPLATES[i];
|
||||
if (template.name === "SB.BowSpawn") {
|
||||
bowPositions.push(template.localPosition);
|
||||
} else if (template.name === "SB.EnemySpawn") {
|
||||
spawnPositions.push(template.localPosition);
|
||||
}
|
||||
}
|
||||
|
||||
gameManager = new ShortbowGameManager(this.entityID, bowPositions, spawnPositions);
|
||||
};
|
||||
this.unload = function() {
|
||||
if (gameManager) {
|
||||
gameManager.cleanup();
|
||||
gameManager = null;
|
||||
}
|
||||
};
|
||||
});
|
BIN
unpublishedScripts/marketplace/shortbow/sounds/escape.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/sounds/escape.wav
Normal file
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/sounds/explosion.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/sounds/explosion.wav
Normal file
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/sounds/fight.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/sounds/fight.wav
Normal file
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/sounds/gameOn.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/sounds/gameOn.wav
Normal file
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/sounds/gameOver.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/sounds/gameOver.wav
Normal file
Binary file not shown.
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/sounds/spawn.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/sounds/spawn.wav
Normal file
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/sounds/targetHit.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/sounds/targetHit.wav
Normal file
Binary file not shown.
Binary file not shown.
BIN
unpublishedScripts/marketplace/shortbow/sounds/waveComplete.wav
Normal file
BIN
unpublishedScripts/marketplace/shortbow/sounds/waveComplete.wav
Normal file
Binary file not shown.
210
unpublishedScripts/marketplace/shortbow/spawnShortbow.js
Normal file
210
unpublishedScripts/marketplace/shortbow/spawnShortbow.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 1/10/2017
|
||||
// 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
|
||||
//
|
||||
/* globals utils, SHORTBOW_ENTITIES, TEMPLATES:true */
|
||||
|
||||
Script.include('utils.js');
|
||||
Script.include('shortbow.js');
|
||||
Script.include('shortbowGameManager.js');
|
||||
TEMPLATES = SHORTBOW_ENTITIES.Entities;
|
||||
|
||||
// Merge two objects into a new object. If a key name appears in both a and b,
|
||||
// the value in a will be used.
|
||||
//
|
||||
// @param {object} a
|
||||
// @param {object} b
|
||||
// @returns {object} The new object
|
||||
function mergeObjects(a, b) {
|
||||
var obj = {};
|
||||
var key;
|
||||
for (key in b) {
|
||||
obj[key] = b[key];
|
||||
}
|
||||
for (key in a) {
|
||||
obj[key] = a[key];
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Spawn an entity from a template.
|
||||
//
|
||||
// The overrides can be used to override or add properties in the template. For instance,
|
||||
// it's common to override the `position` property so that you can set the position
|
||||
// of the entity to be spawned.
|
||||
//
|
||||
// @param {string} templateName The name of the template to spawn
|
||||
// @param {object} overrides An object containing properties that will override
|
||||
// any properties set in the template.
|
||||
function spawnTemplate(templateName, overrides) {
|
||||
var template = getTemplate(templateName);
|
||||
if (template === null) {
|
||||
print("ERROR, unknown template name:", templateName);
|
||||
return null;
|
||||
}
|
||||
print("Spawning: ", templateName);
|
||||
var properties = mergeObjects(overrides, template);
|
||||
return Entities.addEntity(properties);
|
||||
}
|
||||
|
||||
function spawnTemplates(templateName, overrides) {
|
||||
var templates = getTemplates(templateName);
|
||||
if (template.length === 0) {
|
||||
print("ERROR, unknown template name:", templateName);
|
||||
return [];
|
||||
}
|
||||
|
||||
var spawnedEntities = [];
|
||||
for (var i = 0; i < templates.length; ++i) {
|
||||
print("Spawning: ", templateName);
|
||||
var properties = mergeObjects(overrides, templates[i]);
|
||||
spawnedEntities.push(Entities.addEntity(properties));
|
||||
}
|
||||
return spawnedEntities;
|
||||
}
|
||||
|
||||
// TEMPLATES contains a dictionary of different named entity templates. An entity
|
||||
// template is just a list of properties.
|
||||
//
|
||||
// @param name Name of the template to get
|
||||
// @return {object} The matching template, or null if not found
|
||||
function getTemplate(name) {
|
||||
for (var i = 0; i < TEMPLATES.length; ++i) {
|
||||
if (TEMPLATES[i].name === name) {
|
||||
return TEMPLATES[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function getTemplates(name) {
|
||||
var templates = [];
|
||||
for (var i = 0; i < TEMPLATES.length; ++i) {
|
||||
if (TEMPLATES[i].name === name) {
|
||||
templates.push(TEMPLATES[i]);
|
||||
}
|
||||
}
|
||||
return templates;
|
||||
}
|
||||
|
||||
|
||||
// Cleanup Shortbow template data
|
||||
for (var i = 0; i < TEMPLATES.length; ++i) {
|
||||
var template = TEMPLATES[i];
|
||||
|
||||
// Fixup model url
|
||||
if (template.type === "Model") {
|
||||
var urlParts = template.modelURL.split("/");
|
||||
var filename = urlParts[urlParts.length - 1];
|
||||
var newURL = Script.resolvePath("models/" + filename);
|
||||
print("Updated url", template.modelURL, "to", newURL);
|
||||
template.modelURL = newURL;
|
||||
}
|
||||
}
|
||||
|
||||
var entityIDs = [];
|
||||
|
||||
var scoreboardID = null;
|
||||
var buttonID = null;
|
||||
var waveDisplayID = null;
|
||||
var scoreDisplayID = null;
|
||||
var highScoreDisplayID = null;
|
||||
var livesDisplayID = null;
|
||||
var platformID = null;
|
||||
function createLocalGame() {
|
||||
var rootPosition = utils.findSurfaceBelowPosition(MyAvatar.position);
|
||||
rootPosition.y += 6.11;
|
||||
|
||||
scoreboardID = spawnTemplate("SB.Scoreboard", {
|
||||
position: rootPosition
|
||||
});
|
||||
entityIDs.push(scoreboardID);
|
||||
|
||||
// Create start button
|
||||
buttonID = spawnTemplate("SB.StartButton", {
|
||||
parentID: scoreboardID,
|
||||
script: Script.resolvePath("startGameButtonClientEntity.js"),
|
||||
userData: JSON.stringify({
|
||||
grabbableKey: {
|
||||
wantsTrigger: true
|
||||
}
|
||||
})
|
||||
});
|
||||
entityIDs.push(buttonID);
|
||||
|
||||
|
||||
waveDisplayID = spawnTemplate("SB.DisplayWave", {
|
||||
parentID: scoreboardID,
|
||||
userData: JSON.stringify({
|
||||
displayType: "wave"
|
||||
})
|
||||
});
|
||||
entityIDs.push(waveDisplayID);
|
||||
|
||||
scoreDisplayID = spawnTemplate("SB.DisplayScore", {
|
||||
parentID: scoreboardID,
|
||||
userData: JSON.stringify({
|
||||
displayType: "score"
|
||||
})
|
||||
});
|
||||
entityIDs.push(scoreDisplayID);
|
||||
|
||||
livesDisplayID = spawnTemplate("SB.DisplayLives", {
|
||||
parentID: scoreboardID,
|
||||
userData: JSON.stringify({
|
||||
displayType: "lives"
|
||||
})
|
||||
});
|
||||
entityIDs.push(livesDisplayID);
|
||||
|
||||
highScoreDisplayID = spawnTemplate("SB.DisplayHighScore", {
|
||||
parentID: scoreboardID,
|
||||
userData: JSON.stringify({
|
||||
displayType: "highscore"
|
||||
})
|
||||
});
|
||||
entityIDs.push(highScoreDisplayID);
|
||||
|
||||
platformID = spawnTemplate("SB.Platform", {
|
||||
parentID: scoreboardID
|
||||
});
|
||||
entityIDs.push(platformID);
|
||||
|
||||
spawnTemplate("SB.GateCollider", {
|
||||
parentID: scoreboardID,
|
||||
visible: false
|
||||
});
|
||||
entityIDs.push(platformID);
|
||||
|
||||
Entities.editEntity(scoreboardID, {
|
||||
serverScripts: Script.resolvePath('shortbowServerEntity.js')
|
||||
});
|
||||
|
||||
spawnTemplates("SB.BowSpawn", {
|
||||
parentID: scoreboardID,
|
||||
visible: false
|
||||
});
|
||||
spawnTemplates("SB.EnemySpawn", {
|
||||
parentID: scoreboardID,
|
||||
visible: false
|
||||
});
|
||||
|
||||
var bowPositions = [];
|
||||
var spawnPositions = [];
|
||||
for (var i = 0; i < TEMPLATES.length; ++i) {
|
||||
var template = TEMPLATES[i];
|
||||
|
||||
if (template.name === "SB.BowSpawn") {
|
||||
bowPositions.push(Vec3.sum(rootPosition, template.localPosition));
|
||||
Vec3.print("Pushing bow position", Vec3.sum(rootPosition, template.localPosition));
|
||||
} else if (template.name === "SB.EnemySpawn") {
|
||||
spawnPositions.push(Vec3.sum(rootPosition, template.localPosition));
|
||||
Vec3.print("Pushing spawnposition", Vec3.sum(rootPosition, template.localPosition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createLocalGame();
|
||||
Script.stop();
|
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 1/10/2017
|
||||
// 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
|
||||
//
|
||||
|
||||
/* globals utils */
|
||||
|
||||
(function() {
|
||||
Script.include('utils.js');
|
||||
|
||||
function StartButton() {
|
||||
}
|
||||
StartButton.prototype = {
|
||||
preload: function(entityID) {
|
||||
this.entityID = entityID;
|
||||
this.commChannel = "shortbow-" + Entities.getEntityProperties(entityID, 'parentID').parentID;
|
||||
Script.addEventHandler(entityID, "collisionWithEntity", this.onCollide.bind(this));
|
||||
},
|
||||
signalAC: function() {
|
||||
Messages.sendMessage(this.commChannel, JSON.stringify({
|
||||
type: 'start-game'
|
||||
}));
|
||||
},
|
||||
onCollide: function(entityA, entityB, collision) {
|
||||
var colliderName = Entities.getEntityProperties(entityB, 'name').name;
|
||||
|
||||
if (colliderName.indexOf("projectile") > -1) {
|
||||
this.signalAC();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
StartButton.prototype.startNearTrigger = StartButton.prototype.signalAC;
|
||||
StartButton.prototype.startFarTrigger = StartButton.prototype.signalAC;
|
||||
StartButton.prototype.clickDownOnEntity = StartButton.prototype.signalAC;
|
||||
|
||||
return new StartButton();
|
||||
});
|
57
unpublishedScripts/marketplace/shortbow/utils.js
Normal file
57
unpublishedScripts/marketplace/shortbow/utils.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// Created by Ryan Huffman on 1/10/2017
|
||||
// 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
|
||||
//
|
||||
|
||||
/* globals utils:true */
|
||||
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function(oThis) {
|
||||
if (typeof this !== 'function') {
|
||||
// closest thing possible to the ECMAScript 5
|
||||
// internal IsCallable function
|
||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
||||
}
|
||||
|
||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
||||
fToBind = this,
|
||||
NOP = function() {},
|
||||
fBound = function() {
|
||||
return fToBind.apply(this instanceof NOP
|
||||
? this
|
||||
: oThis,
|
||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
||||
};
|
||||
|
||||
if (this.prototype) {
|
||||
// Function.prototype doesn't have a prototype property
|
||||
NOP.prototype = this.prototype;
|
||||
}
|
||||
fBound.prototype = new NOP();
|
||||
|
||||
return fBound;
|
||||
};
|
||||
}
|
||||
|
||||
utils = {
|
||||
parseJSON: function(json) {
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (e) {
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
findSurfaceBelowPosition: function(pos) {
|
||||
var result = Entities.findRayIntersection({
|
||||
origin: pos,
|
||||
direction: { x: 0.0, y: -1.0, z: 0.0 }
|
||||
}, true);
|
||||
if (result.intersects) {
|
||||
return result.intersection;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
};
|
Loading…
Reference in a new issue