mirror of
https://github.com/overte-org/overte.git
synced 2025-04-29 12:02:34 +02:00
671 lines
26 KiB
JavaScript
671 lines
26 KiB
JavaScript
//
|
|
// 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;
|
|
});
|