// // laser.js // // Adapted from gun.js by David Back // by Rebecca Stankus on 01/05/18 // Copyright 2018 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 /* globals RayPick */ (function() { var _this; var TRIGGER_CONTROLS = [Controller.Standard.LT, Controller.Standard.RT]; var BEAM_LOCAL_OFFSET = {x: 0, y: -0.0055, z: -0.045}; var BEAM_LOCAL_DIRECTION = {x: 0, y: 0, z: -1000}; var BEAM_MIN_SIZE = 0.04; var TRIGGER_TOGGLE_VALUE = 0.97; var ORIGIN_SIZE_RATIO = 0.6; var BEAM_OFFSET_RATIO = 0.5; var MIN_FOCUS_SIZE = 0.02; var BEAM_WIDTH = 0.01; var HALF = 0.5; var LASER_ON_SOUND = SoundCache.getSound(Script.resolvePath("sounds/on.wav")); var FLASHLIGHT_ON_SOUND = SoundCache.getSound(Script.resolvePath("sounds/flashlight.wav")); var OFF_SOUND = SoundCache.getSound(Script.resolvePath("sounds/off.wav")); var AUDIO_VOLUME_LEVEL = 1; var equipped = false; var currentHand = null; var doNotRayPick = []; var triggerReleased = true; var injector; var Laser = function() { _this = this; }; Laser.prototype = { on: false, flashlight: null, flashlightBeam: null, position: null, diameter: null, beamOffset: null, origin: null, beam: null, focus: null, preload: function(entityID) { _this.entityID = entityID; var dimensions = Entities.getEntityProperties(_this.entityID, 'dimensions').dimensions; _this.diameter = dimensions.x; _this.beamOffset = { x: 0, y: 0, z: -(BEAM_OFFSET_RATIO * dimensions.z) }; }, startEquip: function(id, params) { equipped = true; currentHand = params[0] === "left" ? 0 : 1; }, continueEquip: function(id, params) { if (!equipped) { var parentJointIndex = Entities.getEntityProperties(_this.entityID, 'parentJointIndex').parentJointIndex; currentHand = (parentJointIndex === MyAvatar.getJointIndex("LeftHand")) ? 0 : 1; equipped = true; } else { _this.toggleWithTriggerPressure(); } }, toggleWithTriggerPressure: function() { var triggerValue = Controller.getValue(TRIGGER_CONTROLS[currentHand]); if (_this.on) { if (_this.flashlight) { if (triggerValue >= TRIGGER_TOGGLE_VALUE && triggerReleased) { _this.turnOff(); triggerReleased = false; } else if (triggerValue < TRIGGER_TOGGLE_VALUE && !triggerReleased) { triggerReleased = true; } } else { if (triggerValue >= TRIGGER_TOGGLE_VALUE && triggerReleased) { _this.flashlightMode(); triggerReleased = false; } else if (triggerValue < TRIGGER_TOGGLE_VALUE && !triggerReleased) { triggerReleased = true; } else { _this.turnOn(); } } } else { if (triggerValue >= TRIGGER_TOGGLE_VALUE && triggerReleased) { _this.turnOn(); triggerReleased = false; } else if (triggerValue < TRIGGER_TOGGLE_VALUE && !triggerReleased) { triggerReleased = true; } } }, releaseEquip: function(id, params) { currentHand = null; equipped = false; _this.turnOff(); }, getBeamPosition: function() { var properties = Entities.getEntityProperties(_this.entityID, ['position', 'rotation']); var beamLocalPosition = Vec3.multiplyQbyV(properties.rotation, BEAM_LOCAL_OFFSET); var beamWorldPosition = Vec3.sum(properties.position, beamLocalPosition); return beamWorldPosition; }, getBeamDirection: function() { var rotation = Entities.getEntityProperties(_this.entityID, ['rotation']).rotation; var beamAdjustedDirection = Vec3.multiplyQbyV(rotation, BEAM_LOCAL_DIRECTION); return beamAdjustedDirection; }, playSound: function(sound) { if (sound.downloaded) { if (injector) { injector.stop(); } injector = Audio.playSound(sound, { position: Entities.getEntityProperties(_this.entityID, ['position']).position, volume: AUDIO_VOLUME_LEVEL }); } }, addFocus: function(size, position) { _this.focus = Entities.addEntity({ type: 'Model', modelURL: Script.resolvePath("models/Glow-ball-red.fbx"), dimensions: {x: size, y: size, z: size}, name: "Laser Focus", color: {red: 255, green: 0, blue: 0}, position: position, collisionless: true, userData: "{\"grabbableKey\":{\"grabbable\":false}}" }, true); doNotRayPick.push(_this.focus); }, turnOn: function() { var beamStart = _this.getBeamPosition(); var beamDirection = _this.getBeamDirection(); var beamDirectionLength = Vec3.length(beamDirection); var beamDirectionNormalized = Vec3.normalize(beamDirection); var beamPickRay = ({ origin: beamStart, direction: beamDirectionNormalized }); var entityIntersection = Entities.findRayIntersection(beamPickRay, true, [], doNotRayPick); var entityIntersectionDistance = entityIntersection.intersects ? entityIntersection.distance : 100; var avatarIntersection = AvatarList.findRayIntersection(beamPickRay); var avatarIntersectionDistance = avatarIntersection.intersects ? avatarIntersection.distance : 100; var intersection = null; var intersectEntityID = null; if (entityIntersection.intersects && entityIntersectionDistance <= avatarIntersectionDistance) { intersectEntityID = entityIntersection.entityID; intersection = entityIntersection; } else if (avatarIntersection.intersects && avatarIntersectionDistance < entityIntersectionDistance) { intersectEntityID = avatarIntersection.avatarID; intersection = avatarIntersection; } var intersectionPosition = false; var beamPointDistance = false; var beamSize = false; if (intersectEntityID) { intersectionPosition = intersection.intersection; beamPointDistance = Vec3.distance(intersectionPosition, beamStart); } else { beamPointDistance = Math.min(entityIntersectionDistance, avatarIntersectionDistance); } beamSize = MIN_FOCUS_SIZE * beamPointDistance; if (beamSize < BEAM_MIN_SIZE) { beamSize = BEAM_MIN_SIZE; } console.log("ZRF HERE " + beamSize); if (!_this.on) { _this.playSound(LASER_ON_SOUND); if (intersectionPosition) { _this.addFocus(beamSize, intersectionPosition); } var originDiameter = ORIGIN_SIZE_RATIO * _this.diameter; _this.origin = Entities.addEntity({ type: 'Model', modelURL: Script.resolvePath("models/Glow-ball-red.fbx"), name: "Laser Origin", parentID: _this.entityID, localPosition: _this.beamOffset, dimensions: {x: originDiameter, y: originDiameter, z: originDiameter}, collisionless: true, userData: "{\"grabbableKey\":{\"grabbable\":false}}", description: "CC_BY Alan Zimmerman" }, true); doNotRayPick.push(_this.origin); _this.beam = Entities.addEntity({ type: 'Model', modelURL: Script.resolvePath("models/laser-beam-red.fbx"), name: "Laser Beam", parentID: _this.origin, registrationPoint: {x: 0.5, y: 0.5, z: 1.0}, localPosition: {x: 0, y: 0, z: 0}, localRotation: Quat.normalize({}), dimensions: {x: BEAM_WIDTH, y: BEAM_WIDTH, z: beamPointDistance}, userData: "{\"grabbableKey\":{\"grabbable\":false}}", description: "CC_BY Alan Zimmerman" }, true); doNotRayPick.push(_this.beam); } else { if (intersectionPosition) { if (_this.focus) { Entities.editEntity(_this.focus, { dimensions: {x: beamSize, y: beamSize, z: beamSize}, position: intersectionPosition }); } else { _this.addFocus(beamSize, intersectionPosition); } } else { if (_this.focus) { Entities.deleteEntity(_this.focus); _this.focus = false; } } Entities.editEntity(_this.beam, { localRotation: Quat.normalize({}), dimensions: {x: BEAM_WIDTH, y: BEAM_WIDTH, z: beamPointDistance} }); } _this.on = true; }, flashlightMode: function() { _this.laserOff(); _this.playSound(FLASHLIGHT_ON_SOUND); _this.flashlight = Entities.addEntity({ type: "Light", color: { blue: 22, green: 88, red: 255 }, cutoff: 18, dimensions: { x: 3.652251958847046, y: 3.652251958847046, z: 11.81893539428711 }, exponent: 120, falloffRadius: 5, intensity: 5, isSpotlight: true, name: "Laser Flashlight", parentID: _this.entityID, localRotation: { w: 0.999969482421875, x: -0.007339596748352051, y: 0.0000152587890625, z: -0.0000457763671875 }, localPosition: { x: -0.011594533920288086, y: -0.0043926239013671875, z: -0.04103599488735199 }, userData: "{\"grabbableKey\":{\"grabbable\":false}}" }, true); _this.flashlightBeam = Entities.addEntity({ type: 'Model', modelURL: Script.resolvePath("models/light-beam.fbx"), name: "Laser Beam", parentID: _this.entityID, localPosition: { x: -0.0007977485656738281, y: -0.0014594662934541702, z: -0.1237635612487793 }, localRotation: { x: 0, y: 0, z: 0, w: 1 }, dimensions: { x: 0.0589, y: 0.0589, z: 0.1641 }, userData: "{\"grabbableKey\":{\"grabbable\":false}}", description: "CC_BY Alan Zimmerman" }, true); }, laserOff: function() { Entities.deleteEntity(_this.focus); Entities.deleteEntity(_this.beam); Entities.deleteEntity(_this.origin); _this.focus = null; _this.beam = null; _this.origin = null; }, lightOff: function() { Entities.deleteEntity(_this.flashlightBeam); Entities.deleteEntity(_this.flashlight); _this.flashlight = null; _this.flashlightBeam = null; }, turnOff: function() { _this.playSound(OFF_SOUND); if (_this.flashlight) { this.lightOff(); } else { _this.laserOff(); } _this.on = false; }, unload: function() { _this.turnOff(); } }; return new Laser(); });