mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-19 08:18:05 +02:00
Functional sword script:
Mouse and hydra. Switchable hands. Scores above buttons (2d) and above head in-world. Adds avatar hit sound while sword is brandished.
This commit is contained in:
parent
200cbd7e8d
commit
c340d336dc
1 changed files with 156 additions and 103 deletions
|
@ -11,24 +11,26 @@
|
|||
//
|
||||
"use strict";
|
||||
/*jslint vars: true*/
|
||||
var Script, Entities, MyAvatar, Window, Overlays, Controller, Vec3, Quat, print, ToolBar; // Referenced globals provided by High Fidelity.
|
||||
Script.include(["../../libraries/toolBars.js"]);
|
||||
var Script, Entities, MyAvatar, Window, Overlays, Controller, Vec3, Quat, print, ToolBar, Settings; // Referenced globals provided by High Fidelity.
|
||||
Script.include("http://s3.amazonaws.com/hifi-public/scripts/libraries/toolBars.js");
|
||||
|
||||
var hand = "right";
|
||||
var hand = Settings.getValue("highfidelity.sword.hand", "right");
|
||||
var nullActionID = "00000000-0000-0000-0000-000000000000";
|
||||
var controllerID;
|
||||
var controllerActive;
|
||||
var stickID = null;
|
||||
var actionID = nullActionID;
|
||||
var targetIDs = [];
|
||||
var dimensions = { x: 0.3, y: 0.1, z: 2.0 };
|
||||
var AWAY_ORIENTATION = Quat.fromPitchYawRollDegrees(-90, 0, 0);
|
||||
var dimensions = { x: 0.3, y: 0.15, z: 2.0 };
|
||||
var BUTTON_SIZE = 32;
|
||||
|
||||
var stickModel = "https://hifi-public.s3.amazonaws.com/eric/models/stick.fbx";
|
||||
var swordModel = "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.fbx";
|
||||
var swordCollisionShape = "https://hifi-public.s3.amazonaws.com/ozan/props/sword/sword.obj";
|
||||
var swordCollisionSoundURL = "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/swordStrike1.wav";
|
||||
var avatarCollisionSoundURL = "https://s3.amazonaws.com/hifi-public/sounds/Collisions-hitsandslaps/airhockey_hit1.wav";
|
||||
var whichModel = "sword";
|
||||
var attachmentOffset, MOUSE_CONTROLLER_OFFSET = {x: 0.5, y: 0.4, z: 0.0}; // A fudge when using mouse rather than hand-controller, to hit yourself less often.
|
||||
var originalAvatarCollisionSound;
|
||||
|
||||
var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.sword.toolbar", function () {
|
||||
return {x: 100, y: 380};
|
||||
|
@ -37,6 +39,7 @@ var toolBar = new ToolBar(0, 0, ToolBar.vertical, "highfidelity.sword.toolbar",
|
|||
var SWORD_IMAGE = "http://s3.amazonaws.com/hifi-public/images/billiardsReticle.png"; // Toggle between brandishing/sheathing sword (creating if necessary)
|
||||
var TARGET_IMAGE = "http://s3.amazonaws.com/hifi-public/images/puck.png"; // Create a target dummy
|
||||
var CLEANUP_IMAGE = "http://s3.amazonaws.com/hifi-public/images/delete.png"; // Remove sword and all target dummies.f
|
||||
var SWITCH_HANDS_IMAGE = "http://s3.amazonaws.com/hifi-public/images/up-arrow.svg"; // Toggle left vs right hand. Persists in settings.
|
||||
var swordButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
|
@ -49,6 +52,12 @@ var targetButton = toolBar.addOverlay("image", {
|
|||
imageURL: TARGET_IMAGE,
|
||||
alpha: 1
|
||||
});
|
||||
var switchHandsButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
imageURL: SWITCH_HANDS_IMAGE,
|
||||
alpha: 1
|
||||
});
|
||||
var cleanupButton = toolBar.addOverlay("image", {
|
||||
width: BUTTON_SIZE,
|
||||
height: BUTTON_SIZE,
|
||||
|
@ -77,53 +86,51 @@ function flash(color) {
|
|||
flasher.timer = Script.setTimeout(clearFlash, 500);
|
||||
}
|
||||
|
||||
|
||||
var health = 100;
|
||||
var display;
|
||||
var isAway = false;
|
||||
var display2d, display3d;
|
||||
function trackAvatarWithText() {
|
||||
Entities.editEntity(display3d, {
|
||||
position: Vec3.sum(MyAvatar.position, {x: 0, y: 1.5, z: 0}),
|
||||
rotation: Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollDegrees(0, 180, 0))
|
||||
});
|
||||
}
|
||||
function updateDisplay() {
|
||||
var text = health.toString();
|
||||
if (!display) {
|
||||
if (!display2d) {
|
||||
health = 100;
|
||||
display = Overlays.addOverlay("text", {
|
||||
display2d = Overlays.addOverlay("text", {
|
||||
text: text,
|
||||
font: { size: 20 },
|
||||
color: {red: 0, green: 255, blue: 0},
|
||||
backgroundColor: {red: 100, green: 100, blue: 100}, // Why doesn't this and the next work?
|
||||
backgroundAlpha: 0.9,
|
||||
x: Window.innerWidth - 50,
|
||||
y: 50
|
||||
x: toolBar.x - 5, // I'd like to add the score to the toolBar and have it drag with it, but toolBar doesn't support text (just buttons).
|
||||
y: toolBar.y - 30 // So next best thing is to position it each time as if it were on top.
|
||||
});
|
||||
display3d = Entities.addEntity({
|
||||
name: MyAvatar.displayName + " score",
|
||||
textColor: {red: 255, green: 255, blue: 255},
|
||||
type: "Text",
|
||||
text: text,
|
||||
lineHeight: 0.14,
|
||||
backgroundColor: {red: 64, green: 64, blue: 64},
|
||||
dimensions: {x: 0.3, y: 0.2, z: 0.01},
|
||||
});
|
||||
Script.update.connect(trackAvatarWithText);
|
||||
} else {
|
||||
Overlays.editOverlay(display, {text: text});
|
||||
Overlays.editOverlay(display2d, {text: text});
|
||||
Entities.editEntity(display3d, {text: text});
|
||||
}
|
||||
}
|
||||
function removeDisplay() {
|
||||
if (display) {
|
||||
Overlays.deleteOverlay(display);
|
||||
display = null;
|
||||
if (display2d) {
|
||||
Overlays.deleteOverlay(display2d);
|
||||
display2d = null;
|
||||
Script.update.disconnect(trackAvatarWithText);
|
||||
Entities.deleteEntity(display3d);
|
||||
display3d = null;
|
||||
}
|
||||
}
|
||||
|
||||
function cleanUp(leaveButtons) {
|
||||
attachmentOffset = {x: 0, y: 0, z: 0};
|
||||
if (stickID) {
|
||||
Entities.deleteAction(stickID, actionID);
|
||||
Entities.deleteEntity(stickID);
|
||||
stickID = null;
|
||||
actionID = null;
|
||||
}
|
||||
targetIDs.forEach(function (id) {
|
||||
Entities.deleteAction(id.entity, id.action);
|
||||
Entities.deleteEntity(id.entity);
|
||||
});
|
||||
targetIDs = [];
|
||||
removeDisplay();
|
||||
if (!leaveButtons) {
|
||||
toolBar.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
function computeEnergy(collision, entityID) {
|
||||
var id = entityID || collision.idA || collision.idB;
|
||||
var entity = id && Entities.getEntityProperties(id);
|
||||
|
@ -133,31 +140,67 @@ function computeEnergy(collision, entityID) {
|
|||
return Math.min(Math.max(1.0, Math.round(energy)), 20);
|
||||
}
|
||||
function gotHit(collision) {
|
||||
if (isAway) { return; }
|
||||
var energy = computeEnergy(collision);
|
||||
print("Got hit - " + energy + " from " + collision.idA + " " + collision.idB);
|
||||
health -= energy;
|
||||
flash({red: 255, green: 0, blue: 0});
|
||||
updateDisplay();
|
||||
}
|
||||
function scoreHit(idA, idB, collision) {
|
||||
if (isAway) { return; }
|
||||
var energy = computeEnergy(collision, idA);
|
||||
print("Score + " + energy + " from " + JSON.stringify(idA) + " " + JSON.stringify(idB));
|
||||
health += energy;
|
||||
flash({red: 0, green: 255, blue: 0});
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
function positionStick(stickOrientation) {
|
||||
var baseOffset = Vec3.sum(attachmentOffset, {x: 0.0, y: 0.0, z: -dimensions.z / 2});
|
||||
var offset = Vec3.multiplyQbyV(stickOrientation, baseOffset);
|
||||
Entities.updateAction(stickID, actionID, {relativePosition: offset,
|
||||
relativeRotation: stickOrientation});
|
||||
function isFighting() {
|
||||
return stickID && (actionID !== nullActionID);
|
||||
}
|
||||
|
||||
|
||||
function initControls() {
|
||||
print("Sword hand is " + hand);
|
||||
if (hand === "right") {
|
||||
controllerID = 3; // right handed
|
||||
} else {
|
||||
controllerID = 4; // left handed
|
||||
}
|
||||
}
|
||||
var inHand = false;
|
||||
function positionStick(stickOrientation) {
|
||||
var reorient = Quat.fromPitchYawRollDegrees(0, -90, 0);
|
||||
var baseOffset = {x: -dimensions.z * 0.8, y: 0, z: 0};
|
||||
var offset = Vec3.multiplyQbyV(reorient, baseOffset);
|
||||
stickOrientation = Quat.multiply(reorient, stickOrientation);
|
||||
inHand = false;
|
||||
Entities.updateAction(stickID, actionID, {
|
||||
relativePosition: offset,
|
||||
relativeRotation: stickOrientation
|
||||
});
|
||||
}
|
||||
function resetToHand() { // Maybe coordinate with positionStick?
|
||||
if (inHand) { // Optimization: bail if we're already inHand.
|
||||
return;
|
||||
}
|
||||
print('Reset to hand');
|
||||
Entities.updateAction(stickID, actionID, {
|
||||
relativePosition: {x: 0.0, y: 0.0, z: -dimensions.z * 0.5},
|
||||
relativeRotation: Quat.fromVec3Degrees({x: 45.0, y: 0.0, z: 0.0}),
|
||||
hand: hand, // It should not be necessary to repeat these two, but there seems to be a bug in that that
|
||||
timeScale: 0.05 // they do not retain their earlier values if you don't repeat them.
|
||||
});
|
||||
inHand = true;
|
||||
}
|
||||
function mouseMoveEvent(event) {
|
||||
attachmentOffset = MOUSE_CONTROLLER_OFFSET;
|
||||
if (!stickID || actionID === nullActionID || isAway) {
|
||||
if (event.deviceID) { // Not a MOUSE mouse event, but a (e.g., hydra) mouse event, with x/y that is not meaningful for us.
|
||||
resetToHand(); // Can only happen when controller is uncradled, so let's drive with that, resetting our attachement.
|
||||
return;
|
||||
}
|
||||
controllerActive = (Vec3.length(Controller.getSpatialControlPosition(controllerID)) > 0);
|
||||
//print("Mouse move with hand controller " + (controllerActive ? "active" : "inactive") + JSON.stringify(event));
|
||||
if (controllerActive || !isFighting()) {
|
||||
print('Attempting attachment reset');
|
||||
resetToHand();
|
||||
return;
|
||||
}
|
||||
var windowCenterX = Window.innerWidth / 2;
|
||||
|
@ -167,73 +210,80 @@ function mouseMoveEvent(event) {
|
|||
var mouseXRatio = mouseXCenterOffset / windowCenterX;
|
||||
var mouseYRatio = mouseYCenterOffset / windowCenterY;
|
||||
|
||||
var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * -90, mouseXRatio * -90, 0);
|
||||
var stickOrientation = Quat.fromPitchYawRollDegrees(mouseYRatio * 90, mouseXRatio * 90, 0);
|
||||
positionStick(stickOrientation);
|
||||
}
|
||||
|
||||
|
||||
function initControls() {
|
||||
if (hand === "right") {
|
||||
controllerID = 3; // right handed
|
||||
} else {
|
||||
controllerID = 4; // left handed
|
||||
function removeSword() {
|
||||
if (stickID) {
|
||||
print('deleting action ' + actionID + ' and entity ' + stickID);
|
||||
Entities.deleteAction(stickID, actionID);
|
||||
Entities.deleteEntity(stickID);
|
||||
stickID = null;
|
||||
actionID = nullActionID;
|
||||
Controller.mouseMoveEvent.disconnect(mouseMoveEvent);
|
||||
MyAvatar.collisionWithEntity.disconnect(gotHit);
|
||||
// removeEventhHandler happens automatically when the entity is deleted.
|
||||
}
|
||||
inHand = false;
|
||||
if (originalAvatarCollisionSound !== undefined) {
|
||||
MyAvatar.collisionSoundURL = originalAvatarCollisionSound;
|
||||
}
|
||||
removeDisplay();
|
||||
}
|
||||
function cleanUp(leaveButtons) {
|
||||
removeSword();
|
||||
targetIDs.forEach(function (id) {
|
||||
Entities.deleteAction(id.entity, id.action);
|
||||
Entities.deleteEntity(id.entity);
|
||||
});
|
||||
targetIDs = [];
|
||||
if (!leaveButtons) {
|
||||
toolBar.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function update() {
|
||||
var palmPosition = Controller.getSpatialControlPosition(controllerID);
|
||||
controllerActive = (Vec3.length(palmPosition) > 0);
|
||||
if (!controllerActive) {
|
||||
return;
|
||||
function makeSword() {
|
||||
initControls();
|
||||
stickID = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: swordModel,
|
||||
compoundShapeURL: swordCollisionShape,
|
||||
dimensions: dimensions,
|
||||
position: (hand === 'right') ? MyAvatar.getRightPalmPosition() : MyAvatar.getLeftPalmPosition(), // initial position doesn't matter, as long as it's close
|
||||
rotation: MyAvatar.orientation,
|
||||
damping: 0.1,
|
||||
collisionSoundURL: swordCollisionSoundURL,
|
||||
restitution: 0.01,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
actionID = Entities.addAction("hold", stickID, {
|
||||
relativePosition: {x: 0.0, y: 0.0, z: -dimensions.z * 0.5},
|
||||
relativeRotation: Quat.fromVec3Degrees({x: 45.0, y: 0.0, z: 0.0}),
|
||||
hand: hand,
|
||||
timeScale: 0.05
|
||||
});
|
||||
if (actionID === nullActionID) {
|
||||
print('*** FAILED TO MAKE SWORD ACTION ***');
|
||||
cleanUp();
|
||||
}
|
||||
|
||||
var stickOrientation = Controller.getSpatialControlRawRotation(controllerID);
|
||||
var adjustment = Quat.fromPitchYawRollDegrees(180, 0, 0);
|
||||
stickOrientation = Quat.multiply(stickOrientation, adjustment);
|
||||
|
||||
positionStick(stickOrientation);
|
||||
}
|
||||
|
||||
function toggleAway() {
|
||||
isAway = !isAway;
|
||||
if (isAway) {
|
||||
positionStick(AWAY_ORIENTATION);
|
||||
removeDisplay();
|
||||
} else {
|
||||
updateDisplay();
|
||||
if (originalAvatarCollisionSound === undefined) {
|
||||
originalAvatarCollisionSound = MyAvatar.collisionSoundURL; // We won't get MyAvatar.collisionWithEntity unless there's a sound URL. (Bug.)
|
||||
SoundCache.getSound(avatarCollisionSoundURL); // Interface does not currently "preload" this? (Bug?)
|
||||
}
|
||||
MyAvatar.collisionSoundURL = avatarCollisionSoundURL;
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
MyAvatar.collisionWithEntity.connect(gotHit);
|
||||
Script.addEventHandler(stickID, 'collisionWithEntity', scoreHit);
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
function onClick(event) {
|
||||
switch (Overlays.getOverlayAtPoint(event)) {
|
||||
case swordButton:
|
||||
if (!stickID) {
|
||||
initControls();
|
||||
stickID = Entities.addEntity({
|
||||
type: "Model",
|
||||
modelURL: (whichModel === "sword") ? swordModel : stickModel,
|
||||
//compoundShapeURL: "https://hifi-public.s3.amazonaws.com/eric/models/stick.obj",
|
||||
shapeType: "box",
|
||||
dimensions: dimensions,
|
||||
position: MyAvatar.getRightPalmPosition(), // initial position doesn't matter, as long as it's close
|
||||
rotation: MyAvatar.orientation,
|
||||
damping: 0.1,
|
||||
collisionSoundURL: "http://public.highfidelity.io/sounds/Collisions-hitsandslaps/swordStrike1.wav",
|
||||
restitution: 0.01,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
actionID = Entities.addAction("hold", stickID, {relativePosition: {x: 0.0, y: 0.0, z: -dimensions.z / 2},
|
||||
hand: hand,
|
||||
timeScale: 0.15});
|
||||
if (actionID === nullActionID) {
|
||||
print('*** FAILED TO MAKE SWORD ACTION ***');
|
||||
cleanUp();
|
||||
}
|
||||
Script.addEventHandler(stickID, 'collisionWithEntity', scoreHit);
|
||||
updateDisplay();
|
||||
makeSword();
|
||||
} else {
|
||||
toggleAway();
|
||||
removeSword();
|
||||
}
|
||||
break;
|
||||
case targetButton:
|
||||
|
@ -256,6 +306,12 @@ function onClick(event) {
|
|||
});
|
||||
targetIDs.push({entity: boxId, action: action});
|
||||
break;
|
||||
case switchHandsButton:
|
||||
cleanUp('leaveButtons');
|
||||
hand = hand === "right" ? "left" : "right";
|
||||
Settings.setValue("highfidelity.sword.hand", hand);
|
||||
makeSword();
|
||||
break;
|
||||
case cleanupButton:
|
||||
cleanUp('leaveButtons');
|
||||
break;
|
||||
|
@ -263,7 +319,4 @@ function onClick(event) {
|
|||
}
|
||||
|
||||
Script.scriptEnding.connect(cleanUp);
|
||||
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
||||
Controller.mousePressEvent.connect(onClick);
|
||||
Script.update.connect(update);
|
||||
MyAvatar.collisionWithEntity.connect(gotHit);
|
||||
|
|
Loading…
Reference in a new issue