added tetherball create and entity scripts

This commit is contained in:
Rob Kayson 2017-04-03 11:55:47 -07:00
parent 999bd0d339
commit 6763ca9b27
2 changed files with 484 additions and 0 deletions

View file

@ -0,0 +1,73 @@
"use strict";
/* jslint vars: true, plusplus: true, forin: true*/
/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, Controller, print, getControllerWorldLocation */
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
//
// createTetherballStick.js
//
// Created by Triplelexx on 17/03/04
// Updated by MrRoboman on 17/03/26
// Copyright 2017 High Fidelity, Inc.
//
// Creates an equippable stick with a tethered ball
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
var STICK_SCRIPT_URL = Script.resolvePath("./entity_scripts/tetherballStick.js?v=" + Date.now());
var STICK_MODEL_URL = "http://hifi-content.s3.amazonaws.com/caitlyn/production/raveStick/newRaveStick2.fbx";
var avatarOrientation = MyAvatar.orientation;
avatarOrientation = Quat.safeEulerAngles(avatarOrientation);
avatarOrientation.x = 0;
avatarOrientation = Quat.fromVec3Degrees(avatarOrientation);
var startPosition = Vec3.sum(MyAvatar.getRightPalmPosition(), Vec3.multiply(1, Quat.getFront(avatarOrientation)));
var STICK_PROPERTIES = {
type: 'Model',
name: "TetherballStick Stick",
modelURL: STICK_MODEL_URL,
position: startPosition,
rotation: MyAvatar.orientation,
dimensions: {
x: 0.0651,
y: 0.0651,
z: 0.5270
},
script: STICK_SCRIPT_URL,
color: {
red: 200,
green: 0,
blue: 20
},
shapeType: 'box',
lifetime: 3600,
userData: JSON.stringify({
grabbableKey: {
grabbable: true,
spatialKey: {
rightRelativePosition: {
x: 0.05,
y: 0,
z: 0
},
leftRelativePosition: {
x: -0.05,
y: 0,
z: 0
},
relativeRotation: {
x: 0.4999999701976776,
y: 0.4999999701976776,
z: -0.4999999701976776,
w: 0.4999999701976776
}
},
invertSolidWhileHeld: true
},
ownerID: MyAvatar.sessionUUID
})
};
Entities.addEntity(STICK_PROPERTIES);
Script.stop();

View file

@ -0,0 +1,411 @@
"use strict";
/* jslint vars: true, plusplus: true, forin: true*/
/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, Controller, print, getControllerWorldLocation */
/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */
//
// tetherballStick.js
//
// Created by Triplelexx on 17/03/04
// Updated by MrRoboman on 17/03/26
// Copyright 2017 High Fidelity, Inc.
//
// Entity script for an equippable stick with a tethered ball
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
(function() {
var _this;
var NULL_UUID = "{00000000-0000-0000-0000-000000000000}";
var ENTITY_CHECK_INTERVAL = 5; // time in sec
var LINE_WIDTH = 0.02;
var BALL_SIZE = 0.175;
var BALL_DAMPING = 0.5;
var BALL_ANGULAR_DAMPING = 0.5;
var BALL_RESTITUTION = 0.4;
var BALL_DENSITY = 1000;
var MAX_DISTANCE_MULTIPLIER = 2;
var ACTION_DISTANCE = 0.25;
var ACTION_TIMESCALE = 0.025;
var ACTION_TAG = "TetherballStick Action";
var BALL_NAME = "TetherballStick Ball";
var LINE_NAME = "TetherballStick Line";
var COLLISION_SOUND_URL = "http://public.highfidelity.io/sounds/Footsteps/FootstepW3Left-12db.wav";
var INTERACT_SOUND_URL = "http://hifi-public.s3.amazonaws.com/sounds/color_busters/powerup.wav";
var INTERACT_SOUND_VOLUME = 0.2;
var USE_INTERACT_SOUND = false;
var AVATAR_CHECK_RANGE = 5; // in meters
var TIP_OFFSET = 0.26;
var LIFETIME = 3600;
tetherballStick = function() {
_this = this;
return;
};
tetherballStick.prototype = {
lastCheckForEntities: ENTITY_CHECK_INTERVAL,
originalDimensions: null,
ownerID: NULL_UUID,
userID: NULL_UUID,
ballID: NULL_UUID,
lineID: NULL_UUID,
actionID: NULL_UUID,
INTERACT_SOUND: undefined,
preload: function(entityID) {
this.entityID = entityID;
if (USE_INTERACT_SOUND) {
this.INTERACT_SOUND = SoundCache.getSound(INTERACT_SOUND_URL);
}
this.originalDimensions = Entities.getEntityProperties(this.entityID).dimensions;
Script.update.connect(this.update);
},
unload: function() {
Script.update.disconnect(this.update);
},
update: function(dt) {
// _this during update due to loss of scope
if (_this.lastCheckForEntities >= ENTITY_CHECK_INTERVAL) {
_this.lastCheckForEntities = 0;
// everyone will be running this update loop
// the only action is to increment the timer till ENTITY_CHECK_INTERVAL
// when that is reached the userdata ownerID is simply validated by everyone
// if it's invalid call setNewOwner, to set a new valid user
if (!_this.checkForOwner()) {
return;
}
// only one person should ever be running this far
if (_this.ownerID == MyAvatar.sessionUUID) {
_this.checkForEntities();
}
} else {
_this.lastCheckForEntities += dt;
}
_this.drawLine();
},
getScale: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
return stickProps.dimensions.z / this.originalDimensions.z;
},
checkForOwner: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
try {
var stickData = JSON.parse(stickProps.userData);
var owner = AvatarManager.getAvatar(stickData.ownerID);
if (owner.sessionUUID !== undefined) {
this.ownerID = owner.sessionUUID;
return true;
} else {
// UUID is invalid
this.setNewOwner();
return false;
}
} catch (e) {
// all other errors
this.setNewOwner();
return false;
}
},
setNewOwner: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
// I only want the closest client to be in charge of creating objects.
// the AvatarList also contains a null representing MyAvatar,
// a new array is created to start with containing the proper UUID
var avatarList = AvatarList.getAvatarIdentifiers()
.filter(Boolean) // remove the null
.concat(MyAvatar.sessionUUID); // add the ID
var closestAvatarID = undefined;
var closestAvatarDistance = AVATAR_CHECK_RANGE;
avatarList.forEach(function(avatarID) {
var avatar = AvatarList.getAvatar(avatarID);
var distFrom = Vec3.distance(avatar.position, stickProps.position);
if (distFrom < closestAvatarDistance) {
closestAvatarDistance = distFrom;
closestAvatarID = avatarID;
}
});
// add reference to userData
try {
var stickData = JSON.parse(stickProps.userData);
stickData.ownerID = closestAvatarID; //NOTE: this will assign undefined if all avatars are further than AVATAR_CHECK_RANGE
Entities.editEntity(this.entityID, {
userData: JSON.stringify(stickData)
});
} catch (e) {
}
},
checkForEntities: function() {
if (!this.hasBall()) {
this.createBall();
}
if (!this.hasAction()) {
this.createAction();
}
},
playInteractionSound: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
var INTERACT_SOUND_OPTIONS = {
volume: INTERACT_SOUND_VOLUME,
loop: false,
position: stickProps.position
};
Audio.playSound(this.INTERACT_SOUND, INTERACT_SOUND_OPTIONS);
},
getTipPosition: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
var scale = this.getScale();
var frontVec = Quat.getFront(stickProps.rotation);
var frontOffset = Vec3.multiply(frontVec, TIP_OFFSET * scale);
var tipPosition = Vec3.sum(stickProps.position, frontOffset);
return tipPosition;
},
getStickFrontPosition: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
var stickFront = Quat.getFront(stickProps.rotation);
var tipPosition = this.getTipPosition();
return Vec3.sum(tipPosition, Vec3.multiply(TIP_OFFSET*.4, stickFront));
},
capBallDistance: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
var ballProps = Entities.getEntityProperties(this.ballID);
var tipPosition = this.getTipPosition();
var scale = this.getScale();
var distance = Vec3.distance(tipPosition, ballProps.position);
var maxDistance = ACTION_DISTANCE * MAX_DISTANCE_MULTIPLIER * scale;
if(distance > maxDistance) {
var direction = Vec3.normalize(Vec3.subtract(ballProps.position, tipPosition));
var newPosition = Vec3.sum(tipPosition, Vec3.multiply(maxDistance, direction));
Entities.editEntity(this.ballID, {
position: newPosition
})
}
},
startNearGrab: function(id, params) {
var stickProps = Entities.getEntityProperties(this.entityID);
// set variables from data in case someone else created the components
try {
var stickData = JSON.parse(stickProps.userData);
this.userID = MyAvatar.sessionUUID;
this.ballID = stickData.ballID;
this.actionID = stickData.actionID;
if(!this.hasLine()) {
this.createLine();
}
if (USE_INTERACT_SOUND) {
var hand = params[0];
Controller.triggerShortHapticPulse(1, hand);
this.playInteractionSound();
}
} catch (e) { }
},
releaseGrab: function(id, params) {
this.userID = NULL_UUID;
if (USE_INTERACT_SOUND) {
this.playInteractionSound();
}
},
continueNearGrab: function(id, params) {
this.repositionAction();
this.updateDimensions();
this.capBallDistance();
},
createLine: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
this.lineID = Entities.addEntity({
type: "PolyLine",
name: LINE_NAME,
color: {
red: 0,
green: 120,
blue: 250
},
textures: "https://hifi-public.s3.amazonaws.com/alan/Particles/Particle-Sprite-Smoke-1.png",
position: stickProps.position,
dimensions: {
x: 10,
y: 10,
z: 10
},
lifetime: LIFETIME
});
},
deleteLine: function() {
Entities.deleteEntity(this.lineID);
this.lineID = NULL_UUID;
},
hasLine: function() {
var lineProps = Entities.getEntityProperties(this.lineID);
return lineProps.name == LINE_NAME;
},
createBall: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
this.ballID = Entities.addEntity({
type: "Model",
modelURL: "http://hifi-content.s3.amazonaws.com/Examples%20Content/production/marblecollection/Star.fbx",
name: BALL_NAME,
shapeType: "Sphere",
position: this.getStickFrontPosition(),
lifetime: LIFETIME,
collisionSoundURL: COLLISION_SOUND_URL,
dimensions: {
x: BALL_SIZE,
y: BALL_SIZE,
z: BALL_SIZE
},
gravity: {
x: 0.0,
y: -9.8,
z: 0.0
},
damping: BALL_DAMPING,
angularDamping: BALL_ANGULAR_DAMPING,
density: BALL_DENSITY,
restitution: BALL_RESTITUTION,
dynamic: true,
collidesWith: "static,dynamic,otherAvatar,",
userData: JSON.stringify({
grabbableKey: {
grabbable: false
}
})
});
// add reference to userData
try {
var stickData = JSON.parse(stickProps.userData);
stickData.ballID = this.ballID;
Entities.editEntity(this.entityID, {
userData: JSON.stringify(stickData)
});
} catch (e) {}
},
hasBall: function() {
// validate the userData to handle unexpected item deletion
var stickProps = Entities.getEntityProperties(this.entityID);
try {
var data = JSON.parse(stickProps.userData);
var ballProps = Entities.getEntityProperties(data.ballID);
return ballProps.name == BALL_NAME;
} catch (e) {
return false;
}
},
createAction: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
this.actionID = Entities.addAction("offset", this.ballID, {
pointToOffsetFrom: stickProps.position,
linearDistance: ACTION_DISTANCE,
tag: ACTION_TAG,
linearTimeScale: ACTION_TIMESCALE
});
// add reference to userData
try {
var stickData = JSON.parse(stickProps.userData);
stickData.actionID = this.actionID;
Entities.editEntity(this.entityID, {
userData: JSON.stringify(stickData)
});
} catch (e) {}
},
hasAction: function() {
// validate the userData to handle unexpected item deletion
var stickProps = Entities.getEntityProperties(this.entityID);
try {
var stickData = JSON.parse(stickProps.userData);
var actionProps = Entities.getActionArguments(stickData.ballID, stickData.actionID);
return actionProps.tag == ACTION_TAG;
} catch (e) {
return false;
}
},
hasRequiredComponents: function() {
return this.hasBall() && this.hasAction() && this.hasLine();
},
updateDimensions: function() {
var scale = this.getScale();
Entities.editEntity(this.ballID, {
dimensions: {
x: BALL_SIZE * scale,
y: BALL_SIZE * scale,
z: BALL_SIZE * scale
}
});
},
drawLine: function() {
if(!this.hasLine())
return;
var stickProps = Entities.getEntityProperties(this.entityID);
var tipPosition = this.getTipPosition();
var ballProps = Entities.getEntityProperties(this.ballID);
var cameraQuat = Vec3.multiplyQbyV(Camera.getOrientation(), Vec3.UNIT_NEG_Z);
var linePoints = [];
var normals = [];
var strokeWidths = [];
linePoints.push(Vec3.ZERO);
normals.push(cameraQuat);
strokeWidths.push(LINE_WIDTH);
linePoints.push(Vec3.subtract(ballProps.position, tipPosition));
normals.push(cameraQuat);
strokeWidths.push(LINE_WIDTH);
var lineProps = Entities.getEntityProperties(this.lineID);
Entities.editEntity(this.lineID, {
linePoints: linePoints,
normals: normals,
strokeWidths: strokeWidths,
position: tipPosition,
});
},
repositionAction: function() {
var stickProps = Entities.getEntityProperties(this.entityID);
var tipPosition = this.getTipPosition();
var scale = this.getScale();
Entities.updateAction(this.ballID, this.actionID, {
pointToOffsetFrom: tipPosition,
linearDistance: ACTION_DISTANCE * scale,
tag: ACTION_TAG,
linearTimeScale: ACTION_TIMESCALE
});
}
};
// entity scripts should return a newly constructed object of our type
return new tetherballStick();
});