diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3b5a17ae92..ed09bb61f2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3431,7 +3431,7 @@ void Application::idle(float nsecsElapsed) { #ifdef Q_OS_WIN static std::once_flag once; std::call_once(once, [] { - initCpuUsage(); + initCpuUsage(); }); vec3 kernelUserAndSystem; diff --git a/unpublishedScripts/marketplace/shortbow/README.md b/unpublishedScripts/marketplace/shortbow/README.md new file mode 100644 index 0000000000..2e056fecfb --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/README.md @@ -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. + diff --git a/unpublishedScripts/marketplace/shortbow/bow/Arrow_impact1.L.wav b/unpublishedScripts/marketplace/shortbow/bow/Arrow_impact1.L.wav new file mode 100644 index 0000000000..30aede7a5a Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/Arrow_impact1.L.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/Bow_draw.1.L.wav b/unpublishedScripts/marketplace/shortbow/bow/Bow_draw.1.L.wav new file mode 100644 index 0000000000..254e20d937 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/Bow_draw.1.L.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/String_release2.L.wav b/unpublishedScripts/marketplace/shortbow/bow/String_release2.L.wav new file mode 100644 index 0000000000..4f3ad767a2 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/String_release2.L.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/arrow-sparkle.png b/unpublishedScripts/marketplace/shortbow/bow/arrow-sparkle.png new file mode 100644 index 0000000000..6663b2ff1e Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/arrow-sparkle.png differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/arrow.fbx b/unpublishedScripts/marketplace/shortbow/bow/arrow.fbx new file mode 100644 index 0000000000..533e8eb1f7 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/arrow.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/bow-deadly.fbx b/unpublishedScripts/marketplace/shortbow/bow/bow-deadly.fbx new file mode 100644 index 0000000000..51f0a25c73 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/bow-deadly.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/bow.js b/unpublishedScripts/marketplace/shortbow/bow/bow.js new file mode 100644 index 0000000000..f8ef025728 --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/bow/bow.js @@ -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; +}); diff --git a/unpublishedScripts/marketplace/shortbow/bow/bow.json b/unpublishedScripts/marketplace/shortbow/bow/bow.json new file mode 100644 index 0000000000..a510df729f --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/bow/bow.json @@ -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 +} diff --git a/unpublishedScripts/marketplace/shortbow/bow/bow.svo.json b/unpublishedScripts/marketplace/shortbow/bow/bow.svo.json new file mode 100644 index 0000000000..1ef66860a6 --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/bow/bow.svo.json @@ -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 +} \ No newline at end of file diff --git a/unpublishedScripts/marketplace/shortbow/bow/bow_collision_hull.obj b/unpublishedScripts/marketplace/shortbow/bow/bow_collision_hull.obj new file mode 100644 index 0000000000..d25786e74f --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/bow/bow_collision_hull.obj @@ -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 diff --git a/unpublishedScripts/marketplace/shortbow/bow/notch.wav b/unpublishedScripts/marketplace/shortbow/bow/notch.wav new file mode 100644 index 0000000000..2aa67bac33 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/bow/notch.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/bow/spawnBow.js b/unpublishedScripts/marketplace/shortbow/bow/spawnBow.js new file mode 100644 index 0000000000..cb94b05556 --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/bow/spawnBow.js @@ -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); diff --git a/unpublishedScripts/marketplace/shortbow/enemyClientEntity.js b/unpublishedScripts/marketplace/shortbow/enemyClientEntity.js new file mode 100644 index 0000000000..3abdaa46fb --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/enemyClientEntity.js @@ -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(); +}); diff --git a/unpublishedScripts/marketplace/shortbow/enemyServerEntity.js b/unpublishedScripts/marketplace/shortbow/enemyServerEntity.js new file mode 100644 index 0000000000..bd3f76c94e --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/enemyServerEntity.js @@ -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(); +}); diff --git a/unpublishedScripts/marketplace/shortbow/models/Amber.fbx b/unpublishedScripts/marketplace/shortbow/models/Amber.fbx new file mode 100644 index 0000000000..4da921f70a Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/Amber.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/shortbow-button.fbx b/unpublishedScripts/marketplace/shortbow/models/shortbow-button.fbx new file mode 100644 index 0000000000..817e39cc3c Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/shortbow-button.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.fbx b/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.fbx new file mode 100644 index 0000000000..b82755a8ca Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/shortbow-platform.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.fbx b/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.fbx new file mode 100644 index 0000000000..b689fe2eee Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/models/shortbow-scoreboard.fbx differ diff --git a/unpublishedScripts/marketplace/shortbow/shortbow.js b/unpublishedScripts/marketplace/shortbow/shortbow.js new file mode 100644 index 0000000000..641e9c45a6 --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/shortbow.js @@ -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; + } + } +} diff --git a/unpublishedScripts/marketplace/shortbow/shortbow.json b/unpublishedScripts/marketplace/shortbow/shortbow.json new file mode 100644 index 0000000000..47934baea5 --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/shortbow.json @@ -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 +} diff --git a/unpublishedScripts/marketplace/shortbow/shortbowGameManager.js b/unpublishedScripts/marketplace/shortbow/shortbowGameManager.js new file mode 100644 index 0000000000..bd42e40427 --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/shortbowGameManager.js @@ -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; + } + } + } +}; diff --git a/unpublishedScripts/marketplace/shortbow/shortbowServerEntity.js b/unpublishedScripts/marketplace/shortbow/shortbowServerEntity.js new file mode 100644 index 0000000000..385220717f --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/shortbowServerEntity.js @@ -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; + } + }; +}); diff --git a/unpublishedScripts/marketplace/shortbow/sounds/escape.wav b/unpublishedScripts/marketplace/shortbow/sounds/escape.wav new file mode 100644 index 0000000000..b576703916 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/escape.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/sounds/explosion.wav b/unpublishedScripts/marketplace/shortbow/sounds/explosion.wav new file mode 100644 index 0000000000..13ee9993e6 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/explosion.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/sounds/fight.wav b/unpublishedScripts/marketplace/shortbow/sounds/fight.wav new file mode 100644 index 0000000000..439684fe70 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/fight.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/sounds/gameOn.wav b/unpublishedScripts/marketplace/shortbow/sounds/gameOn.wav new file mode 100644 index 0000000000..8da091b1e3 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/gameOn.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/sounds/gameOver.wav b/unpublishedScripts/marketplace/shortbow/sounds/gameOver.wav new file mode 100644 index 0000000000..04eb9fd7ee Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/gameOver.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/sounds/letTheGamesBegin.wav b/unpublishedScripts/marketplace/shortbow/sounds/letTheGamesBegin.wav new file mode 100644 index 0000000000..c8884a04ea Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/letTheGamesBegin.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/sounds/spawn.wav b/unpublishedScripts/marketplace/shortbow/sounds/spawn.wav new file mode 100644 index 0000000000..ad6579993f Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/spawn.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/sounds/targetHit.wav b/unpublishedScripts/marketplace/shortbow/sounds/targetHit.wav new file mode 100644 index 0000000000..5357fc94a6 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/targetHit.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/sounds/tenSecondsRemaining.wav b/unpublishedScripts/marketplace/shortbow/sounds/tenSecondsRemaining.wav new file mode 100644 index 0000000000..b5375b3024 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/tenSecondsRemaining.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/sounds/waveComplete.wav b/unpublishedScripts/marketplace/shortbow/sounds/waveComplete.wav new file mode 100644 index 0000000000..5fb9f27b45 Binary files /dev/null and b/unpublishedScripts/marketplace/shortbow/sounds/waveComplete.wav differ diff --git a/unpublishedScripts/marketplace/shortbow/spawnShortbow.js b/unpublishedScripts/marketplace/shortbow/spawnShortbow.js new file mode 100644 index 0000000000..6b0be7f7f5 --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/spawnShortbow.js @@ -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(); diff --git a/unpublishedScripts/marketplace/shortbow/startGameButtonClientEntity.js b/unpublishedScripts/marketplace/shortbow/startGameButtonClientEntity.js new file mode 100644 index 0000000000..c15b93c047 --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/startGameButtonClientEntity.js @@ -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(); +}); diff --git a/unpublishedScripts/marketplace/shortbow/utils.js b/unpublishedScripts/marketplace/shortbow/utils.js new file mode 100644 index 0000000000..3ef82dcc13 --- /dev/null +++ b/unpublishedScripts/marketplace/shortbow/utils.js @@ -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; + } +};