var appUUID = Uuid.generate(); var ROOT = "http://mpassets.highfidelity.com/5cb2cc54-729a-461f-abeb-0bdd357345e0-v1/"; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var icon = ROOT + "hoverKart.svg?"; var appHTML = ROOT + "hoverKart.html"; var button = tablet.addButton({ icon: icon, text: "SWAMPBOAT2" }); var isOpen = false; var running = false; var ANIMATION_URL = ROOT + "sitting.fbx"; var ANIMATION_FPS = 30, ANIMATION_FIRST_FRAME = 1, ANIMATION_LAST_FRAME = 200; var kartWearRotation = {x: 0, y: 0, z: 0, w: 1}; var kartURL = ROOT + "raceboat_diesel.fbx"; var kartEntity; var kartName = "hoverKart"; var SCALE = 1; var TIMEOUT = 500; var INT_2 = 2; var MS_CONVERSION = 1000; var jointIndex = -1; var kartSize = { x: 1.1228, y: 0.6090, z: 1.8364 }; var soundEnabled = true; var sounds = { setup: function () { var s = { IDLE_SOUND_URL: ROOT + "engine.wav", RUNNING_SOUND_URL: ROOT + "engineDrive.wav", REZ_SOUND_URL: ROOT + "rez.wav", DEREZ_SOUND_URL: ROOT + "derez.wav", JUMP_SOUND_URL: ROOT + "jump.wav", REZ_SOUND_VOLUME: 0.1, IDLE_SOUND_VOLUME: 0.03, RUNNING_SOUND_VOLUME: 0.03, RUNNING_SOUND_VOLUME_MIN: 0.03, RUNNING_SOUND_VOLUME_MAX: 0.6, idleInjector: null, runningInjector: null, rezInjector: null, derezInjector: null, jumpInjector: null, positionTimer: null, positionTimerInterval: 100, runningTimer: null, runningTimerInterval: 100 }; s.idleProps = {position: MyAvatar.position, volume: s.IDLE_SOUND_VOLUME, loop: true}; s.runningProps = {position: MyAvatar.position, volume: s.RUNNING_SOUND_VOLUME, loop: true}; s.effectProps = {position: MyAvatar.position, volume: s.REZ_SOUND_VOLUME, loop: false}; s.idleSoundURL = SoundCache.getSound(s.IDLE_SOUND_URL); s.runningSoundURL = SoundCache.getSound(s.RUNNING_SOUND_URL); s.rezSoundURL = SoundCache.getSound(s.REZ_SOUND_URL); s.derezSoundURL = SoundCache.getSound(s.DEREZ_SOUND_URL); s.jumpSoundURL = SoundCache.getSound(s.JUMP_SOUND_URL); if (!s.idleSoundURL.downloaded) { s.idleSoundURL = SoundCache.getSound(s.IDLE_SOUND_URL); } if (!s.runningSoundURL.downloaded) { s.runningSoundURL = SoundCache.getSound(s.RUNNING_SOUND_URL); } if (!s.rezSoundURL.downloaded) { s.rezSoundURL = SoundCache.getSound(s.REZ_SOUND_URL); } if (!s.derezSoundURL.downloaded) { s.derezSoundURL = SoundCache.getSound(s.DEREZ_SOUND_URL); } if (!s.jumpSoundURL.downloaded) { s.jumpSoundURL = SoundCache.getSound(s.JUMP_SOUND_URL); } s.playJump = function () { if (s.jumpInjector === null) { s.effectProps.position = MyAvatar.position; s.jumpInjector = Audio.playSound(s.jumpSoundURL, s.effectProps); } else { s.effectProps.position = MyAvatar.position; s.jumpInjector.setOptions(s.effectProps); s.jumpInjector.restart(); } }; s.playRez = function () { if (s.rezInjector === null) { s.effectProps.position = MyAvatar.position; s.rezInjector = Audio.playSound(s.rezSoundURL, s.effectProps); } else { s.effectProps.position = MyAvatar.position; s.rezInjector.setOptions(s.effectProps); s.rezInjector.restart(); } }; s.playDerez = function () { if (s.derezInjector === null) { s.effectProps.position = MyAvatar.position; s.derezInjector = Audio.playSound(s.derezSoundURL, s.effectProps); } else { s.effectProps.position = MyAvatar.position; s.derezInjector.setOptions(s.effectProps); s.derezInjector.restart(); } }; s.playIdle = function () { if (s.runningInjector !== null) { Script.clearInterval(s.runningTimer); s.runningInjector.stop(); } if (s.idleInjector === null) { s.idleProps.position = MyAvatar.position; s.idleInjector = Audio.playSound(s.idleSoundURL, s.idleProps); } else { s.idleProps.position = MyAvatar.position; s.idleInjector.setOptions(s.idleProps); s.idleInjector.restart(); } }; s.playRunning = function () { if (s.idleInjector !== null) { s.idleInjector.stop(); } if (s.runningInjector === null) { s.runningProps.position = MyAvatar.position; s.runningInjector = Audio.playSound(s.runningSoundURL, s.runningProps); } else { s.runningProps.position = MyAvatar.position; s.runningInjector.setOptions(s.runningProps); s.runningInjector.restart(); } s.runningTimer = Script.setInterval(s.runningUpdate, s.runningTimerInterval); }; s.runningUpdate = function () { var vel = MyAvatar.motorVelocity; s.RUNNING_SOUND_VOLUME = normalize(Math.abs(vel.z), s.RUNNING_SOUND_VOLUME_MIN, s.RUNNING_SOUND_VOLUME_MAX) * Math.sign(vel.z); }; s.positionUpdate = function () { s.idleProps.position = MyAvatar.position; s.runningProps.position = MyAvatar.position; if (s.idleInjector !== null) { s.idleInjector.setOptions(s.idleProps); } if (s.runningInjector !== null) { s.runningInjector.setOptions(s.runningProps); } }; s.start = function () { s.positionTimer = Script.setInterval(s.positionUpdate, s.positionTimerInterval); s.playIdle(); }; s.stop = function () { Script.clearInterval(s.positionTimer); if (s.idleInjector !== null) { s.idleInjector.stop(); } if (s.runningInjector !== null) { s.runningInjector.stop(); } }; s.idleInjector = Audio.playSound(s.idleSoundURL, {volume: 0}); s.runningInjector = Audio.playSound(s.runningSoundURL, {volume: 0}); s.idleInjector.stop(); s.runningInjector.stop(); return s; } }; var vehicleSounds = sounds.setup(); var camera = { setup: function () { var cam = {active: false, offset: {x: 0, y: 2, z: 7}, speedPos: 10, speedRot: 5}; cam.update = function (deltaTime) { if (cam.active && !HMD.active) { var endPos = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(spinOut.active ? Camera.orientation : MyAvatar.orientation, cam.offset)); Camera.position = Vec3.mix(Camera.position, endPos, cam.speedPos * deltaTime); if (!spinOut.active) { var endRot = MyAvatar.orientation; Camera.orientation = Quat.slerp(Camera.orientation, endRot, cam.speedRot * deltaTime); } } }; return cam; } }; var kartCamera = camera.setup(); var spinOut = { active: false, amount: 12, target: 1080, count: 1, lastAmount: 0, spinOut: function (deltaTime) { spinOut.count -= deltaTime / INT_2; spinOut.amount = spinOut.target * cubicIn(spinOut.count); MyAvatar.orientation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollDegrees(0, spinOut.amount - spinOut.lastAmount, 0)); spinOut.lastAmount = spinOut.amount; if (spinOut.count <= 0) { spinOut.amount = 0; spinOut.count = 1; vel.z = 0; spinOut.active = false; } } }; var boost = { active: false, power: 10, duration: 5, boost: function (power, duration) { print("BoostON"); boost.active = true; boost.power = power; boost.duration = duration; Script.setTimeout(function () { print("BoostOFF"); boost.active = false; }, duration * MS_CONVERSION); } }; var force = 20; var dir = 0; var turn = 0; var air = 0.5; var drift = 0.5; var vel = {x: 0, y: 0, z: 0}; var vehicleYaw = 40; var defaultYaw = 100; var SPEED_RESET_MIN = 0.5; var SPEED_RESET_MAX = 5; var MIN_HMD = 0.05; var MAX_HMD = 0.8; var oldjumpValue = 0; var MOTOR_TIMESCALE = 1.0; var MOTOR_TIMESCALE_MAX = 1000000.0; MyAvatar.motorReferenceFrame = "avatar"; var Vive = Controller.Hardware.Vive; var KEY_SPACE = 32; var controllerMapping; var controllerMappingName; var oldCameraMode = "third person"; Script.scriptEnding.connect(function () { Controller.disableMapping(controllerMappingName); }); controllerMappingName = 'Hifi-HoverKart-Mapping'; controllerMapping = Controller.newMapping(controllerMappingName); function empty(val) { } controllerMapping.from(Controller.Hardware.Keyboard.W).to(function (value) { if (value !== 0) { dir = 1; } else { if (dir === 1) { dir = 0; } } }); controllerMapping.from(Controller.Hardware.Keyboard.S).to(function (value) { if (value !== 0) { dir = -1; } else { if (dir === -1) { dir = 0; } } }); controllerMapping.from(Controller.Hardware.Keyboard.A).to(function (value) { if (value !== 0) { turn = 1; } else { if (turn === 1) { turn = 0; } } }); controllerMapping.from(Controller.Hardware.Keyboard.D).to(function (value) { if (value !== 0) { turn = -1; } else { if (turn === -1) { turn = 0; } } }); controllerMapping.from(Controller.Standard.LY).to(function (value) { if (Vive && Controller.getValue(Controller.Standard.LS) === 0) { dir = 0; return; } if (Math.abs(value) < MIN_HMD) { value = 0; } if (Math.abs(value) > MAX_HMD) { value = 1*Math.sign(value); } dir = -value; }); controllerMapping.from(Controller.Standard.RX).to(function (value) { if (Vive && Controller.getValue(Controller.Standard.RS) === 0) { turn = 0; return; } if (Math.abs(value) < MIN_HMD) { value = 0; } if (Math.abs(value) > MAX_HMD) { value = 1*Math.sign(value); } turn = -value; }); controllerMapping.from(Controller.Standard.LeftPrimaryThumb).to(empty); // utils start if (!Math.sign) { Math.sign = function (x) { return ((x > 0) - (x < 0)) || +x; }; } if (!Math.moveTowards) { Math.moveTowards = function (current, target, maxDelta) { if (Math.abs(target - current) <= maxDelta) { return target; } return current + Math.sign(target - current) * maxDelta; }; } function normalize(value, min, max) { var normalized = (value - min) / (max - min); if (normalized < 0) { normalized = 0; } if (normalized > 1) { normalized = 1; } return normalized; } function cubicIn(t) { return t * t * t; } // utils end function sit() { jointIndex = MyAvatar.getJointIndex("Hips"); MyAvatar.restoreAnimation(); MyAvatar.getAnimationRoles().filter(function (role) { return !/^(right|left)/.test(role); }).forEach(function (role) { MyAvatar.overrideRoleAnimation(role, ANIMATION_URL, ANIMATION_FPS, true, ANIMATION_FIRST_FRAME, ANIMATION_LAST_FRAME); }); Script.setTimeout(function () { MyAvatar.setJointRotation(jointIndex, Quat.IDENTITY); }, TIMEOUT); kartEntity = Entities.addEntity({ visible: true, name: kartName, type: "Model", modelURL: kartURL, parentID: MyAvatar.sessionUUID, parentJointIndex: jointIndex, localPosition: {x: 0, y: 0, z: 0}, localRotation: kartWearRotation, dimensions: Vec3.multiply((SCALE * MyAvatar.scale), kartSize), collisionless: true, userData: "{ \"grabbableKey\": { \"grabbable\": false} }" }, true); } function unsit() { MyAvatar.getAnimationRoles().filter(function (role) { return !/^(right|left)/.test(role); }).forEach(function (role) { MyAvatar.restoreRoleAnimation(role); }); MyAvatar.clearJointData(jointIndex); Entities.deleteEntity(kartEntity); } function toggleCamera(on) { if (on) { oldCameraMode = Camera.mode; if (running && !HMD.active) { Camera.setModeString("independent"); } kartCamera.active = true; } if (!on) { kartCamera.active = false; if (running && !HMD.active) { Camera.setModeString(oldCameraMode === "independent" ? "third person" : oldCameraMode); } } } function toggleSound(on) { if (on) { if (running) { vehicleSounds = sounds.setup(); vehicleSounds.start(); } soundEnabled = true; } if (!on) { soundEnabled = false; if (running) { vehicleSounds.stop(); } } } function timeFunction(deltaTime) { var jumpValue = Controller.getValue(Controller.Standard.RY); if (jumpValue < -MIN_HMD) { jumpValue = 1; } else { jumpValue = 0; } if (oldjumpValue !== jumpValue && oldjumpValue > MIN_HMD) { vehicleSounds.playJump(); } if (oldjumpValue !== jumpValue) { oldjumpValue = jumpValue; } if ((dir !== 0 || boost.active) && vehicleSounds.idleInjector.playing) { vehicleSounds.playRunning(); } else if (dir === 0 && !boost.active && vehicleSounds.runningInjector.playing) { vehicleSounds.playIdle(); } MyAvatar.motorTimescale = MOTOR_TIMESCALE; vel = { x: (MyAvatar.motorVelocity.x + (vel.x * deltaTime)) * (Math.pow(drift, deltaTime)), y: vel.y, z: (vel.z + (((dir * force) + (boost.active ? boost.power : 0)) * deltaTime)) * (Math.pow(air, deltaTime)) }; MyAvatar.motorVelocity = { x: vel.x, y: MyAvatar.motorVelocity.y, z: vel.z }; MyAvatar.orientation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollDegrees(0, (vehicleYaw * deltaTime) * turn, 0)); var localVelocity = Vec3.multiplyQbyV(Quat.inverse(MyAvatar.orientation), MyAvatar.velocity); if (Math.abs(localVelocity.z) < SPEED_RESET_MIN && Math.abs(vel.z) > SPEED_RESET_MAX) { vel.z = 0; } if (spinOut.active) { spinOut.spinOut(deltaTime); } kartCamera.update(deltaTime); } function messageReceived(channel, message) { if (channel === "VROOM_MESSAGE") { var data = JSON.parse(message); if (data.type === "boost") { boost.boost(data.data.power, data.data.duration); } if (data.type === "spinOut") { spinOut.active = true; } } } function keyPressEvent(event) { if (event.key === KEY_SPACE && !event.isAutoRepeat && soundEnabled && running) { vehicleSounds.playJump(); } } Messages.messageReceived.connect(messageReceived); // humbletim's barnacle locator function findBarnacles(name) { return Object.keys(MyAvatar.getAvatarEntityData()) .filter(function (id) { return Entities.getNestableType(id) === 'entity'; }) .map(function (entityID) { return Entities.getEntityProperties(entityID); }) .filter(function (props) { return props.name === name; }); } var oldKarts = findBarnacles(kartName); oldKarts.forEach(function (ent) { Entities.deleteEntity(ent.id); }); // Tablet Code function onClicked() { // Nothing else here! if (isOpen) { tablet.gotoHomeScreen(); } else { tablet.gotoWebScreen(appHTML + "?enabled=" + running + "&camera=" + kartCamera.active + "&sound=" + soundEnabled + "&appUUID=" + appUUID, {}); } } function onScreenChanged(type, url) { isOpen = (url === appHTML); if (isOpen) { tablet.emitScriptEvent(JSON.stringify({state: running})); } } button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); function init() { Controller.keyPressEvent.connect(keyPressEvent); try { tablet.webEventReceived.connect(onWebEventReceived); } catch (e) { print("connectWebHandler: error connecting: " + e); } } function onWebEventReceived(event) { event = JSON.parse(event); if (event.appUUID === appUUID) { if (event.type === "ready") { // vehicleSounds = sounds.setup(); // Bleh } if (event.sound === true) { toggleSound(true); } if (event.sound === false) { toggleSound(false); } if (event.camera === true) { toggleCamera(true); } if (event.camera === false) { toggleCamera(false); } if (event.state === true) { if (soundEnabled) { vehicleSounds = sounds.setup(); vehicleSounds.playRez(); vehicleSounds.start(); } running = true; if (kartCamera.active && !HMD.active) { oldCameraMode = Camera.mode; Camera.setModeString("independent"); } sit(); Controller.enableMapping(controllerMappingName); MyAvatar.setHMDLeanRecenterEnabled(false); defaultYaw = MyAvatar.yawSpeed; MyAvatar.yawSpeed = vehicleYaw; Script.update.connect(timeFunction); } if (event.state === false) { running = false; if (soundEnabled) { vehicleSounds.playDerez(); vehicleSounds.stop(); } if (kartCamera.active && !HMD.active) { Camera.setModeString(oldCameraMode); } unsit(); Script.update.disconnect(timeFunction); MyAvatar.motorTimescale = MOTOR_TIMESCALE_MAX; MyAvatar.setHMDLeanRecenterEnabled(true); Controller.disableMapping(controllerMappingName); MyAvatar.yawSpeed = defaultYaw; } } } function shutdown() { try { tablet.webEventReceived.disconnect(onWebEventReceived); } catch (e) { print("disconnectWebHandler: error disconnecting web handler: " + e); return; } MyAvatar.motorTimescale = MOTOR_TIMESCALE_MAX; MyAvatar.yawSpeed = defaultYaw; if (Camera.mode === "independent") { Camera.setModeString(oldCameraMode); } try { unsit(); } catch (e) { // empty } try { vehicleSounds.stop(); } catch (e) { // empty } try { Controller.keyPressEvent.disconnect(keyPressEvent); } catch (e) { // empty } try { Messages.messageReceived.disconnect(messageReceived); } catch (e) { // empty } try { Script.update.disconnect(timeFunction); } catch (e) { // empty } try { Controller.disableMapping(controllerMappingName); } catch (e) { // empty } try { button.clicked.disconnect(onClicked); } catch (e) { // empty } tablet.screenChanged.disconnect(onScreenChanged); tablet.removeButton(button); } init(); Script.scriptEnding.connect(function () { shutdown(); });