This commit is contained in:
Leonardo Murillo 2015-12-02 10:42:57 -06:00
commit f5dfc7d3e4
40 changed files with 32483 additions and 171 deletions

View file

@ -12,7 +12,6 @@
#include <QThread>
#include <GLMHelpers.h>
#include "ScriptableAvatar.h"
// hold and priority unused but kept so that client side JS can run.
@ -48,14 +47,12 @@ AnimationDetails ScriptableAvatar::getAnimationDetails() {
}
void ScriptableAvatar::update(float deltatime) {
if (_bind.isNull() && !_skeletonFBXURL.isEmpty()) { // AvatarData will parse the .fst, but not get the .fbx skeleton.
_bind = DependencyManager::get<AnimationCache>()->getAnimation(_skeletonFBXURL);
}
// Run animation
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0) {
QStringList modelJoints = getJointNames();
QStringList animationJoints = _animation->getJointNames();
if (_jointData.size() != modelJoints.size()) {
_jointData.resize(modelJoints.size());
}
if (_animation && _animation->isLoaded() && _animation->getFrames().size() > 0 && _bind->isLoaded()) {
float currentFrame = _animationDetails.currentFrame + deltatime * _animationDetails.fps;
if (_animationDetails.loop || currentFrame < _animationDetails.lastFrame) {
@ -63,19 +60,29 @@ void ScriptableAvatar::update(float deltatime) {
currentFrame -= (_animationDetails.lastFrame - _animationDetails.firstFrame);
}
_animationDetails.currentFrame = currentFrame;
const QVector<FBXJoint>& modelJoints = _bind->getGeometry().joints;
QStringList animationJointNames = _animation->getJointNames();
if (_jointData.size() != modelJoints.size()) {
_jointData.resize(modelJoints.size());
}
const int frameCount = _animation->getFrames().size();
const FBXAnimationFrame& floorFrame = _animation->getFrames().at((int)glm::floor(currentFrame) % frameCount);
const FBXAnimationFrame& ceilFrame = _animation->getFrames().at((int)glm::ceil(currentFrame) % frameCount);
const float frameFraction = glm::fract(currentFrame);
for (int i = 0; i < animationJoints.size(); i++) {
const QString& name = animationJoints[i];
int mapping = getJointIndex(name);
for (int i = 0; i < animationJointNames.size(); i++) {
const QString& name = animationJointNames[i];
// As long as we need the model preRotations anyway, let's get the jointIndex from the bind skeleton rather than
// trusting the .fst (which is sometimes not updated to match changes to .fbx).
int mapping = _bind->getGeometry().getJointIndex(name);
if (mapping != -1 && !_maskedJoints.contains(name)) {
JointData& data = _jointData[mapping];
auto newRotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
auto newRotation = modelJoints[mapping].preRotation *
safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
// We could probably do translations as in interpolation in model space (rather than the parent space that each frame is in),
// but we don't do so for MyAvatar yet, so let's not be different here.
if (data.rotation != newRotation) {

View file

@ -33,6 +33,7 @@ private:
AnimationPointer _animation;
AnimationDetails _animationDetails;
QStringList _maskedJoints;
AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies
};
#endif // hifi_ScriptableAvatar_h

View file

@ -43,6 +43,7 @@ void OctreeInboundPacketProcessor::resetStats() {
_totalPackets = 0;
_lastNackTime = usecTimestampNow();
QWriteLocker locker(&_senderStatsLock);
_singleSenderStats.clear();
}
@ -220,6 +221,8 @@ void OctreeInboundPacketProcessor::trackInboundPacket(const QUuid& nodeUUID, uns
_totalElementsInPacket += editsInPacket;
_totalPackets++;
QWriteLocker locker(&_senderStatsLock);
// find the individual senders stats and track them there too...
// see if this is the first we've heard of this node...
if (_singleSenderStats.find(nodeUUID) == _singleSenderStats.end()) {
@ -242,6 +245,8 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
int packetsSent = 0;
int totalBytesSent = 0;
QWriteLocker locker(&_senderStatsLock);
NodeToSenderStatsMapIterator i = _singleSenderStats.begin();
while (i != _singleSenderStats.end()) {
@ -262,10 +267,9 @@ int OctreeInboundPacketProcessor::sendNackPackets() {
}
const SharedNodePointer& destinationNode = DependencyManager::get<NodeList>()->nodeWithUUID(nodeUUID);
// If the node no longer exists, wait until the ReceivedPacketProcessor has cleaned up the node
// to remove it from our stats list.
// FIXME Is it safe to clean it up here before ReceivedPacketProcess has?
// if the node no longer exists, remove its stats
if (!destinationNode) {
i = _singleSenderStats.erase(i);
continue;
}

View file

@ -72,7 +72,7 @@ public:
void resetStats();
NodeToSenderStatsMap& getSingleSenderStats() { return _singleSenderStats; }
NodeToSenderStatsMap getSingleSenderStats() { QReadLocker locker(&_senderStatsLock); return _singleSenderStats; }
virtual void terminating() { _shuttingDown = true; ReceivedPacketProcessor::terminating(); }
@ -94,15 +94,16 @@ private:
OctreeServer* _myServer;
int _receivedPacketCount;
quint64 _totalTransitTime;
quint64 _totalProcessTime;
quint64 _totalLockWaitTime;
quint64 _totalElementsInPacket;
quint64 _totalPackets;
std::atomic<uint64_t> _totalTransitTime;
std::atomic<uint64_t> _totalProcessTime;
std::atomic<uint64_t> _totalLockWaitTime;
std::atomic<uint64_t> _totalElementsInPacket;
std::atomic<uint64_t> _totalPackets;
NodeToSenderStatsMap _singleSenderStats;
QReadWriteLock _senderStatsLock;
quint64 _lastNackTime;
std::atomic<uint64_t> _lastNackTime;
bool _shuttingDown;
};
#endif // hifi_OctreeInboundPacketProcessor_h

View file

@ -711,7 +711,7 @@ bool OctreeServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url
int senderNumber = 0;
NodeToSenderStatsMap& allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
NodeToSenderStatsMap allSenderStats = _octreeInboundPacketProcessor->getSingleSenderStats();
for (NodeToSenderStatsMapConstIterator i = allSenderStats.begin(); i != allSenderStats.end(); i++) {
senderNumber++;
QUuid senderID = i.key();

View file

@ -1,6 +1,6 @@
"use strict";
/*jslint vars: true, plusplus: true*/
/*global Agent, Avatar, Script, Entities, Vec3, print*/
/*global Agent, Avatar, Script, Entities, Vec3, Quat, print*/
//
// animatedAvatar.js
// examples/acScripts
@ -16,15 +16,62 @@
var origin = {x: 500, y: 500, z: 500};
var spread = 20; // meters
var turnSpread = 90; // How many degrees should turn from front range over.
var animationData = {url: "https://hifi-public.s3.amazonaws.com/ozan/anim/standard_anims/walk_fwd.fbx", lastFrame: 35};
Avatar.skeletonModelURL = "https://hifi-public.s3.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst"; //lovejoy
Avatar.displayName = "'Bot";
var models = [ // Commented-out avatars do not animate properly on AC's. Presumably because ScriptableAvatar doesn't use model pre-rotations.
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/alan/alan.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/andrew/andrew.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/austin/austin.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/birarda/birarda.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/brad/brad.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/chris/chris2.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/clement/clement.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/emily/emily.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/ewing/ewing.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/howard/howard.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/huffman/huffman.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/james/james.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/philip/philip.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/ryan/ryan.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/sam/sam.fst",
"https://hifi-public.s3.amazonaws.com/ozan/avatars/hifi_team/tony/tony.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/1e57c395-612e-4acd-9561-e79dbda0bc49/faafb83c63a3e5e265884d270fc29f8b.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/615ca447-06ee-4215-8dd1-a609c2fcd0cd/c7af6d4224c501fdd9cb54e0101ff281.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/731c39d7-559a-4ce2-947c-3e2768f5471c/8d5eba2fd5bf068259556aec1861c5dd.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/8bdaa1ec-99df-4a29-a249-6941c7fd1930/37351a18a3dea468088fc3d822aaf29c.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/0909d7b7-c67e-45fb-acd9-a07380dc6b9c/da76b8c59dbc680bdda90df4b9a46faa.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/ad0dffd7-f811-487b-a20a-2509235491ef/29106da1f7e6a42c7907603421fd7df5.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/3ebe5c84-8b88-4d91-86ac-f104f3620fe3/3534b032d079514aa8960a316500ce13.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/dd03b8e3-52fb-4ab3-9ac9-3b17e00cd85d/98baa90b3b66803c5d7bd4537fca6993.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/ff060580-2fc7-4b6c-8e12-f023d05363cf/dadef29b1e60f23b413d1850d7e0dd4a.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/b55d3baf-4eb3-4cac-af4c-0fb66d0c907b/ad2c9157f3924ab1f7f6ea87a8cce6cc.fst",
"https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/contents/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73/4618d52e711fbb34df442b414da767bb.fst"
];
var nameMap = {
faafb83c63a3e5e265884d270fc29f8b: 'albert',
c7af6d4224c501fdd9cb54e0101ff281: 'boss',
'8d5eba2fd5bf068259556aec1861c5dd': 'dougland',
'37351a18a3dea468088fc3d822aaf29c': 'fightbot blue',
da76b8c59dbc680bdda90df4b9a46faa: 'judd',
'29106da1f7e6a42c7907603421fd7df5': 'kate',
'3534b032d079514aa8960a316500ce13': 'lenny',
'98baa90b3b66803c5d7bd4537fca6993': 'lovejoy',
dadef29b1e60f23b413d1850d7e0dd4a: 'mery', // eyes no good
ad2c9157f3924ab1f7f6ea87a8cce6cc: 'owen',
'4618d52e711fbb34df442b414da767bb': 'will'
};
Avatar.skeletonModelURL = models[Math.round(Math.random() * (models.length - 1))]; // pick one
Avatar.displayName = Avatar.skeletonModelURL.match(/\/(\w*).fst/)[1]; // grab the file basename
Avatar.displayName = nameMap[Avatar.displayName] || Avatar.displayName;
var millisecondsToWaitBeforeStarting = 10 * 1000; // To give the various servers a chance to start.
Agent.isAvatar = true;
function coord() { return (Math.random() * spread) - (spread / 2); } // randomly distribute a coordinate zero += spread/2.
Script.setTimeout(function () {
Avatar.position = Vec3.sum(origin, {x: coord(), y: 0, z: coord()});
Avatar.orientation = Quat.fromPitchYawRollDegrees(0, turnSpread * (Math.random() - 0.5), 0);
print("Starting at", JSON.stringify(Avatar.position));
Avatar.startAnimation(animationData.url, animationData.fps || 30, 1, true, false, animationData.firstFrame || 0, animationData.lastFrame);
}, millisecondsToWaitBeforeStarting);

View file

@ -200,6 +200,7 @@ function entityIsGrabbedByOther(entityID) {
return false;
}
function MyController(hand) {
this.hand = hand;
if (this.hand === RIGHT_HAND) {
@ -223,6 +224,8 @@ function MyController(hand) {
this.rawTriggerValue = 0;
this.rawBumperValue = 0;
this.overlayLine = null;
this.offsetPosition = {
x: 0.0,
y: 0.0,
@ -318,6 +321,33 @@ function MyController(hand) {
});
}
this.overlayLineOn = function(closePoint, farPoint, color) {
if (this.overlayLine === null) {
var lineProperties = {
lineWidth: 5,
start: closePoint,
end: farPoint,
color: color,
ignoreRayIntersection: true, // always ignore this
visible: true,
alpha: 1
};
this.overlayLine = Overlays.addOverlay("line3d", lineProperties);
} else {
var success = Overlays.editOverlay(this.overlayLine, {
lineWidth: 5,
start: closePoint,
end: farPoint,
color: color,
visible: true,
ignoreRayIntersection: true, // always ignore this
alpha: 1
});
}
}
this.lineOn = function(closePoint, farPoint, color) {
// draw a line
if (this.pointer === null) {
@ -356,6 +386,13 @@ function MyController(hand) {
this.pointer = null;
};
this.overlayLineOff = function() {
if (this.overlayLine !== null) {
Overlays.deleteOverlay(this.overlayLine);
}
this.overlayLine = null;
};
this.triggerPress = function(value) {
_this.rawTriggerValue = value;
};
@ -479,7 +516,11 @@ function MyController(hand) {
this.setState(STATE_NEAR_GRABBING);
} else { // equipping
if (typeof grabbableData.spatialKey !== 'undefined') {
this.setState(STATE_EQUIP_SPRING);
// TODO
// if we go to STATE_EQUIP_SPRING the item will be pulled to the hand and will then switch
// to STATE_EQUIP. This needs some debugging, so just jump straight to STATE_EQUIP here.
// this.setState(STATE_EQUIP_SPRING);
this.setState(STATE_EQUIP);
} else {
this.setState(STATE_EQUIP);
}
@ -493,7 +534,9 @@ function MyController(hand) {
this.grabbedEntity = intersection.entityID;
if (typeof grabbableData.spatialKey !== 'undefined' && this.state == STATE_EQUIP_SEARCHING) {
// if a distance pick in equip mode hits something with a spatialKey, equip it
this.setState(STATE_EQUIP_SPRING);
// TODO use STATE_EQUIP_SPRING here once it works right.
// this.setState(STATE_EQUIP_SPRING);
this.setState(STATE_EQUIP);
return;
} else if (this.state == STATE_SEARCHING) {
this.setState(STATE_DISTANCE_HOLDING);
@ -598,7 +641,8 @@ function MyController(hand) {
}
}
this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
//this.lineOn(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH), NO_INTERSECT_COLOR);
this.overlayLineOn(distantPickRay.origin, Vec3.sum(distantPickRay.origin, Vec3.multiply(distantPickRay.direction, LINE_LENGTH)), NO_INTERSECT_COLOR);
};
this.distanceHolding = function() {
@ -644,6 +688,9 @@ function MyController(hand) {
this.currentAvatarPosition = MyAvatar.position;
this.currentAvatarOrientation = MyAvatar.orientation;
this.overlayLineOff();
};
this.continueDistanceHolding = function() {
@ -751,6 +798,7 @@ function MyController(hand) {
}
this.lineOff();
this.overlayLineOff();
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
this.activateEntity(this.grabbedEntity, grabbedProperties);
@ -802,6 +850,7 @@ function MyController(hand) {
// equipping
Entities.callEntityMethod(this.grabbedEntity, "startEquip", [JSON.stringify(this.hand)]);
this.startHandGrasp();
this.setState(STATE_CONTINUE_EQUIP_BD);
}
@ -885,11 +934,13 @@ function MyController(hand) {
Entities.callEntityMethod(this.grabbedEntity, "releaseGrab");
Entities.callEntityMethod(this.grabbedEntity, "unequip");
this.endHandGrasp();
}
};
this.pullTowardEquipPosition = function() {
this.lineOff();
this.overlayLineOff();
var grabbedProperties = Entities.getEntityProperties(this.grabbedEntity, GRABBABLE_PROPERTIES);
var grabbableData = getEntityCustomData(GRABBABLE_DATA_KEY, this.grabbedEntity, DEFAULT_GRABBABLE_DATA);
@ -1093,7 +1144,7 @@ function MyController(hand) {
this.release = function() {
this.lineOff();
this.overlayLineOff();
if (this.grabbedEntity !== null) {
if (this.actionID !== null) {
Entities.deleteAction(this.grabbedEntity, this.actionID);
@ -1215,10 +1266,10 @@ Controller.enableMapping(MAPPING_NAME);
var handToDisable = 'none';
function update() {
if (handToDisable !== LEFT_HAND) {
if (handToDisable !== LEFT_HAND && handToDisable!=='both') {
leftController.update();
}
if (handToDisable !== RIGHT_HAND) {
if (handToDisable !== RIGHT_HAND && handToDisable!=='both') {
rightController.update();
}
}
@ -1226,15 +1277,20 @@ function update() {
Messages.subscribe('Hifi-Hand-Disabler');
handleHandDisablerMessages = function(channel, message, sender) {
if (sender === MyAvatar.sessionUUID) {
handToDisable = message;
if (message === 'left') {
handToDisable = LEFT_HAND;
}
if (message === 'right') {
handToDisable = RIGHT_HAND;
}
if(message==='both'){
handToDisable='both';
}
if(message==='none'){
handToDisable='none';
}
}
}
@ -1248,4 +1304,4 @@ function cleanup() {
}
Script.scriptEnding.connect(cleanup);
Script.update.connect(update);
Script.update.connect(update);

View file

@ -22,8 +22,10 @@ var laserPointer = (function () {
];
function isHandPointing(hand) {
var MINIMUM_TRIGGER_PULL = 0.9;
return Controller.getTriggerValue(hand) > MINIMUM_TRIGGER_PULL;
var MINIMUM_TRIGGER_PULL = 0.9,
controller;
controller = hand === 0 ? Controller.Standard.LT : Controller.Standard.RT;
return Controller.getValue(controller) > MINIMUM_TRIGGER_PULL;
}
function isFingerPointing(hand) {

View file

@ -193,13 +193,12 @@ var leapHands = (function () {
}
// Set avatar arms vertical, forearms horizontal, as "zero" position for calibration
MyAvatar.setJointData("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, -90.0));
MyAvatar.setJointData("LeftForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0));
MyAvatar.setJointData("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointData("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 90.0));
MyAvatar.setJointData("RightForeArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 180.0));
MyAvatar.setJointData("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointRotation("LeftArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
MyAvatar.setJointRotation("LeftForeArm", Quat.fromPitchYawRollDegrees(0.0, 90.0, 90.0));
MyAvatar.setJointRotation("LeftHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
MyAvatar.setJointRotation("RightArm", Quat.fromPitchYawRollDegrees(90.0, 0.0, 0.0));
MyAvatar.setJointRotation("RightForeArm", Quat.fromPitchYawRollDegrees(0.0, -90.0, -90.0));
MyAvatar.setJointRotation("RightHand", Quat.fromPitchYawRollRadians(0.0, 0.0, 0.0));
// Wait for arms to assume their positions before calculating
Script.setTimeout(finishCalibration, CALIBRATION_TIME);
@ -382,23 +381,14 @@ var leapHands = (function () {
// Hand rotation in camera coordinates ...
handRotation = {
x: handRotation.z,
y: handRotation.y,
z: handRotation.x,
x: -handRotation.x,
y: -handRotation.z,
z: -handRotation.y,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
if (h === 0) {
handRotation.x = -handRotation.x;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 0, z: 1 }), handRotation);
} else {
handRotation.z = -handRotation.z;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 0, z: 1 }), handRotation);
}
handRotation = Quat.multiply(Quat.angleAxis(180.0, { x: 0, y: 1, z: 0 }), handRotation);
cameraOrientation.x = -cameraOrientation.x;
cameraOrientation.z = -cameraOrientation.z;
handRotation = Quat.multiply(cameraOrientation, handRotation);
@ -421,22 +411,14 @@ var leapHands = (function () {
// Hand rotation in camera coordinates ...
handRotation = {
x: handRotation.z,
y: handRotation.y,
z: handRotation.x,
x: -handRotation.x,
y: -handRotation.z,
z: -handRotation.y,
w: handRotation.w
};
// Hand rotation in avatar coordinates ...
if (h === 0) {
handRotation.x = -handRotation.x;
handRotation = Quat.multiply(Quat.angleAxis(-90.0, { x: 0, y: 1, z: 0 }),
handRotation);
} else {
handRotation.z = -handRotation.z;
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 0, y: 1, z: 0 }),
handRotation);
}
handRotation = Quat.multiply(Quat.angleAxis(90.0, { x: 1, y: 0, z: 0 }), handRotation);
}
// Set hand position and orientation ...
@ -462,7 +444,7 @@ var leapHands = (function () {
w: locRotation.w
};
}
MyAvatar.setJointData(fingers[h][i][j].jointName, locRotation);
MyAvatar.setJointRotation(fingers[h][i][j].jointName, locRotation);
}
}
}

View file

@ -0,0 +1 @@
/node_modules

View file

@ -0,0 +1 @@
web: node app.js

View file

@ -0,0 +1,5 @@
This gameserver sets up a server with websockets that listen for messages from interface regarding when users shoot rats, and updates a real-time game board with that information. This is just a first pass, and the plan is to abstract this to work with any kind of game content creators wish to make with High Fidelity.
To enter the game: Run pistol.js and shoot at rats.
For every rat you kill, you get a point.
You're score will be displayed at https://desolate-bastion-1742.herokuapp.com/

View file

@ -0,0 +1,76 @@
'use strict';
/**
* Module dependencies.
*/
var express = require('express');
var http = require('http');
var _ = require('underscore');
var shortid = require('shortid');
var app = express();
var server = http.createServer(app);
var WebSocketServer = require('websocket').server;
var wsServer = new WebSocketServer({
httpServer: server
});
var users = [];
var connections = [];
wsServer.on('request', function(request) {
console.log("SOMEONE JOINED");
var connection = request.accept(null, request.origin);
connections.push(connection);
connection.on('message', function(data) {
var userData = JSON.parse(data.utf8Data);
var user = _.find(users, function(user) {
return user.username === userData.username;
});
if (user) {
// This user already exists, so just update score
users[users.indexOf(user)].score = userData.score;
} else {
users.push({
id: shortid.generate(),
username: userData.username,
score: userData.score
});
}
connections.forEach(function(aConnection) {
aConnection.sendUTF(JSON.stringify({
users: users
}));
})
});
});
app.get('/users', function(req, res) {
res.send({
users: users
});
});
/* Configuration */
app.set('views', __dirname + '/views');
app.use(express.static(__dirname + '/public'));
app.set('port', (process.env.PORT || 5000));
if (process.env.NODE_ENV === 'development') {
app.use(express.errorHandler({
dumpExceptions: true,
showStack: true
}));
}
/* Start server */
server.listen(app.get('port'), function() {
console.log('Express server listening on port %d in %s mode', app.get('port'), app.get('env'));
});
module.exports = app;

View file

@ -0,0 +1,87 @@
'use strict';
var React = require('react');
var _ = require('underscore')
var $ = require('jquery');
var UserList = React.createClass({
render: function(){
var sortedUsers = _.sortBy(this.props.data.users, function(users){
//Show higher scorers at top of board
return 1 - users.score;
});
var users = sortedUsers.map(function(user) {
return (
<User username = {user.username} score = {user.score} key = {user.id}></User>
)
});
return (
<div>{users}</div>
)
}
});
var GameBoard = React.createClass({
loadDataFromServer: function(data) {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: {users: []}};
},
componentDidMount: function() {
this.loadDataFromServer();
//set up web socket
var path = window.location.hostname + ":" + window.location.port;
console.log("LOCATION ", path)
var socketClient = new WebSocket("wss://" + path);
var self = this;
socketClient.onopen = function() {
console.log("CONNECTED");
socketClient.onmessage = function(data) {
console.log("ON MESSAGE");
self.setState({data: JSON.parse(data.data)});
};
};
},
render: function() {
return (
<div>
<div className = "gameTitle">Kill All The Rats!</div>
<div className = "boardHeader">
<div className="username">PLAYER</div>
<div className="score" > SCORE </div>
</div>
<UserList data ={this.state.data}/>
</div>
);
}
});
var User = React.createClass({
render: function() {
return (
<div className = "entry">
<div className="username"> {this.props.username} </div>
<div className="score" > {this.props.score} </div>
</div>
);
}
})
React.render(
<GameBoard url = "/users" />,
document.getElementById('app')
);

View file

@ -0,0 +1,15 @@
var gulp = require('gulp');
var exec = require('child_process').exec;
gulp.task('build', function() {
exec('npm run build', function(msg){
console.log(msg);
});
});
gulp.task('watch', function() {
gulp.watch('client/*.jsx', ['build']);
});
gulp.task('default', ['build', 'watch'])

View file

@ -0,0 +1,21 @@
{
"name": "KillAllTheRats",
"version": "0.6.9",
"scripts": {
"build": "browserify ./client/app.jsx -t babelify --outfile ./public/js/app.js",
"start": "node app.js"
},
"dependencies": {
"express": "^4.13.1",
"gulp": "^3.9.0",
"jquery": "^2.1.4",
"react": "^0.13.3",
"shortid": "^2.2.4",
"underscore": "^1.8.3",
"websocket": "^1.0.22"
},
"devDependencies": {
"babelify": "^6.1.3",
"browserify": "^10.2.6"
}
}

View file

@ -0,0 +1,36 @@
body {
font-family: Impact;
background-color: #009DC0 ;
font-size: 60px;
}
.gameTitle {
color: #D61010;
}
.entry{
width:100%;
height:50px;
border:1px solid #A9D1E1;
color: white;
margin-right:10px;
padding: 10px;
float:left;
font-size: 40px;
}
.boardHeader{
width:100%;
height:50px;
border:5px solid #A9D1E1;
color: white;
margin-right:10px;
padding: 10px;
float:left;
font-size: 40px;
}
.username{
font-weight: bold;
float: left;
margin-right: 50%;
}

View file

@ -0,0 +1,12 @@
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link rel="stylesheet" href="css/style.css">
<title>Kill The Rats!</title>
</head>
<body>
<div id="app"></div>
<script src="js/app.js"></script>
</body>
</html>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,477 @@
//
// pistol.js
// examples
//
// Created by Eric Levin on 11/12/2015
// Copyright 2013 High Fidelity, Inc.
//
// This is an example script that turns the hydra controllers and mouse into a entity gun.
// It reads the controller, watches for trigger pulls, and adds a force to any entity it hits
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
Script.include("../../../libraries/utils.js");
Script.include("../../../libraries/constants.js");
var GUN_FORCE =20;
Messages.sendMessage('Hifi-Hand-Disabler', "both");
var gameName = "Kill All The Rats!"
// var HOST = "localhost:5000"
var HOST = "desolate-bastion-1742.herokuapp.com";
var socketClient = new WebSocket("ws://" + HOST);
var username = GlobalServices.username;
var currentScore = 0;
function score() {
currentScore++;
socketClient.send(JSON.stringify({
username: username,
score: currentScore,
gameName: gameName
}))
}
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var fireSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/GUN-SHOT2.raw");
var LASER_LENGTH = 100;
var LASER_WIDTH = 2;
var POSE_CONTROLS = [Controller.Standard.LeftHand, Controller.Standard.RightHand];
var TRIGGER_CONTROLS = [Controller.Standard.LT, Controller.Standard.RT];
var MIN_THROWER_DELAY = 1000;
var MAX_THROWER_DELAY = 1000;
var RELOAD_INTERVAL = 5;
var GUN_MODEL = HIFI_PUBLIC_BUCKET + "cozza13/gun/m1911-handgun+1.fbx?v=4";
var BULLET_VELOCITY = 10.0;
var GUN_OFFSETS = [{
x: 0.04,
y: 0.26,
z: 0.04
}, {
x: 0.04,
y: 0.26,
z: 0.04
}];
var GUN_ORIENTATIONS = [Quat.fromPitchYawRollDegrees(0, 90, 90), Quat.fromPitchYawRollDegrees(0, -90, 270)];
//x -> y
//y -> z
// z -> x
var BARREL_OFFSETS = [ {
x: -0.12,
y: 0.12,
z: 0.04
}, {
x: 0.12,
y: 0.12,
z: 0.04
} ];
var pointers = [];
pointers.push(Overlays.addOverlay("line3d", {
start: ZERO_VECTOR,
end: ZERO_VECTOR,
color: COLORS.RED,
alpha: 1,
visible: true,
lineWidth: LASER_WIDTH
}));
pointers.push(Overlays.addOverlay("line3d", {
start: ZERO_VECTOR,
end: ZERO_VECTOR,
color: COLORS.RED,
alpha: 1,
visible: true,
lineWidth: LASER_WIDTH
}));
var mapping = Controller.newMapping();
var validPoses = [false, false];
var barrelVectors = [0, 0];
var barrelTips = [0, 0];
// If enabled, anything can be shot, otherwise, an entity needs to have "isShootable" set in its userData
var shootAnything = true;
function update(deltaTime) {
// FIXME we should also expose MyAvatar.handPoses[2], MyAvatar.tipPoses[2]
var tipPoses = [MyAvatar.leftHandTipPose, MyAvatar.rightHandTipPose];
for (var side = 0; side < 2; side++) {
// First check if the controller is valid
var controllerPose = Controller.getPoseValue(POSE_CONTROLS[side]);
validPoses[side] = controllerPose.valid;
// Need to adjust the laser
var tipPose = tipPoses[side];
var handRotation = tipPoses[side].rotation;
var barrelOffset = Vec3.multiplyQbyV(handRotation, BARREL_OFFSETS[side]);
barrelTips[side] = Vec3.sum(tipPose.translation, barrelOffset);
barrelVectors[side] = Vec3.multiplyQbyV(handRotation, {
x: 0,
y: 1,
z: 0
});
var laserTip = Vec3.sum(Vec3.multiply(LASER_LENGTH, barrelVectors[side]), barrelTips[side]);
// Update Lasers
Overlays.editOverlay(pointers[side], {
start: barrelTips[side],
end: laserTip,
alpha: 1,
});
}
}
function displayPointer(side) {
Overlays.editOverlay(pointers[side], {
visible: true
});
}
function hidePointer(side) {
Overlays.editOverlay(pointers[side], {
visible: false
});
}
function fire(side, value) {
if (value == 0) {
return;
}
Audio.playSound(fireSound, {
position: barrelTips[side],
volume: 0.5
});
var shotDirection = Vec3.normalize(barrelVectors[side]);
var pickRay = {
origin: barrelTips[side],
direction: shotDirection
};
createMuzzleFlash(barrelTips[side]);
var intersection = Entities.findRayIntersectionBlocking(pickRay, true);
if (intersection.intersects) {
Script.setTimeout(function() {
createEntityHitEffect(intersection.intersection);
if (shootAnything && intersection.properties.collisionsWillMove === 1) {
// Any entity with collisions will move can be shot
Entities.editEntity(intersection.entityID, {
velocity: Vec3.multiply(shotDirection, GUN_FORCE)
});
}
if (intersection.properties.name === "rat") {
score();
createBloodSplatter(intersection.intersection);
Entities.deleteEntity(intersection.entityID);
}
//Attempt to call entity method's shot method
var forceDirection = JSON.stringify({
forceDirection: shotDirection
});
Entities.callEntityMethod(intersection.entityID, 'onShot', [forceDirection]);
}, 0);
}
}
function scriptEnding() {
Messages.sendMessage('Hifi-Hand-Disabler', 'none');
mapping.disable();
for (var i = 0; i < pointers.length; ++i) {
Overlays.deleteOverlay(pointers[i]);
}
MyAvatar.detachOne(GUN_MODEL);
MyAvatar.detachOne(GUN_MODEL);
clearPose();
}
MyAvatar.attach(GUN_MODEL, "LeftHand", GUN_OFFSETS[0], GUN_ORIENTATIONS[0], 0.40);
MyAvatar.attach(GUN_MODEL, "RightHand", GUN_OFFSETS[1], GUN_ORIENTATIONS[1], 0.40);
function showPointer(side) {
Overlays.editOverlay(pointers[side], {
visible: true
});
}
mapping.from(Controller.Standard.LT).hysteresis(0.0, 0.5).to(function(value) {
fire(0, value);
});
mapping.from(Controller.Standard.RT).hysteresis(0.0, 0.5).to(function(value) {
fire(1, value);
});
mapping.enable();
Script.scriptEnding.connect(scriptEnding);
Script.update.connect(update);
function createEntityHitEffect(position) {
var flash = Entities.addEntity({
type: "ParticleEffect",
position: position,
lifetime: 4,
"name": "Flash Emitter",
"color": {
red: 228,
green: 128,
blue: 12
},
"maxParticles": 1000,
"lifespan": 0.15,
"emitRate": 1000,
"emitSpeed": 1,
"speedSpread": 0,
"emitOrientation": {
"x": -0.4,
"y": 1,
"z": -0.2,
"w": 0.7071068286895752
},
"emitDimensions": {
"x": 0,
"y": 0,
"z": 0
},
"polarStart": 0,
"polarFinish": Math.PI,
"azimuthStart": -3.1415927410125732,
"azimuthFinish": 2,
"emitAcceleration": {
"x": 0,
"y": 0,
"z": 0
},
"accelerationSpread": {
"x": 0,
"y": 0,
"z": 0
},
"particleRadius": 0.03,
"radiusSpread": 0.02,
"radiusStart": 0.02,
"radiusFinish": 0.03,
"colorSpread": {
red: 100,
green: 100,
blue: 20
},
"alpha": 1,
"alphaSpread": 0,
"alphaStart": 0,
"alphaFinish": 0,
"additiveBlending": true,
"textures": "http://ericrius1.github.io/PartiArt/assets/star.png"
});
Script.setTimeout(function() {
Entities.editEntity(flash, {
isEmitting: false
});
}, 100);
}
function createBloodSplatter(position) {
var splatter = Entities.addEntity({
type: "ParticleEffect",
position: position,
lifetime: 4,
"name": "Blood Splatter",
"color": {
red: 230,
green: 2,
blue: 30
},
"maxParticles": 1000,
"lifespan": 0.3,
"emitRate": 1000,
"emitSpeed": 0.5,
"speedSpread": 0,
"emitOrientation": {
"x": -0.4,
"y": 1,
"z": -0.2,
"w": 0.7071068286895752
},
"emitDimensions": {
"x": 0,
"y": 0,
"z": 0
},
"polarStart": 0,
"polarFinish": Math.PI,
"azimuthStart": -3.1415927410125732,
"azimuthFinish": 2,
"emitAcceleration": {
"x": 0,
"y": -5,
"z": 0
},
"accelerationSpread": {
"x": 0,
"y": 0,
"z": 0
},
"particleRadius": 0.05,
"radiusSpread": 0.03,
"radiusStart": 0.05,
"radiusFinish": 0.05,
"colorSpread": {
red: 40,
green: 0,
blue: 30
},
"alpha": 1,
"alphaSpread": 0,
"alphaStart": 0,
"alphaFinish": 0,
"textures": "http://ericrius1.github.io/PartiArt/assets/star.png"
});
Script.setTimeout(function() {
Entities.editEntity(splatter, {
isEmitting: false
});
}, 100)
}
function createMuzzleFlash(position) {
var smoke = Entities.addEntity({
type: "ParticleEffect",
position: position,
lifetime: 1,
"name": "Smoke Hit Emitter",
"maxParticles": 1000,
"lifespan": 4,
"emitRate": 20,
emitSpeed: 0,
"speedSpread": 0,
"emitDimensions": {
"x": 0,
"y": 0,
"z": 0
},
"polarStart": 0,
"polarFinish": 0,
"azimuthStart": -3.1415927410125732,
"azimuthFinish": 3.14,
"emitAcceleration": {
"x": 0,
"y": 0.5,
"z": 0
},
"accelerationSpread": {
"x": .2,
"y": 0,
"z": .2
},
"radiusSpread": .04,
"particleRadius": 0.07,
"radiusStart": 0.07,
"radiusFinish": 0.07,
"alpha": 0.7,
"alphaSpread": 0,
"alphaStart": 0,
"alphaFinish": 0,
"additiveBlending": 0,
"textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png"
});
Script.setTimeout(function() {
Entities.editEntity(smoke, {
isEmitting: false
});
}, 100);
var flash = Entities.addEntity({
type: "ParticleEffect",
position: position,
lifetime: 4,
"name": "Muzzle Flash",
"color": {
red: 228,
green: 128,
blue: 12
},
"maxParticles": 1000,
"lifespan": 0.1,
"emitRate": 1000,
"emitSpeed": 0.5,
"speedSpread": 0,
"emitOrientation": {
"x": -0.4,
"y": 1,
"z": -0.2,
"w": 0.7071068286895752
},
"emitDimensions": {
"x": 0,
"y": 0,
"z": 0
},
"polarStart": 0,
"polarFinish": Math.PI,
"azimuthStart": -3.1415927410125732,
"azimuthFinish": 2,
"emitAcceleration": {
"x": 0,
"y": 0,
"z": 0
},
"accelerationSpread": {
"x": 0,
"y": 0,
"z": 0
},
"particleRadius": 0.05,
"radiusSpread": 0.01,
"radiusStart": 0.05,
"radiusFinish": 0.05,
"colorSpread": {
red: 100,
green: 100,
blue: 20
},
"alpha": 1,
"alphaSpread": 0,
"alphaStart": 0,
"alphaFinish": 0,
"additiveBlending": true,
"textures": "http://ericrius1.github.io/PartiArt/assets/star.png"
});
Script.setTimeout(function() {
Entities.editEntity(flash, {
isEmitting: false
});
}, 100)
}

View file

@ -0,0 +1,37 @@
//
// Rat.js
// examples/toybox/entityScripts
//
// Created by Eric Levin on11/11/15.
// 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 print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
(function() {
var scriptURL = Script.resolvePath('pistol.js');
var _this;
PistolScriptSpawner = function() {
_this = this;
this.forceMultiplier = 1;
};
PistolScriptSpawner.prototype = {
enterEntity: function() {
Script.load(scriptURL);
},
preload: function(entityID) {
this.entityID = entityID;
},
};
// entity scripts always need to return a newly constructed object of our type
return new PistolScriptSpawner();
});

View file

@ -0,0 +1,31 @@
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
var scriptURL = Script.resolvePath("pistolScriptSpawner.js");
var modelURL = "http://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx";
var pistolSpawnerEntity = Entities.addEntity({
type: 'Box',
position: center,
dimensions: {x: 0.38, y: 1.9, z: 3.02},
script: scriptURL,
visible: false,
ignoreForCollisions: true
});
var pistol = Entities.addEntity({
type: 'Model',
modelURL: modelURL,
position: center,
dimensions: {x: 0.38, y: 1.9, z: 3.02},
script: scriptURL,
color: {red: 200, green: 0, blue: 20},
ignoreForCollisions: true
});
function cleanup() {
Entities.deleteEntity(pistolSpawnerEntity);
Entities.deleteEntity(pistol);
}
// Script.update.connect(update);
Script.scriptEnding.connect(cleanup);

View file

@ -67,12 +67,12 @@ var colorPalette = [{
var MIN_STROKE_WIDTH = 0.002;
var MAX_STROKE_WIDTH = 0.05;
function controller(side, cycleColorButton) {
function controller(side, triggerAction) {
this.triggerHeld = false;
this.triggerThreshold = 0.9;
this.side = side;
this.trigger = side == LEFT ? Controller.Stantard.LT : Controller.Standard.RT;
this.cycleColorButton = side == LEFT ? Controller.Stantard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb;
this.triggerAction = triggerAction;
this.cycleColorButton = side == LEFT ? Controller.Standard.LeftPrimaryThumb : Controller.Standard.RightPrimaryThumb;
this.points = [];
this.normals = [];
@ -116,6 +116,7 @@ function controller(side, cycleColorButton) {
y: LINE_DIMENSIONS,
z: LINE_DIMENSIONS
},
textures: "http://localhost:8080/trails.png",
lifetime: LIFETIME
});
this.points = [];
@ -174,7 +175,7 @@ function controller(side, cycleColorButton) {
this.cycleColorButtonPressed = Controller.getValue(this.cycleColorButton);
this.palmPosition = this.side == RIGHT ? MyAvatar.rightHandPose.translation : MyAvatar.leftHandPose.translation;
this.tipPosition = this.side == RIGHT ? MyAvatar.rightHandTipPose.translation : MyAvatar.leftHandTipPose.translation;
this.triggerValue = Controller.getValue(this.trigger);
this.triggerValue = Controller.getActionValue(this.triggerAction);
if (this.prevCycleColorButtonPressed === true && this.cycleColorButtonPressed === false) {
@ -212,8 +213,8 @@ function vectorIsZero(v) {
}
var rightController = new controller(RIGHT);
var leftController = new controller(LEFT);
var rightController = new controller(RIGHT, Controller.findAction("RIGHT_HAND_CLICK"));
var leftController = new controller(LEFT, Controller.findAction("LEFT_HAND_CLICK"));
Script.update.connect(update);
Script.scriptEnding.connect(scriptEnding);

View file

@ -1,81 +1,138 @@
(function () {
(function() {
var spawnPoint = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getFront(Camera.getOrientation())));
// constructor
function TestBox() {
this.entity = Entities.addEntity({ type: "Box",
position: spawnPoint,
dimentions: { x: 1, y: 1, z: 1 },
color: { red: 100, green: 100, blue: 255 },
gravity: { x: 0, y: 0, z: 0 },
visible: true,
locked: false,
lifetime: 6000});
this.entity = Entities.addEntity({
type: "Box",
position: spawnPoint,
dimentions: {
x: 1,
y: 1,
z: 1
},
color: {
red: 100,
green: 100,
blue: 255
},
gravity: {
x: 0,
y: 0,
z: 0
},
visible: true,
locked: false,
lifetime: 6000
});
var self = this;
this.timer = Script.setInterval(function () {
var colorProp = { color: { red: Math.random() * 255,
green: Math.random() * 255,
blue: Math.random() * 255 } };
this.timer = Script.setInterval(function() {
var colorProp = {
color: {
red: Math.random() * 255,
green: Math.random() * 255,
blue: Math.random() * 255
}
};
Entities.editEntity(self.entity, colorProp);
}, 1000);
}
TestBox.prototype.Destroy = function () {
TestBox.prototype.Destroy = function() {
Script.clearInterval(this.timer);
Entities.editEntity(this.entity, { locked: false });
Entities.editEntity(this.entity, {
locked: false
});
Entities.deleteEntity(this.entity);
}
// constructor
function TestFx(color, emitDirection, emitRate, emitStrength, blinkRate) {
var PI = 3.141593;
var DEG_TO_RAD = PI / 180.0;
this.entity = Entities.addEntity({ type: "ParticleEffect",
isEmitting: true,
position: spawnPoint,
dimensions: {x: 2, y: 2, z: 2},
emitSpeed: 0.05,
maxParticles: 2,
speedSpread: 2,
polarFinish: 30 * DEG_TO_RAD,
emitAcceleration: {x: 0, y: -9.8, z: 0},
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
color: color,
lifespan: 1.0,
visible: true,
locked: false });
this.entity = Entities.addEntity({
type: "ParticleEffect",
isEmitting: true,
position: spawnPoint,
dimensions: {
x: 2,
y: 2,
z: 2
},
emitSpeed: 0.05,
maxParticles: 2,
speedSpread: 2,
polarFinish: 30 * DEG_TO_RAD,
emitAcceleration: {
x: 0,
y: -9.8,
z: 0
},
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
color: color,
lifespan: 1.0,
visible: true,
locked: false
});
this.isPlaying = true;
var self = this;
this.timer = Script.setInterval(function () {
this.timer = Script.setInterval(function() {
// flip is playing state
self.isPlaying = !self.isPlaying;
var emittingProp = { isEmitting: self.isPlaying };
var emittingProp = {
isEmitting: self.isPlaying
};
Entities.editEntity(self.entity, emittingProp);
}, (1 / blinkRate) * 1000);
}
TestFx.prototype.Destroy = function () {
TestFx.prototype.Destroy = function() {
Script.clearInterval(this.timer);
Entities.editEntity(this.entity, { locked: false });
Entities.editEntity(this.entity, {
locked: false
});
Entities.deleteEntity(this.entity);
}
var objs = [];
function Init() {
objs.push(new TestBox());
objs.push(new TestFx({ red: 255, green: 0, blue: 0 },
{ x: 0.5, y: 1.0, z: 0.0 },
100, 3, 1));
objs.push(new TestFx({ red: 0, green: 255, blue: 0 },
{ x: 0, y: 1, z: 0 },
1000, 5, 0.5));
objs.push(new TestFx({ red: 0, green: 0, blue: 255 },
{ x: -0.5, y: 1, z: 0 },
100, 3, 1));
objs.push(new TestFx({
red: 255,
green: 0,
blue: 0
}, {
x: 0.5,
y: 1.0,
z: 0.0
},
100, 3, 1));
objs.push(new TestFx({
red: 0,
green: 255,
blue: 0
}, {
x: 0,
y: 1,
z: 0
},
1000, 5, 0.5));
objs.push(new TestFx({
red: 0,
green: 0,
blue: 255
}, {
x: -0.5,
y: 1,
z: 0
},
100, 3, 1));
}
function ShutDown() {
@ -88,6 +145,4 @@
Init();
Script.scriptEnding.connect(ShutDown);
})();
})();

View file

@ -36,14 +36,10 @@
Entities.editEntity(this.entityID, {
animation: {
url: "https://hifi-public.s3.amazonaws.com/models/Bboys/zombie_scream.fbx",
currentFrame: 0
running: true
}
});
Entities.editEntity(_this.entityID, {
animationIsPlaying: true
});
var position = Entities.getEntityProperties(this.entityID, "position").position;
this.audioInjector = Audio.playSound(this.screamSounds[randInt(0, this.screamSounds.length)], {
position: position,
@ -67,8 +63,10 @@
this.audioInjector.stop();
Entities.editEntity(this.entityID, {
animation: {
url: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx",
currentFrame: 0
// Providing actual model fbx for animation used to work, now contorts doll into a weird ball
// See bug: https://app.asana.com/0/26225263936266/70097355490098
// url: "http://hifi-public.s3.amazonaws.com/models/Bboys/bboy2/bboy2.fbx",
running: false,
}
});

View file

@ -0,0 +1,46 @@
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(1.5, Quat.getFront(Camera.getOrientation())));
var scriptURL = Script.resolvePath('pistol.js');
var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/gun.fbx";
var pistol = Entities.addEntity({
type: 'Model',
modelURL: modelURL,
position: center,
dimensions: {
x: 0.05,
y: .23,
z: .36
},
script: scriptURL,
color: {
red: 200,
green: 0,
blue: 20
},
shapeType: 'box',
collisionsWillMove: true,
gravity: {x: 0, y: -5.0, z: 0},
restitution: 0,
collisionSoundURL: "https://s3.amazonaws.com/hifi-public/sounds/Guns/Gun_Drop_and_Metalli_1.wav",
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
relativePosition: {
x: 0,
y: 0,
z: 0
},
relativeRotation: Quat.fromPitchYawRollDegrees(45, 90, 0)
},
invertSolidWhileHeld: true
}
})
});
function cleanup() {
Entities.deleteEntity(pistol);
}
Script.scriptEnding.connect(cleanup);

View file

@ -0,0 +1,354 @@
//
// pistol.js
// examples/toybox/entityScripts
//
// Created by Eric Levin on11/11/15.
// 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 print, MyAvatar, Entities, AnimationCache, SoundCache, Scene, Camera, Overlays, Audio, HMD, AvatarList, AvatarManager, Controller, UndoStack, Window, Account, GlobalServices, Script, ScriptDiscoveryService, LODManager, Menu, Vec3, Quat, AudioDevice, Paths, Clipboard, Settings, XMLHttpRequest, randFloat, randInt */
(function() {
Script.include("../../libraries/utils.js");
Script.include("../../libraries/constants.js");
var _this;
// if the trigger value goes below this while held, the can will stop spraying. if it goes above, it will spray
var DISABLE_LASER_THRESHOLD = 0.2;
var TRIGGER_CONTROLS = [
Controller.Standard.LT,
Controller.Standard.RT,
];
var RELOAD_THRESHOLD = 0.95;
Pistol = function() {
_this = this;
this.equipped = false;
this.forceMultiplier = 1;
this.laserLength = 100;
this.laserOffsets = {
y: .095
};
this.firingOffsets = {
z: 0.16
}
this.fireSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/GUN-SHOT2.raw");
this.ricochetSound = SoundCache.getSound("https://s3.amazonaws.com/hifi-public/sounds/Guns/Ricochet.L.wav");
this.playRichochetSoundChance = 0.1;
this.fireVolume = 0.2;
this.bulletForce = 10;
this.showLaser = false;
};
Pistol.prototype = {
canShoot: false,
startEquip: function(id, params) {
this.equipped = true;
this.hand = JSON.parse(params[0]);
},
continueNearGrab: function() {
if (!this.equipped) {
return;
}
this.toggleWithTriggerPressure();
if (this.showLaser) {
this.updateLaser();
}
},
toggleWithTriggerPressure: function() {
this.triggerValue = Controller.getValue(TRIGGER_CONTROLS[this.hand]);
if (this.triggerValue < RELOAD_THRESHOLD) {
// print('RELOAD');
this.canShoot = true;
}
if (this.canShoot === true && this.triggerValue === 1) {
// print('SHOOT');
this.fire();
this.canShoot = false;
}
if (this.triggerValue < DISABLE_LASER_THRESHOLD && this.showLaser === true) {
this.showLaser = false;
Overlays.editOverlay(this.laser, {
visible: false
});
} else if (this.triggerValue >= DISABLE_LASER_THRESHOLD && this.showLaser === false) {
this.showLaser = true
Overlays.editOverlay(this.laser, {
visible: true
});
}
},
updateLaser: function() {
var gunProps = Entities.getEntityProperties(this.entityID, ['position', 'rotation']);
var position = gunProps.position;
var rotation = gunProps.rotation;
this.firingDirection = Quat.getFront(rotation);
var upVec = Quat.getUp(rotation);
this.barrelPoint = Vec3.sum(position, Vec3.multiply(upVec, this.laserOffsets.y));
var laserTip = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.laserLength));
this.barrelPoint = Vec3.sum(this.barrelPoint, Vec3.multiply(this.firingDirection, this.firingOffsets.z))
Overlays.editOverlay(this.laser, {
start: this.barrelPoint,
end: laserTip,
alpha: 1
});
},
unequip: function() {
this.hand = null;
this.equipped = false;
Overlays.editOverlay(this.laser, {
visible: false
});
},
preload: function(entityID) {
this.entityID = entityID;
// this.initControllerMapping();
this.laser = Overlays.addOverlay("line3d", {
start: ZERO_VECTOR,
end: ZERO_VECTOR,
color: COLORS.RED,
alpha: 1,
visible: true,
lineWidth: 2
});
},
triggerPress: function(hand, value) {
if (this.hand === hand && value === 1) {
//We are pulling trigger on the hand we have the gun in, so fire
this.fire();
}
},
fire: function() {
var pickRay = {
origin: this.barrelPoint,
direction: this.firingDirection
};
Audio.playSound(this.fireSound, {
position: this.barrelPoint,
volume: this.fireVolume
});
this.createGunFireEffect(this.barrelPoint)
var intersection = Entities.findRayIntersectionBlocking(pickRay, true);
if (intersection.intersects) {
this.createEntityHitEffect(intersection.intersection);
if (Math.random() < this.playRichochetSoundChance) {
Script.setTimeout(function() {
Audio.playSound(_this.ricochetSound, {
position: intersection.intersection,
volume: _this.fireVolume
});
}, randFloat(10, 200));
}
if (intersection.properties.collisionsWillMove === 1) {
// Any entity with collisions will move can be shot
Entities.editEntity(intersection.entityID, {
velocity: Vec3.multiply(this.firingDirection, this.bulletForce)
});
}
}
},
unload: function() {
Overlays.deleteOverlay(this.laser);
},
createEntityHitEffect: function(position) {
var flash = Entities.addEntity({
type: "ParticleEffect",
position: position,
lifetime: 4,
"name": "Flash Emitter",
"color": {
red: 228,
green: 128,
blue: 12
},
"maxParticles": 1000,
"lifespan": 0.15,
"emitRate": 1000,
"emitSpeed": 1,
"speedSpread": 0,
"emitOrientation": {
"x": -0.4,
"y": 1,
"z": -0.2,
"w": 0.7071068286895752
},
"emitDimensions": {
"x": 0,
"y": 0,
"z": 0
},
"polarStart": 0,
"polarFinish": Math.PI,
"azimuthStart": -3.1415927410125732,
"azimuthFinish": 2,
"emitAcceleration": {
"x": 0,
"y": 0,
"z": 0
},
"accelerationSpread": {
"x": 0,
"y": 0,
"z": 0
},
"particleRadius": 0.03,
"radiusSpread": 0.02,
"radiusStart": 0.02,
"radiusFinish": 0.03,
"colorSpread": {
red: 100,
green: 100,
blue: 20
},
"alpha": 1,
"alphaSpread": 0,
"alphaStart": 0,
"alphaFinish": 0,
"additiveBlending": true,
"textures": "http://ericrius1.github.io/PartiArt/assets/star.png"
});
Script.setTimeout(function() {
Entities.editEntity(flash, {
isEmitting: false
});
}, 100);
},
createGunFireEffect: function(position) {
var smoke = Entities.addEntity({
type: "ParticleEffect",
position: position,
lifetime: 1,
"name": "Smoke Hit Emitter",
"maxParticles": 1000,
"lifespan": 4,
"emitRate": 20,
emitSpeed: 0,
"speedSpread": 0,
"emitDimensions": {
"x": 0,
"y": 0,
"z": 0
},
"polarStart": 0,
"polarFinish": 0,
"azimuthStart": -3.1415927410125732,
"azimuthFinish": 3.14,
"emitAcceleration": {
"x": 0,
"y": 0.5,
"z": 0
},
"accelerationSpread": {
"x": .2,
"y": 0,
"z": .2
},
"radiusSpread": .04,
"particleRadius": 0.07,
"radiusStart": 0.07,
"radiusFinish": 0.07,
"alpha": 0.7,
"alphaSpread": 0,
"alphaStart": 0,
"alphaFinish": 0,
"additiveBlending": 0,
"textures": "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png"
});
Script.setTimeout(function() {
Entities.editEntity(smoke, {
isEmitting: false
});
}, 100);
var flash = Entities.addEntity({
type: "ParticleEffect",
position: position,
lifetime: 4,
"name": "Muzzle Flash",
"color": {
red: 228,
green: 128,
blue: 12
},
"maxParticles": 1000,
"lifespan": 0.1,
"emitRate": 1000,
"emitSpeed": 0.5,
"speedSpread": 0,
"emitOrientation": {
"x": -0.4,
"y": 1,
"z": -0.2,
"w": 0.7071068286895752
},
"emitDimensions": {
"x": 0,
"y": 0,
"z": 0
},
"polarStart": 0,
"polarFinish": Math.PI,
"azimuthStart": -3.1415927410125732,
"azimuthFinish": 2,
"emitAcceleration": {
"x": 0,
"y": 0,
"z": 0
},
"accelerationSpread": {
"x": 0,
"y": 0,
"z": 0
},
"particleRadius": 0.05,
"radiusSpread": 0.01,
"radiusStart": 0.05,
"radiusFinish": 0.05,
"colorSpread": {
red: 100,
green: 100,
blue: 20
},
"alpha": 1,
"alphaSpread": 0,
"alphaStart": 0,
"alphaFinish": 0,
"additiveBlending": true,
"textures": "http://ericrius1.github.io/PartiArt/assets/star.png"
});
Script.setTimeout(function() {
Entities.editEntity(flash, {
isEmitting: false
});
}, 100)
}
};
// entity scripts always need to return a newly constructed object of our type
return new Pistol();
});

View file

@ -0,0 +1,117 @@
Script.include("../libraries/utils.js");
var shootingRangeURL = "https://s3.amazonaws.com/hifi-public/eric/models/shootingRange/shootingRange.fbx";
var floorURL = "https://s3.amazonaws.com/hifi-public/eric/models/shootingRange/shootingRange.fbx";
MyAvatar.bodyYaw = 0;
var rangePosition = Vec3.sum(MyAvatar.position, {
x: 0,
y: 0,
z: -30
});
var rangeDimensions = {
x: 44,
y: 29,
z: 96
}
var shootingRange = Entities.addEntity({
type: 'Model',
modelURL: shootingRangeURL,
position: rangePosition,
dimensions: rangeDimensions
});
var floorPosition = Vec3.subtract(rangePosition, {
x: 0,
y: 2,
z: 0
});
var shootingRangeFloor = Entities.addEntity({
type: "Model",
modelURL: floorURL,
shapeType: 'box',
position: floorPosition,
dimensions: {
x: 93,
y: 1,
z: 93
}
})
var monsters = [];
var numMonsters = 10;
var monsterURLS = ["https://s3.amazonaws.com/hifi-public/eric/models/shootingRange/monster1.fbx", "https://s3.amazonaws.com/hifi-public/eric/models/shootingRange/monster2.fbx"]
initMonsters();
function initMonsters() {
for (var i = 0; i < numMonsters; i++) {
var index = randInt(0, monsterURLS.length);
var monsterURL = monsterURLS[index]
var monsterPosition = Vec3.sum(rangePosition, {
x: -rangeDimensions.x / 2 - i * randFloat(5, 10),
y: 0,
z: randFloat(-10, 10)
});
var monster = Entities.addEntity({
type: "Model",
modelURL: monsterURL,
position: monsterPosition,
dimensions: {
x: 1.5,
y: 1.6,
z: 0.07
},
collisionsWillMove: true,
shapeType: 'box',
velocity: {
x: randFloat(1, 3),
y: 0,
z: 0
},
damping: 0
});
monsters.push(monster);
}
}
function checkMonsters() {
// check monsters to see if they've gone out of bounds, if so, set them back to starting point
monsters.forEach(function(monster) {
var position = Entities.getEntityProperties(monster, "position").position;
if (position.x > rangePosition.x + rangeDimensions.x / 2 ||
position.z < rangePosition.z - rangeDimensions.z / 2 ||
position.y < rangePosition.y - rangeDimensions.y / 2 || position.y > rangePosition.y + rangeDimensions.y / 2) {
var monsterPosition = Vec3.sum(rangePosition, {
x: -rangeDimensions.x / 2 - randFloat(5, 10),
y: 0,
z: randFloat(-10, 10)
});
Entities.editEntity(monster, {
position: monsterPosition,
velocity: {
x: randFloat(1, 3),
y: 0,
z: 0
},
angularVelocity: {x: 0, y: 0, z:0},
rotation: Quat.fromPitchYawRollDegrees(0, 0, 0),
});
}
});
}
var checkMonsterInterval = Script.setInterval(checkMonsters, 1000);
function cleanup() {
Script.clearInterval(checkMonsterInterval);
Entities.deleteEntity(shootingRange);
Entities.deleteEntity(shootingRangeFloor);
monsters.forEach(function(monster) {
Entities.deleteEntity(monster);
});
}
Script.scriptEnding.connect(cleanup);

View file

@ -2856,6 +2856,8 @@ void Application::update(float deltaTime) {
}
}
_controllerScriptingInterface->updateInputControllers();
// Transfer the user inputs to the driveKeys
// FIXME can we drop drive keys and just have the avatar read the action states directly?
myAvatar->clearDriveKeys();

View file

@ -106,6 +106,7 @@ void AvatarActionHold::updateActionWorker(float deltaTimeStep) {
doKinematicUpdate(deltaTimeStep);
} else {
activateBody();
forceBodyNonStatic();
ObjectActionSpring::updateActionWorker(deltaTimeStep);
}
}
@ -156,6 +157,7 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) {
});
activateBody();
forceBodyNonStatic();
}
bool AvatarActionHold::updateArguments(QVariantMap arguments) {
@ -328,5 +330,6 @@ void AvatarActionHold::deserialize(QByteArray serializedArguments) {
_active = true;
});
activateBody();
forceBodyNonStatic();
}

View file

@ -44,9 +44,11 @@ void AvatarUpdate::synchronousProcess() {
bool AvatarUpdate::process() {
PerformanceTimer perfTimer("AvatarUpdate");
quint64 start = usecTimestampNow();
quint64 deltaMicroseconds = 0;
quint64 deltaMicroseconds = 10000;
if (_lastAvatarUpdate > 0) {
deltaMicroseconds = start - _lastAvatarUpdate;
} else {
deltaMicroseconds = 10000; // 10 ms
}
float deltaSeconds = (float) deltaMicroseconds / (float) USECS_PER_SECOND;
_lastAvatarUpdate = start;

View file

@ -84,13 +84,12 @@ glm::vec2 ControllerScriptingInterface::getViewportDimensions() const {
return qApp->getUiSize();
}
controller::InputController::Pointer ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
// This is where we retreive the Device Tracker category and then the sub tracker within it
controller::InputController* ControllerScriptingInterface::createInputController(const QString& deviceName, const QString& tracker) {
// This is where we retrieve the Device Tracker category and then the sub tracker within it
auto icIt = _inputControllers.find(0);
if (icIt != _inputControllers.end()) {
return (*icIt).second;
}
return (*icIt).second.get();
}
// Look for device
DeviceTracker::ID deviceID = DeviceTracker::getDeviceID(deviceName.toStdString());
@ -110,18 +109,24 @@ controller::InputController::Pointer ControllerScriptingInterface::createInputCo
controller::InputController::Pointer inputController = std::make_shared<InputController>(deviceID, trackerID, this);
controller::InputController::Key key = inputController->getKey();
_inputControllers.insert(InputControllerMap::value_type(key, inputController));
return inputController;
return inputController.get();
}
}
}
return controller::InputController::Pointer();
return nullptr;
}
void ControllerScriptingInterface::releaseInputController(controller::InputController::Pointer input) {
void ControllerScriptingInterface::releaseInputController(controller::InputController* input) {
_inputControllers.erase(input->getKey());
}
void ControllerScriptingInterface::updateInputControllers() {
for (auto it = _inputControllers.begin(); it != _inputControllers.end(); it++) {
(*it).second->update();
}
}
InputController::InputController(int deviceTrackerId, int subTrackerId, QObject* parent) :
_deviceTrackerId(deviceTrackerId),
_subTrackerId(subTrackerId),

View file

@ -85,6 +85,8 @@ public:
bool isKeyCaptured(const KeyEvent& event) const;
bool isJoystickCaptured(int joystickIndex) const;
void updateInputControllers();
public slots:
virtual void captureKeyEvents(const KeyEvent& event);
@ -96,8 +98,8 @@ public slots:
virtual glm::vec2 getViewportDimensions() const;
/// Factory to create an InputController
virtual controller::InputController::Pointer createInputController(const QString& deviceName, const QString& tracker);
virtual void releaseInputController(controller::InputController::Pointer input);
virtual controller::InputController* createInputController(const QString& deviceName, const QString& tracker);
virtual void releaseInputController(controller::InputController* input);
signals:
void keyPressEvent(const KeyEvent& event);

View file

@ -116,7 +116,7 @@ void AvatarData::setOrientation(const glm::quat& orientation, bool overideRefere
glm::vec3 eulerAngles = glm::degrees(safeEulerAngles(orientation));
_bodyPitch = eulerAngles.x;
_bodyYaw = eulerAngles.y;
_bodyRoll = eulerAngles.z;
_bodyRoll = eulerAngles.z;
}
}
@ -1212,7 +1212,14 @@ void AvatarData::setJointMappingsFromNetworkReply() {
QByteArray line;
while (!(line = networkReply->readLine()).isEmpty()) {
if (!(line = line.trimmed()).startsWith("jointIndex")) {
line = line.trimmed();
if (line.startsWith("filename")) {
int filenameIndex = line.indexOf('=') + 1;
if (filenameIndex > 0) {
_skeletonFBXURL = _skeletonModelURL.resolved(QString(line.mid(filenameIndex).trimmed()));
}
}
if (!line.startsWith("jointIndex")) {
continue;
}
int jointNameIndex = line.indexOf('=') + 1;
@ -1522,6 +1529,17 @@ QJsonObject AvatarData::toJson() const {
}
void AvatarData::fromJson(const QJsonObject& json) {
// The head setOrientation likes to overwrite the avatar orientation,
// so lets do the head first
// Most head data is relative to the avatar, and needs no basis correction,
// but the lookat vector does need correction
if (json.contains(JSON_AVATAR_HEAD)) {
if (!_headData) {
_headData = new HeadData(this);
}
_headData->fromJson(json[JSON_AVATAR_HEAD].toObject());
}
if (json.contains(JSON_AVATAR_HEAD_MODEL)) {
auto faceModelURL = json[JSON_AVATAR_HEAD_MODEL].toString();
if (faceModelURL != getFaceModelURL().toString()) {
@ -1593,15 +1611,6 @@ void AvatarData::fromJson(const QJsonObject& json) {
}
setRawJointData(jointArray);
}
// Most head data is relative to the avatar, and needs no basis correction,
// but the lookat vector does need correction
if (json.contains(JSON_AVATAR_HEAD)) {
if (!_headData) {
_headData = new HeadData(this);
}
_headData->fromJson(json[JSON_AVATAR_HEAD].toObject());
}
}
// Every frame will store both a basis for the recording and a relative transform

View file

@ -392,6 +392,7 @@ protected:
QUrl _faceModelURL; // These need to be empty so that on first time setting them they will not short circuit
QUrl _skeletonModelURL; // These need to be empty so that on first time setting them they will not short circuit
QUrl _skeletonFBXURL;
QVector<AttachmentData> _attachmentData;
QString _displayName;

View file

@ -248,7 +248,7 @@ QByteArray OctreePersistThread::getPersistFileContents() const {
}
void OctreePersistThread::persist() {
if (_tree->isDirty()) {
if (_tree->isDirty() && _initialLoadComplete) {
_tree->withWriteLock([&] {
qCDebug(octree) << "pruning Octree before saving...";

View file

@ -81,6 +81,9 @@ void avatarDataFromScriptValue(const QScriptValue &object, AvatarData* &out) {
out = qobject_cast<AvatarData*>(object.toQObject());
}
Q_DECLARE_METATYPE(controller::InputController*)
static int inputControllerPointerId = qRegisterMetaType<controller::InputController*>();
QScriptValue inputControllerToScriptValue(QScriptEngine *engine, controller::InputController* const &in) {
return engine->newQObject(in);
}
@ -89,8 +92,6 @@ void inputControllerFromScriptValue(const QScriptValue &object, controller::Inpu
out = qobject_cast<controller::InputController*>(object.toQObject());
}
static bool hasCorrectSyntax(const QScriptProgram& program) {
const auto syntaxCheck = QScriptEngine::checkSyntax(program.sourceCode());
if (syntaxCheck.state() != QScriptSyntaxCheckResult::Valid) {

View file

@ -14,6 +14,7 @@
var _this;
var gunScriptURL = Script.resolvePath("../examples/toybox/pistol/pistol.js");
var sprayPaintScriptURL = Script.resolvePath("../examples/toybox/spray_paint/sprayPaintCan.js");
var catScriptURL = Script.resolvePath("../examples/toybox/cat/cat.js");
var flashlightScriptURL = Script.resolvePath('../examples/toybox/flashlight/flashlight.js');
@ -85,6 +86,12 @@
z: 505.09
});
createGun({
x: 546.2,
y: 495.5,
z: 505.2
});
createWand({
x: 546.71,
y: 495.55,
@ -125,11 +132,12 @@
createLights();
createCat({
x: 551.09,
y: 494.98,
z: 503.49
x: 551.0,
y: 495.3,
z: 503.3
});
createSprayCan({
x: 549.7,
y: 495.6,
@ -152,6 +160,52 @@
});
}
function createGun(position) {
var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/gun.fbx";
var pistol = Entities.addEntity({
type: 'Model',
name: "pistol",
modelURL: modelURL,
position: position,
collisionSoundURL: "https://s3.amazonaws.com/hifi-public/sounds/Guns/Gun_Drop_and_Metalli_1.wav",
dimensions: {
x: 0.05,
y: 0.23,
z: 0.36
},
script: gunScriptURL,
color: {
red: 200,
green: 0,
blue: 20
},
shapeType: 'box',
gravity: {
x: 0,
y: -3.0,
z: 0
},
collisionsWillMove: true,
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
relativePosition: {
x: 0,
y: 0,
z: 0
},
relativeRotation: Quat.fromPitchYawRollDegrees(45, 90, 0)
},
invertSolidWhileHeld: true
},
resetMe: {
resetMe: true
}
})
});
}
function createBow() {
var startPosition = {
@ -187,6 +241,7 @@
gravity: BOW_GRAVITY,
shapeType: 'compound',
compoundShapeURL: COLLISION_HULL_URL,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/bow_fall.L.wav",
script: bowScriptURL,
userData: JSON.stringify({
resetMe: {
@ -595,6 +650,7 @@
z: 0.08
},
collisionsWillMove: true,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/flashlight_drop.L.wav",
gravity: {
x: 0,
y: -3.5,
@ -1131,10 +1187,11 @@
z: 0.07
},
collisionsWillMove: true,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/SpryPntCnDrp1.L.wav",
shapeType: 'box',
gravity: {
x: 0,
y: -0.5,
y: -3.0,
z: 0
},
velocity: {
@ -1328,4 +1385,4 @@
};
// entity scripts always need to return a newly constructed object of our type
return new ResetSwitch();
});
});

View file

@ -20,17 +20,29 @@ function createHiddenMasterSwitch() {
type: "Box",
name: "Master Switch",
script: hiddenEntityScriptURL,
dimensions: {x: 0.7, y: 0.2, z: 0.1},
position: {x: 543.9, y: 496.05, z: 502.43},
dimensions: {
x: 0.7,
y: 0.2,
z: 0.1
},
position: {
x: 543.9,
y: 496.05,
z: 502.43
},
rotation: Quat.fromPitchYawRollDegrees(0, 33, 0),
visible: false
visible: false,
userData: JSON.stringify({
grabbableKey: {
wantsTrigger: true
}
})
});
}
var entities = Entities.findEntities(MyAvatar.position, 100);
entities.forEach(function(entity) {
//params: customKey, id, defaultValue
var name = Entities.getEntityProperties(entity, "name").name
if (name === "Master Switch") {
Entities.deleteEntity(entity);

View file

@ -14,6 +14,7 @@
var utilitiesScript = Script.resolvePath("../examples/libraries/utils.js");
Script.include(utilitiesScript);
var gunScriptURL = Script.resolvePath("../examples/toybox/pistol/pistol.js");
var sprayPaintScriptURL = Script.resolvePath("../examples/toybox/spray_paint/sprayPaintCan.js");
var catScriptURL = Script.resolvePath("../examples/toybox/cat/cat.js");
var flashlightScriptURL = Script.resolvePath('../examples/toybox/flashlight/flashlight.js');
@ -58,6 +59,13 @@ MasterReset = function() {
z: 505.09
});
createGun({
x: 546.2,
y: 495.5,
z: 505.2
});
createWand({
x: 546.71,
y: 495.55,
@ -102,20 +110,19 @@ MasterReset = function() {
createCat({
x: 551.09,
y: 494.98,
z: 503.49
x: 551.0,
y: 495.3,
z: 503.3
});
createSprayCan({
x: 549.7,
y: 495.6,
z: 503.91
});
createBow();
createBow();
}
function deleteAllToys() {
@ -130,6 +137,54 @@ MasterReset = function() {
});
}
function createGun(position) {
var modelURL = "https://s3.amazonaws.com/hifi-public/eric/models/gun.fbx";
var pistol = Entities.addEntity({
type: 'Model',
name: "pistol",
modelURL: modelURL,
position: position,
dimensions: {
x: 0.05,
y: 0.23,
z: 0.36
},
script: gunScriptURL,
color: {
red: 200,
green: 0,
blue: 20
},
shapeType: 'box',
gravity: {
x: 0,
y: -5.0,
z: 0
},
restitution: 0,
collisionsWillMove: true,
collisionSoundURL: "https://s3.amazonaws.com/hifi-public/sounds/Guns/Gun_Drop_and_Metalli_1.wav",
userData: JSON.stringify({
grabbableKey: {
spatialKey: {
relativePosition: {
x: 0,
y: 0,
z: -0.1
},
relativeRotation: Quat.fromPitchYawRollDegrees(100, 90, 0)
},
invertSolidWhileHeld: true
},
resetMe: {
resetMe: true
}
})
});
}
function createBow() {
var startPosition = {
@ -165,6 +220,7 @@ MasterReset = function() {
gravity: BOW_GRAVITY,
shapeType: 'compound',
compoundShapeURL: COLLISION_HULL_URL,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/bow_fall.L.wav",
script: bowScriptURL,
userData: JSON.stringify({
resetMe: {
@ -183,7 +239,6 @@ MasterReset = function() {
}
})
});
}
function createFire() {
@ -575,6 +630,7 @@ MasterReset = function() {
z: 0.08
},
collisionsWillMove: true,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/flashlight_drop.L.wav",
gravity: {
x: 0,
y: -3.5,
@ -1111,10 +1167,11 @@ MasterReset = function() {
z: 0.07
},
collisionsWillMove: true,
collisionSoundURL: "http://hifi-public.s3.amazonaws.com/sounds/SpryPntCnDrp1.L.wav",
shapeType: 'box',
gravity: {
x: 0,
y: -0.5,
y: -3.0,
z: 0
},
velocity: {
@ -1304,4 +1361,4 @@ MasterReset = function() {
Script.scriptEnding.connect(cleanup);
}
};
};