Needs a lot of cleanup. Data has been de-duplicated, and where identical copies existed, one of them has been replaced with a symlink. Some files have been excluded, such as binaries, installers and debug dumps. Some of that may still be present.
451 lines
No EOL
16 KiB
JavaScript
451 lines
No EOL
16 KiB
JavaScript
//
|
|
// frisbee.js
|
|
// examples
|
|
//
|
|
// Created by Thijs Wenker on 7/5/14.
|
|
// Copyright 2014 High Fidelity, Inc.
|
|
//
|
|
// Requirements: Razer Hydra's
|
|
//
|
|
// Fun game to throw frisbee's to eachother. Hold the trigger on any of the hydra's to create or catch a frisbee.
|
|
//
|
|
// Tip: use this together with the squeezeHands.js script to make it look nicer.
|
|
//
|
|
// 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/globals.js");
|
|
Script.include("../../libraries/toolBars.js");
|
|
|
|
const LEFT_PALM = 0;
|
|
const LEFT_TIP = 1;
|
|
const LEFT_BUTTON_FWD = 5;
|
|
const LEFT_BUTTON_3 = 3;
|
|
|
|
const RIGHT_PALM = 2;
|
|
const RIGHT_TIP = 3;
|
|
const RIGHT_BUTTON_FWD = 11;
|
|
const RIGHT_BUTTON_3 = 9;
|
|
|
|
const FRISBEE_RADIUS = 0.08;
|
|
const GRAVITY_STRENGTH = 0.5;
|
|
|
|
const CATCH_RADIUS = 0.5;
|
|
const MIN_SIMULATION_SPEED = 0.15;
|
|
const THROWN_VELOCITY_SCALING = 1.5;
|
|
|
|
const SOUNDS_ENABLED = true;
|
|
const FRISBEE_BUTTON_URL = "http://test.thoys.nl/hifi/images/frisbee/frisbee_button_by_Judas.svg";
|
|
const FRISBEE_MODEL_SCALE = 275;
|
|
const FRISBEE_MENU = "Toys>Frisbee";
|
|
const FRISBEE_DESIGN_MENU = "Toys>Frisbee>Design";
|
|
const FRISBEE_ENABLED_SETTING = "Frisbee>Enabled";
|
|
const FRISBEE_CREATENEW_SETTING = "Frisbee>CreateNew";
|
|
const FRISBEE_DESIGN_SETTING = "Frisbee>Design";
|
|
const FRISBEE_FORCE_MOUSE_CONTROLS_SETTING = "Frisbee>ForceMouseControls";
|
|
|
|
//Add your own designs in FRISBEE_DESIGNS, be sure to put frisbee in the URL if you want others to be able to catch it without having a copy of your frisbee script.
|
|
const FRISBEE_DESIGNS = [
|
|
{"name":"Interface", "model":"http://test.thoys.nl/hifi/models/frisbee/frisbee.fbx"},
|
|
{"name":"Pizza", "model":"http://test.thoys.nl/hifi/models/frisbee/pizza.fbx"},
|
|
{"name":"Swirl", "model":"http://test.thoys.nl/hifi/models/frisbee/swirl.fbx"},
|
|
{"name":"Mayan", "model":"http://test.thoys.nl/hifi/models/frisbee/mayan.fbx"},
|
|
];
|
|
const FRISBEE_MENU_DESIGN_POSTFIX = " Design";
|
|
const FRISBEE_DESIGN_RANDOM = "Random";
|
|
|
|
const SPIN_MULTIPLIER = 1000;
|
|
const FRISBEE_LIFETIME = 300; // 5 minutes
|
|
|
|
var windowDimensions = Controller.getViewportDimensions();
|
|
var toolHeight = 50;
|
|
var toolWidth = 50;
|
|
var frisbeeToggle;
|
|
var toolBar;
|
|
var frisbeeEnabled = true;
|
|
var newfrisbeeEnabled = false;
|
|
var forceMouseControls = false;
|
|
var hydrasConnected = false;
|
|
var selectedDesign = FRISBEE_DESIGN_RANDOM;
|
|
|
|
function loadSettings() {
|
|
frisbeeEnabled = Settings.getValue(FRISBEE_ENABLED_SETTING, "true") == "true";
|
|
newfrisbeeEnabled = Settings.getValue(FRISBEE_CREATENEW_SETTING, "false") == "true";
|
|
forceMouseControls = Settings.getValue(FRISBEE_FORCE_MOUSE_CONTROLS_SETTING, "false") == "true";
|
|
selectedDesign = Settings.getValue(FRISBEE_DESIGN_SETTING, "Random");
|
|
}
|
|
|
|
function saveSettings() {
|
|
Settings.setValue(FRISBEE_ENABLED_SETTING, frisbeeEnabled ? "true" : "false");
|
|
Settings.setValue(FRISBEE_CREATENEW_SETTING, newfrisbeeEnabled ? "true" : "false");
|
|
Settings.setValue(FRISBEE_FORCE_MOUSE_CONTROLS_SETTING, forceMouseControls ? "true" : "false");
|
|
Settings.setValue(FRISBEE_DESIGN_SETTING, selectedDesign);
|
|
}
|
|
|
|
function moveOverlays() {
|
|
var newViewPort = Controller.getViewportDimensions();
|
|
if (typeof(toolBar) === 'undefined') {
|
|
initToolBar();
|
|
} else if (windowDimensions.x == newViewPort.x &&
|
|
windowDimensions.y == newViewPort.y) {
|
|
return;
|
|
}
|
|
|
|
windowDimensions = newViewPort;
|
|
var toolsX = windowDimensions.x - 8 - toolBar.width;
|
|
var toolsY = (windowDimensions.y - toolBar.height) / 2 + 80;
|
|
toolBar.move(toolsX, toolsY);
|
|
}
|
|
|
|
function frisbeeURL() {
|
|
return selectedDesign == FRISBEE_DESIGN_RANDOM ? FRISBEE_DESIGNS[Math.floor(Math.random() * FRISBEE_DESIGNS.length)].model : getFrisbee(selectedDesign).model;
|
|
}
|
|
|
|
//This function checks if the modelURL is inside of our Designs or contains "frisbee" in it.
|
|
function validFrisbeeURL(frisbeeURL) {
|
|
for (var frisbee in FRISBEE_DESIGNS) {
|
|
if (FRISBEE_DESIGNS[frisbee].model == frisbeeURL) {
|
|
return true;
|
|
}
|
|
}
|
|
return frisbeeURL.toLowerCase().indexOf("frisbee") !== -1;
|
|
}
|
|
|
|
function getFrisbee(frisbeeName) {
|
|
for (var frisbee in FRISBEE_DESIGNS) {
|
|
if (FRISBEE_DESIGNS[frisbee].name == frisbeeName) {
|
|
return FRISBEE_DESIGNS[frisbee];
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function Hand(name, palm, tip, forwardButton, button3, trigger) {
|
|
this.name = name;
|
|
this.palm = palm;
|
|
this.tip = tip;
|
|
this.forwardButton = forwardButton;
|
|
this.button3 = button3;
|
|
this.trigger = trigger;
|
|
this.holdingFrisbee = false;
|
|
this.entity = false;
|
|
this.palmPosition = function() { return Controller.getSpatialControlPosition(this.palm); }
|
|
this.grabButtonPressed = function() {
|
|
return (
|
|
Controller.isButtonPressed(this.forwardButton) ||
|
|
Controller.isButtonPressed(this.button3) ||
|
|
Controller.getTriggerValue(this.trigger) > 0.5
|
|
)
|
|
};
|
|
this.holdPosition = function() { return this.palm == LEFT_PALM ? MyAvatar.getLeftPalmPosition() : MyAvatar.getRightPalmPosition(); };
|
|
this.holdRotation = function() {
|
|
var q = Controller.getSpatialControlRawRotation(this.palm);
|
|
q = Quat.multiply(MyAvatar.orientation, q);
|
|
return {x: q.x, y: q.y, z: q.z, w: q.w};
|
|
};
|
|
this.tipVelocity = function() { return Controller.getSpatialControlVelocity(this.tip); };
|
|
}
|
|
|
|
function MouseControl(button) {
|
|
this.button = button;
|
|
}
|
|
|
|
var leftHand = new Hand("LEFT", LEFT_PALM, LEFT_TIP, LEFT_BUTTON_FWD, LEFT_BUTTON_3, 0);
|
|
var rightHand = new Hand("RIGHT", RIGHT_PALM, RIGHT_TIP, RIGHT_BUTTON_FWD, RIGHT_BUTTON_3, 1);
|
|
|
|
var leftMouseControl = new MouseControl("LEFT");
|
|
var middleMouseControl = new MouseControl("MIDDLE");
|
|
var rightMouseControl = new MouseControl("RIGHT");
|
|
var mouseControls = [leftMouseControl, middleMouseControl, rightMouseControl];
|
|
var currentMouseControl = false;
|
|
|
|
var newSound = SoundCache.getSound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/throw.raw");
|
|
var catchSound = SoundCache.getSound("https://dl.dropboxusercontent.com/u/1864924/hifi-sounds/catch.raw");
|
|
var throwSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Switches%20and%20sliders/slider%20-%20whoosh1.raw");
|
|
|
|
var simulatedFrisbees = [];
|
|
|
|
var wantDebugging = false;
|
|
function debugPrint(message) {
|
|
if (wantDebugging) {
|
|
print(message);
|
|
}
|
|
}
|
|
|
|
function playSound(sound, position) {
|
|
if (!SOUNDS_ENABLED) {
|
|
return;
|
|
}
|
|
|
|
Audio.playSound(sound,{
|
|
position: position
|
|
});
|
|
}
|
|
|
|
function cleanupFrisbees() {
|
|
simulatedFrisbees = [];
|
|
var entities = Entities.findEntities(MyAvatar.position, 1000);
|
|
for (entity in entities) {
|
|
Entities.deleteEntity(entities[entity]);
|
|
}
|
|
}
|
|
|
|
function checkControllerSide(hand) {
|
|
// print("cCS");
|
|
// If I don't currently have a frisbee in my hand, then try to catch closest one
|
|
if (!hand.holdingFrisbee && hand.grabButtonPressed()) {
|
|
var closestEntity = Entities.findClosestEntity(hand.palmPosition(), CATCH_RADIUS);
|
|
var modelUrl = Entities.getEntityProperties(closestEntity).modelURL;
|
|
print("lol2"+closestEntity.isKnownID);
|
|
if (closestEntity.isKnownID && validFrisbeeURL(Entities.getEntityProperties(closestEntity).modelURL)) {
|
|
print("lol");
|
|
Entities.editEntity(closestEntity, {modelScale: 1, inHand: true, position: hand.holdPosition(), shouldDie: true});
|
|
Entities.deleteEntity(closestEntity);
|
|
debugPrint(hand.message + " HAND- CAUGHT SOMETHING!!");
|
|
print("lol");
|
|
var properties = {
|
|
type: "Model",
|
|
position: hand.holdPosition(),
|
|
velocity: { x: 0, y: 0, z: 0},
|
|
gravity: { x: 0, y: 0, z: 0},
|
|
inHand: true,
|
|
dimensions: { x: FRISBEE_RADIUS, y: FRISBEE_RADIUS / 5, z: FRISBEE_RADIUS },
|
|
damping: 0.999,
|
|
modelURL: modelUrl,
|
|
scale: FRISBEE_MODEL_SCALE,
|
|
rotation: hand.holdRotation(),
|
|
lifetime: FRISBEE_LIFETIME
|
|
};
|
|
|
|
newEntity = Entities.addEntity(properties);
|
|
|
|
hand.holdingFrisbee = true;
|
|
hand.entity = newEntity;
|
|
|
|
playSound(catchSound, hand.holdPosition());
|
|
|
|
return; // exit early
|
|
}
|
|
}
|
|
|
|
// If '3' is pressed, and not holding a frisbee, make a new one
|
|
if (hand.grabButtonPressed() && !hand.holdingFrisbee && newfrisbeeEnabled) {
|
|
var properties = {
|
|
type: "Model",
|
|
position: hand.holdPosition(),
|
|
velocity: { x: 0, y: 0, z: 0},
|
|
gravity: { x: 0, y: 0, z: 0},
|
|
inHand: true,
|
|
dimensions: { x: FRISBEE_RADIUS, y: FRISBEE_RADIUS / 5, z: FRISBEE_RADIUS },
|
|
damping: 0,
|
|
modelURL: frisbeeURL(),
|
|
scale: FRISBEE_MODEL_SCALE,
|
|
rotation: hand.holdRotation(),
|
|
lifetime: FRISBEE_LIFETIME
|
|
};
|
|
|
|
newEntity = Entities.addEntity(properties);
|
|
hand.holdingFrisbee = true;
|
|
hand.entity = newEntity;
|
|
|
|
// Play a new frisbee sound
|
|
playSound(newSound, hand.holdPosition());
|
|
|
|
return; // exit early
|
|
}
|
|
|
|
if (hand.holdingFrisbee) {
|
|
// If holding the frisbee keep it in the palm
|
|
if (hand.grabButtonPressed()) {
|
|
debugPrint(">>>>> " + hand.name + "-FRISBEE IN HAND, grabbing, hold and move");
|
|
var properties = {
|
|
position: hand.holdPosition(),
|
|
modelRotation: hand.holdRotation()
|
|
};
|
|
Entities.editEntity(hand.entity, properties);
|
|
} else {
|
|
debugPrint(">>>>> " + hand.name + "-FRISBEE IN HAND, not grabbing, THROW!!!");
|
|
// If frisbee just released, add velocity to it!
|
|
|
|
var properties = {
|
|
velocity: Vec3.multiply(hand.tipVelocity(), THROWN_VELOCITY_SCALING),
|
|
inHand: false,
|
|
lifetime: FRISBEE_LIFETIME,
|
|
gravity: { x: 0, y: -GRAVITY_STRENGTH, z: 0},
|
|
rotation: hand.holdRotation()
|
|
};
|
|
|
|
Entities.editEntity(hand.entity, properties);
|
|
|
|
simulatedFrisbees.push(hand.entity);
|
|
|
|
hand.holdingFrisbee = false;
|
|
hand.entity = false;
|
|
|
|
playSound(throwSound, hand.holdPosition());
|
|
}
|
|
}
|
|
}
|
|
|
|
function initToolBar() {
|
|
toolBar = new ToolBar(0, 0, ToolBar.VERTICAL);
|
|
frisbeeToggle = toolBar.addTool({
|
|
imageURL: FRISBEE_BUTTON_URL,
|
|
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
|
width: toolWidth,
|
|
height: toolHeight,
|
|
visible: true,
|
|
alpha: 0.9
|
|
}, true);
|
|
enableNewFrisbee(newfrisbeeEnabled);
|
|
}
|
|
|
|
function hydraCheck() {
|
|
var numberOfButtons = Controller.getNumberOfButtons();
|
|
var numberOfTriggers = Controller.getNumberOfTriggers();
|
|
var numberOfSpatialControls = Controller.getNumberOfSpatialControls();
|
|
var controllersPerTrigger = numberOfSpatialControls / numberOfTriggers;
|
|
hydrasConnected = (numberOfButtons == 12 && numberOfTriggers == 2 && controllersPerTrigger == 2);
|
|
return true;//hydrasConnected;
|
|
}
|
|
|
|
function checkController(deltaTime) {
|
|
moveOverlays();
|
|
if (!frisbeeEnabled) {
|
|
return;
|
|
}
|
|
// this is expected for hydras
|
|
if (hydraCheck()) {
|
|
///print("testrr ");
|
|
checkControllerSide(leftHand);
|
|
checkControllerSide(rightHand);
|
|
}
|
|
if (!hydrasConnected || forceMouseControls) {
|
|
//TODO: add mouse cursor control code here.
|
|
}
|
|
}
|
|
|
|
function controlFrisbees(deltaTime) {
|
|
var killSimulations = [];
|
|
for (frisbee in simulatedFrisbees) {
|
|
var properties = Entities.getEntityProperties(simulatedFrisbees[frisbee]);
|
|
//get the horizon length from the velocity origin in order to get speed
|
|
var speed = Vec3.length({x:properties.velocity.x, y:0, z:properties.velocity.z});
|
|
if (speed < MIN_SIMULATION_SPEED) {
|
|
//kill the frisbee simulation when speed is low
|
|
killSimulations.push(frisbee);
|
|
continue;
|
|
}
|
|
Entities.editEntity(simulatedFrisbees[frisbee], {rotation: Quat.multiply(properties.modelRotation, Quat.fromPitchYawRollDegrees(0, speed * deltaTime * SPIN_MULTIPLIER, 0))});
|
|
|
|
}
|
|
for (var i = killSimulations.length - 1; i >= 0; i--) {
|
|
simulatedFrisbees.splice(killSimulations[i], 1);
|
|
}
|
|
}
|
|
|
|
//catches interfering calls of hydra-cursors
|
|
function withinBounds(coords) {
|
|
return coords.x >= 0 && coords.x < windowDimensions.x && coords.y >= 0 && coords.y < windowDimensions.y;
|
|
}
|
|
|
|
function mouseMoveEvent(event) {
|
|
//TODO: mouse controls //print(withinBounds(event)); //print("move"+event.x);
|
|
}
|
|
|
|
function mousePressEvent(event) {
|
|
print(event.x);
|
|
var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y});
|
|
if (frisbeeToggle == toolBar.clicked(clickedOverlay)) {
|
|
newfrisbeeEnabled = !newfrisbeeEnabled;
|
|
saveSettings();
|
|
enableNewFrisbee(newfrisbeeEnabled);
|
|
}
|
|
}
|
|
|
|
function enableNewFrisbee(enable) {
|
|
if (toolBar.numberOfTools() > 0) {
|
|
toolBar.tools[0].select(enable);
|
|
}
|
|
}
|
|
|
|
function mouseReleaseEvent(event) {
|
|
//TODO: mouse controls //print(JSON.stringify(event));
|
|
}
|
|
|
|
function setupMenus() {
|
|
Menu.addMenu(FRISBEE_MENU);
|
|
Menu.addMenuItem({
|
|
menuName: FRISBEE_MENU,
|
|
menuItemName: "Frisbee Enabled",
|
|
isCheckable: true,
|
|
isChecked: frisbeeEnabled
|
|
});
|
|
Menu.addMenuItem({
|
|
menuName: FRISBEE_MENU,
|
|
menuItemName: "Cleanup Frisbees"
|
|
});
|
|
Menu.addMenuItem({
|
|
menuName: FRISBEE_MENU,
|
|
menuItemName: "Force Mouse Controls",
|
|
isCheckable: true,
|
|
isChecked: forceMouseControls
|
|
});
|
|
Menu.addMenu(FRISBEE_DESIGN_MENU);
|
|
Menu.addMenuItem({
|
|
menuName: FRISBEE_DESIGN_MENU,
|
|
menuItemName: FRISBEE_DESIGN_RANDOM + FRISBEE_MENU_DESIGN_POSTFIX,
|
|
isCheckable: true,
|
|
isChecked: selectedDesign == FRISBEE_DESIGN_RANDOM
|
|
});
|
|
for (frisbee in FRISBEE_DESIGNS) {
|
|
Menu.addMenuItem({
|
|
menuName: FRISBEE_DESIGN_MENU,
|
|
menuItemName: FRISBEE_DESIGNS[frisbee].name + FRISBEE_MENU_DESIGN_POSTFIX,
|
|
isCheckable: true,
|
|
isChecked: selectedDesign == FRISBEE_DESIGNS[frisbee].name
|
|
});
|
|
}
|
|
}
|
|
|
|
//startup calls:
|
|
loadSettings();
|
|
setupMenus();
|
|
function scriptEnding() {
|
|
toolBar.cleanup();
|
|
Menu.removeMenu(FRISBEE_MENU);
|
|
}
|
|
|
|
function menuItemEvent(menuItem) {
|
|
if (menuItem == "Cleanup Frisbees") {
|
|
cleanupFrisbees();
|
|
return;
|
|
} else if (menuItem == "Frisbee Enabled") {
|
|
frisbeeEnabled = Menu.isOptionChecked(menuItem);
|
|
saveSettings();
|
|
return;
|
|
} else if (menuItem == "Force Mouse Controls") {
|
|
forceMouseControls = Menu.isOptionChecked(menuItem);
|
|
saveSettings();
|
|
return;
|
|
}
|
|
if (menuItem.indexOf(FRISBEE_MENU_DESIGN_POSTFIX, menuItem.length - FRISBEE_MENU_DESIGN_POSTFIX.length) !== -1) {
|
|
var item_name = menuItem.substring(0, menuItem.length - FRISBEE_MENU_DESIGN_POSTFIX.length);
|
|
if (item_name == FRISBEE_DESIGN_RANDOM || getFrisbee(item_name) != undefined) {
|
|
Menu.setIsOptionChecked(selectedDesign + FRISBEE_MENU_DESIGN_POSTFIX, false);
|
|
selectedDesign = item_name;
|
|
saveSettings();
|
|
Menu.setIsOptionChecked(selectedDesign + FRISBEE_MENU_DESIGN_POSTFIX, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// register the call back so it fires before each data send
|
|
Controller.mouseMoveEvent.connect(mouseMoveEvent);
|
|
Controller.mousePressEvent.connect(mousePressEvent);
|
|
Controller.mouseReleaseEvent.connect(mouseReleaseEvent);
|
|
Menu.menuItemEvent.connect(menuItemEvent);
|
|
Script.scriptEnding.connect(scriptEnding);
|
|
Script.update.connect(checkController);
|
|
Script.update.connect(controlFrisbees); |