// ==UserScript== // @name TagPro Smooth Spectator Cam // @version 1.1 // @author browncoat // @description Smoothly pan the camera between players when spectating // @include http://tagpro-*.koalabeast.com:* // @include http://tangent.jukejuice.com* // @include http://*.newcompte.fr* // ==/UserScript== tagpro.ready(function () { // OPTIONS // true: camera will 'smooth' whether you are playing or spectating // false: camera will 'smooth' only when spectating var smoothAsPlayerAsWell = false; // "linear": camera will pan at a constant speed // "ease_out": camera will smoothly slow down as it reaches its target (like a car coming to a stop) // "elastic": camera will bounce slightly back and forth until settling on the target (like a spring) var followType = "ease_out"; // Between 0 and 1. Closer to 1 means the camera will reach the target quicker. var cameraSpeed = 0.3; // END OPTIONS var tr = tagpro.renderer; var state = "snap"; var initialSet = false; var targetX = 0; var targetY = 0; var cameraX = 0; var cameraY = 0; var accX = 0; var accY = 0; var linearCameraSpeed = cameraSpeed * 100; var snapDistance = 10; var startEaseThreshold = 80; var FollowType = {EASE_OUT: "ease_out", LINEAR: "linear", ELASTIC: "elastic"}; // Override this to store the desired or 'target' position for the camera to follow var centerContainerToPoint = tr.centerContainerToPoint; tr.centerContainerToPoint = function (x, y) { if (!initialSet) { initialSet = true; cameraX = targetX; cameraY = targetY; } targetX = x; targetY = y; if (state == "snap") { var xd = targetX - cameraX; var yd = targetY - cameraY; var distance = Math.sqrt(xd * xd + yd * yd); if (distance > startEaseThreshold) { state = "easing"; console.log("Start ease"); } } }; function checkSnap() { // If the cam is close enough just snap to the target if (Math.abs(targetX - cameraX) <= snapDistance && Math.abs(targetY - cameraY) <= snapDistance) { cameraX = targetX; cameraY = targetY; state = "snap"; console.log("Ease stop"); } } // Override to run the smoothing function every render frame, after all other graphics have been updated var updateGraphics = tr.updateGraphics; tr.updateGraphics = function () { updateGraphics(); if (smoothAsPlayerAsWell || tagpro.spectator) { if (state == "snap") { cameraX = targetX; cameraY = targetY; } else if (state == "easing") { // Easing function if (followType == FollowType.LINEAR) { cameraX += Math.sign(targetX - cameraX) * linearCameraSpeed; cameraY += Math.sign(targetY - cameraY) * linearCameraSpeed; checkSnap(); } else if (followType == FollowType.EASE_OUT) { cameraX += (targetX - cameraX) * cameraSpeed; cameraY += (targetY - cameraY) * cameraSpeed; checkSnap(); } else if (followType == FollowType.ELASTIC) { accX += (targetX - cameraX) * cameraSpeed; accY += (targetY - cameraY) * cameraSpeed; accX *= 0.8; accY *= 0.8; cameraX += accX; cameraY += accY; // If the cam is close enough just snap to the target var lowDistance = Math.abs(targetX - cameraX) <= snapDistance && Math.abs(targetY - cameraY) <= snapDistance; var lowAcc = Math.abs(accX) <= snapDistance && Math.abs(accY) <= snapDistance; if (lowDistance && lowAcc) { cameraX = targetX; cameraY = targetY; state = "snap"; } } } } else { // Don't smooth if not spectating cameraX = targetX; cameraY = targetY; } // Run default tr.centerContainerToPoint - actually sets the camera position // centerContainerToPoint(cameraX, cameraY); var viewport = $('#viewport'); var vpWidth = viewport.outerWidth(); var vpHeight = viewport.outerHeight(); var resizeScaleFactor = (vpHeight / tr.canvas_height).toFixed(2); tr.gameContainer.x = Math.round(vpWidth / 2 - (cameraX / tagpro.zoom * resizeScaleFactor)); tr.gameContainer.y = Math.round(vpHeight / 2 - (cameraY / tagpro.zoom * resizeScaleFactor)); }; });