Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * TagPro Neomacro | UserScript by Popcorn.
- * ========================================
- * Are you tired of overly complex chat macro systems? Have you forgotten whether you bound "Yes" to I or U?
- * This is Neomacro -- TagPro macro system that uses arrow keys, ctrl and 0 (zero) (you use WASD to control
- * the game, don't you?) to create powerful and easy-to-remember macro codes.
- *
- * 1.0 update: Redesigned some macros, be sure to take a look.
- * 1.1 update: Simplified code, added more comments
- * 1.2 update: Goodbye, backspace and ctrl+something!
- * 1.3 update: Added special handling for direction macros. Introduced 0 (zero) as a direction-giving key.
- * Added more direction macros.
- *
- * It may contain bugs. If you notice one, please leave comment below ↓↓.
- *
- * Install this UserScript by pressing the <> in the upper right corner of the code ↗
- *
- * Find the macro combinations by scrolling down the script
- * (they're inside the config section, starting with "var teamMacros =", "var directionMacros ="
- * and "var globalMacros =") ↓
- *
- * Enter the combination by pressing the keys one by one (←↑→↓ arrow keys and ● ctrl).
- *
- * Some combinations have aliases: for example, ←↓ is the same as ↓←, and →←→ is the same as ←→←.
- *
- * Here's the macro dictionary. It will help you remember the codes:
- *
- * Directions to enemy FC: two arrow presses, for example:
- * ↑↑ Enemy FC on upper side (↑)
- * ↑→ or →↑ Enemy FC on upper right (↗)
- * Or a zero press:
- * 0 Enemy FC in the middle (●)
- *
- * (Directions to something else see below)
- *
- * "Yes" and "No" codes are similar to head movements (shaking/nodding):
- * yes ↑↓↑ or ↓↑↓
- * no ←→← or →←→
- *
- * Words used in sentences:
- * nouns verbs
- * --------- ---------------
- * I/me ↓ defend ↓
- * enemy ↑ attack ↑
- * trick/tactic ←
- * use button →
- *
- * Indicative sentences: (noun)●(verb)
- * examples: ↓●↓ I'll defend.
- * ↑●← Enemy's trying to trick us!
- *
- * Questions: ●(verb)
- * Be aware about ●↑ Where's enemy FC?
- * examples: ●↓ Is our flag safe?
- * ●← What's our plan? How many defenders?
- * ●→ Is the button safe?
- *
- * Giving commands: (verb)●●
- * examples: ↓●● Defend our flag!
- * ←●● Trick the enemy!
- *
- * Giving directions: (noun)0(direction)
- * ↑0↑↑ Enemy on top. (↑)
- * ↓0↓↓ I'm coming bottom. (↓)
- * ←0←↑ Upper left is safe. (↖)
- * Directions to enemy FC are unprefixed, as mentioned before.
- *
- * Emotes (messages chosen randomly):
- * ●●↑ happy
- * ●●↓ angry
- * ●●← really bad jokes
- * ●●→ promote Neomacro ('Cos it's best, that's why.)
- *
- * Have fun and don't forget to report bugs.
- */
- // ==UserScript==
- // @name TagPro Neomacro
- // @namespace http://tiny.cc/neomacro
- // @description Based on https://gist.github.com/steppin/5292526 by http://www.reddit.com/user/contact_lens_linux/
- // @require https://gist.github.com/chrahunt/4843f0258c516882eea0/raw/8d5f273ca484e3c480ec0fde70cdc5861344388a/loopback.user.js
- // @include http://tagpro-*.koalabeast.com:*
- // @include http://maptest*.newcompte.fr:*
- // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
- // @author Popcorn
- // @version 1.3
- // @run-at document-start
- // ==/UserScript==
- // Body of this function is injected into the HTML page
- function Script()
- {
- /**************************
- * *
- * START OF CONFIG SECTION *
- * *
- **************************/
- // These are JS keycodes (event.keyCode) with corresponding character
- // Find them here: http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
- var buttons = {
- 37:"←", // left arrow
- 38:"↑", // up arrow
- 39:"→", // right arrow
- 40:"↓", // down arrow
- 17:"●", // ctrl
- 96:"0" // numpad 0
- };
- // All generated messages are prefixed with this
- var messagePrefix = "";
- // Maximal interval beteween two keypresses (ms)
- var keypressLimit = 1000;
- // Minimal interval beteween two messages sent (ms)
- var messageLimit = 300;
- // All macros support random messages - for example, you can replace
- // "↑↓↑": "Yes"
- // with
- // "↑↓↑": ["Yes", "Yeah", "Yea", "Whatever..."]
- // Macros sent to your team only (the T-key chat)
- var teamMacros = {
- // Yes/No
- "←→←": ["no", "no"],
- "↑↓↑": ["yes", "sí"],
- // Indicative sentences
- "↓●↑": ["o", "O"],
- "↓●↓": ["d", "D"],
- "↓●←": "I've got regrab",
- "↓●→": "I'll get the button",
- "↑●↑": "Enemy's attacking!",
- "↑●↓": "Enemy's defending!",
- "↑●←": "Enemy's trying to trick us!",
- "↑●→": "Enemy's controlling the button!",
- // Questions
- "●↓" : "is our flag safe?",
- "●↑" : "where is enemy fc?",
- "●←" : "What's our plan? How many defenders?",
- "●→" : "Who's controlling the button?",
- // Commands
- "↓●●" : "2 defense, please",
- "↑●●" : "Attack!",
- "←●●" : "someone please get the powerup on your way out",
- "→●●" : "please don't use the boost out of base",
- };
- // Prefixes for giving directions. Two arrow presses indicating the direction comes after them.
- // First macro is empty: it's triggered just by two arrow presses
- // [Direction], [direction] and [arrow] are wildcards.
- // Sent to your team only.
- var directionMacros = {
- "" : "[arrow][arrow] ENEMY FC IS [direction] [arrow][arrow]",
- "↑0" : "Enemy on [direction]. ([arrow])",
- "↓0" : "I'm coming [direction]. ([arrow])",
- "←0" : "[Direction] is safe. ([arrow])",
- "→0" : "[Direction] powerup is respawning soon/has respawned. ([arrow])",
- "●0" : "[player_relation] got [pup_type] on [direction] @ [time].",
- };
- // Macros sent to everyone (Enter-key chat)
- var globalMacros = {
- "●●↑" : [ // Positive emotions
- "Yea!",
- "We're good!",
- "YES WE CAN",
- "Dream team.",
- "Well played."
- ],
- "●●↓" : [ // Angry emotions
- "Shazbot!",
- "Oh noez!",
- "I've let you win this time.",
- "C'mon team!",
- "This made me angry."
- ],
- "●●←" : [ // Bad jokes
- "WALRI never lose.",
- "WALRI make the best dancers.",
- "WALRI WALRI."
- ],
- "●●→" : "Neomacro! TagPro macro system. http://tiny.cc/neomacro" // Use often. Very often.
- };
- // Aliases for some combinations. Alias comes first, real code second.
- // You can setup more aliases for a macro.
- // If you setup the same alias as the macro itself (for example, "←←":"←←"), the universe will explode
- var aliases = {
- "→←→":"←→←", // No
- "↓↑↓":"↑↓↑", // Yes
- "●●●":"0" // Backwards compatibility
- };
- var debugMacros = {
- "0●0": true
- };
- /************************
- * *
- * END OF CONFIG SECTION *
- * *
- ************************/
- // hashArray output -> map names
- var maps = {
- "1125424804": "star",
- "1726053568": "ricochet",
- "-391837510": "iron",
- "351716786": "shine",
- "-1152475206": "dz3"
- }
- // Why would you change this?
- var directions = {
- "↓←": ["LOWER LEFT", "↙"],
- "←←": ["LEFT", "←"],
- "↑←": ["UPPER LEFT", "↖"],
- "↑↑": ["TOP", "↑"],
- "↑→": ["UPPER RIGHT", "↗"],
- "→→": ["RIGHT", "→"],
- "↓→": ["LOWER RIGHT", "↘"],
- "↓↓": ["BOTTOM", "↓"],
- "00": ["MIDDLE", "●"]
- };
- directions["←↓"] = directions["↓←"];
- directions["←↑"] = directions["↑←"];
- directions["→↓"] = directions["↓→"];
- directions["→↑"] = directions["↑→"];
- // Because the game's keypress handlers are more prioritized, they're circumvented using a dummy input
- var handlerbtn = document.getElementById("macrohandlerbutton");
- handlerbtn.focus();
- handlerbtn.addEventListener('keydown', keydownHandler, false);
- handlerbtn.addEventListener('keyup', keyupHandler, false);
- document.addEventListener('keydown', documentKeydown, false);
- function documentKeydown(event)
- {
- if(!tagpro.disableControls)
- {
- handlerbtn.focus(); // The handler button should be always fucused
- // Disables backspace and all ctrl interactions -- prevents leaving page by accident
- if((event.keyCode==8 || event.ctrlKey) && !tagpro.disableControls)
- {
- event.preventDefault();
- event.stopPropagation();
- return false;
- }
- }
- }
- // Relasing arrow key tricks TagPro to think that you relased WASD-key too, even if you didn't
- // This code prevents that from happening
- function keyupHandler(event)
- {
- if(event.keyCode in buttons && !tagpro.disableControls)
- {
- event.preventDefault();
- event.stopPropagation();
- }
- }
- // Test function for identifying a map.
- function hashArray(arr) {
- var arrStr = JSON.stringify(arr);
- var hash = 0, i, chr, len;
- if (arrStr.length === 0) return hash;
- for (i = 0, len = arrStr.length; i < len; i++) {
- chr = arrStr.charCodeAt(i);
- hash = ((hash << 5) - hash) + chr;
- hash |= 0; // Convert to 32bit integer
- }
- return hash;
- }
- // Main macro keypresses handler
- var lastKey = 0;
- var currentMacro = "";
- function keydownHandler(event)
- {
- if(!(event.keyCode in buttons) || tagpro.disableControls)
- return;
- event.preventDefault();
- event.stopPropagation();
- var now = new Date();
- if((now - lastKey) > keypressLimit)
- {
- currentMacro = "";
- }
- lastKey = now;
- currentMacro += buttons[event.keyCode];
- var message = getMacro(currentMacro);
- if (message && message.debugging) {
- chat(hashArray(tagpro.wallMap), true);
- currentMacro = "";
- } else if(message) {
- chat(message);
- currentMacro = "";
- }
- }
- // Utility function to get corresponding message for macro code
- function getMacro(x)
- {
- function capitalize(s)
- {
- return s[0].toUpperCase() + s.slice(1);
- }
- function isDirectionMacro(s)
- {
- /*console.log("isDirectionMacro"); // DEBUG
- console.log("directionMacro: " + s.substring(0, s.length-2)); // DEBUG
- console.log("in durectionMacros: " + s.substring(0,s.length-2) in directionMacros); // DEBUG
- console.log("direction: " + s.substring(s.length-2, s.length)); // DEBUG // DEBUG
- console.log("in directions: " + s.substring(s.length-2, s.length) in directions); // DEBUG*/
- return s.substring(0,s.length-2) in directionMacros && s.substring(s.length-2, s.length) in directions;
- }
- function getDirectionMacro(s)
- {
- // Takes the id of another player and gives their relationship to you as a string.
- function getPlayerString(otherPlayerId) {
- if (otherPlayerId == tagpro.playerId)
- return "I";
- var myTeam = tagpro.players[tagpro.playerId].team;
- if (tagpro.players[otherPlayerId].team == myTeam) {
- return "Teammate";
- } else {
- return "Enemy";
- }
- }
- var directionType = s.substring(0,s.length-2);
- var direction = directions[s.substring(s.length-2, s.length)];
- //console.log("Direction:"); // DEBUG
- //console.log(direction); // DEBUG
- // Powerup direction declaration.
- if (directionType == "●0") {
- // Check for recent powerup info; within 5 seconds.
- var currentTime = (new Date()).getTime();
- if (lastPowerupSeen && (currentTime - lastPowerupSeen.time <= 5e3)) {
- var playerString = getPlayerString(lastPowerupSeen.player);
- var pupString = powerupNames[lastPowerupSeen.type];
- var timeString = lastPowerupSeen.game_timer;
- return select(directionMacros[directionType])
- .replace(/\[Direction\]/g, capitalize(direction[0]))
- .replace(/\[direction\]/g, direction[0])
- .replace(/\[arrow\]/g, direction[1])
- .replace(/\[player_relation\]/g, playerString)
- .replace(/\[pup_type\]/g, pupString)
- .replace(/\[time\]/g, timeString);
- } else {
- return false;
- }
- }
- // Normal direction handling.
- return select(directionMacros[directionType])
- .replace(/\[Direction\]/g, capitalize(direction[0]))
- .replace(/\[direction\]/g, direction[0])
- .replace(/\[arrow\]/g, direction[1]);
- }
- if(isDirectionMacro(x)) {
- var text = getDirectionMacro(x);
- if (text) {
- return {text:text, global:0};
- } else {
- return false;
- }
- }
- // When array is supplied, return random message - ideal to add variety to your messages
- function select(x)
- {
- if(x instanceof Array)
- return x[Math.floor(Math.random()*x.length)];
- return x;
- }
- if(x in teamMacros)
- return {text:select(teamMacros[x]), global:0};
- if(x in globalMacros)
- return {text:select(globalMacros[x]), global:1};
- if(x in aliases)
- return getMacro(aliases[x]);
- if(x in debugMacros)
- return {debugging:true};
- return false;
- }
- // This functions does what expected - sends a chat message
- var lastMessage = 0;
- function chat(chatMessage, group)
- {
- if (typeof group == "undefined") group = false;
- var now = new Date();
- var timeDiff = now - lastMessage;
- if (timeDiff > messageLimit)
- {
- if (!group) {
- tagpro.socket.emit("chat",
- {
- message: messagePrefix + chatMessage.text,
- toAll: chatMessage.global
- });
- } else {
- tagpro.group.socket.emit("chat", chatMessage);
- }
- lastMessage = new Date();
- }
- else if (timeDiff >= 0)
- {
- setTimeout(function() {
- chat(chatMessage, group);
- }, messageLimit - timeDiff);
- }
- }
- // Ids for powerup attributes on players and in socket messages.
- var powerupIds = [
- "bomb",
- "grip",
- "tagpro",
- "speed"
- ];
- // Names of powerups for chat.
- var powerupNames = {
- "bomb": "rolling bomb",
- "grip": "juke juice",
- "tagpro": "tagpro",
- "speed": "speed pup"
- };
- // The last powerup seen.
- var lastPowerupSeen = false;
- /* Listens for powerup information (player update frame with attribute from powerupIds set to true). If one
- * is found, then it sets lastPowerupSeen to an object with properties
- * - type: the type of powerup (from the powerupIds array).
- * - game_timer: string with the time left in the game when the frame was seen
- * - player: id of the player the update pertained to.
- * - time: the current time (in ms; result of getTime() on a new date object)
- */
- function powerupListener(data) {
- //console.log("Listening"); // DEBUG
- var time = data.t;
- var updates = data.u;
- var pup_data = [];
- if (!updates) return;
- // There should only be one, but in case there are multiple.
- updates.forEach(function(update) {
- for (var i = 0; i < powerupIds.length; i++) {
- var pup = powerupIds[i];
- // Check if property is present and true.
- if (update.hasOwnProperty(pup) && update[pup]) {
- //console.log("Found powerup frame."); // DEBUG
- var current_time = (new Date()).getTime();
- var time_left_ms = tagpro.gameEndsAt - current_time;
- var mins = "00" + Math.floor(time_left_ms / 6e4);
- var secs = "00" + Math.floor(time_left_ms % 6e4 / 1e3);
- var display_time = mins.substr(mins.length - 2, 2) + ":" + secs.substr(secs.length - 2, 2);
- pup_data.push({
- "type": pup,
- "game_timer": display_time,
- "player": update.id,
- "time": current_time
- });
- //console.log(pup_data); // DEBUG
- break;
- }
- }
- }, this);
- // Make available to outside script.
- if (pup_data.length > 0) {
- if (pup_data.length !== 1) {
- console.log("Powerup anomaly!"); // DEBUG
- }
- lastPowerupSeen = pup_data[0];
- }
- }
- var addSocketListener = function(id, fn) {
- //console.log("Checking for socket."); // DEBUG
- if (tagpro.socket) {
- //console.log("Socket found, listening..."); // DEBUG
- tagpro.socket.on(id, fn);
- } else {
- //console.log("Socket not found, retrying..."); // DEBUG
- setTimeout(function() {
- addSocketListener(id, fn);
- }.bind(this), 250);
- }
- }.bind(this);
- addSocketListener("p", powerupListener);
- //console.log("Script set."); // DEBUG
- var addToTagproReady = function(fn) {
- if (tagpro) {
- tagpro.ready(fn);
- } else {
- setTimeout(function() {
- addToTagproReady(fn);
- }, 0);
- }
- }
- var waitForChat = function(fn) {
- if (tagpro && tagpro.playerId) {
- // Additional delay is for compatibility with chat enhancer.
- setTimeout(fn, 1000);
- } else {
- setTimeout(function() {
- waitForChat(fn);
- }, 0);
- }
- }
- waitForChat(function() {
- if (io && io.__loopback) {
- console.log("Loopback loaded.");
- tagpro.socket.emit("local:chat", {
- to: "all",
- from: null,
- message: "Tagpro Neomacro Loaded!"
- });
- } else {
- console.log("Loopback not loaded.");
- }
- });
- }
- var addToPage = function() {
- // This dummy input will handle macro keypresses
- var btn = document.createElement("input");
- btn.style.opacity = 0;
- btn.style.position = "absolute";
- btn.style.top = "-100px";
- btn.style.left = "-100px";
- btn.id = "macrohandlerbutton";
- document.body.appendChild(btn);
- // Create a script node holding this source code
- var script = document.createElement('script');
- script.setAttribute("type", "application/javascript");
- script.textContent = '(' + Script + ')();';
- document.body.appendChild(script);
- };
- var runOnBody = function(fn) {
- //console.log("Checking for body"); // DEBUG
- if (document.body) {
- //console.log("Body found, executing."); // DEBUG
- fn();
- } else {
- //console.log("Body not found, retrying..."); // DEBUG
- setTimeout(function() {
- runOnBody(fn);
- }, 150);
- }
- };
- var runOnTagpro = function(fn) {
- if (typeof tagpro !== 'undefined') {
- fn();
- } else {
- setTimeout(function() {
- runOnTagpro(fn);
- }, 0);
- }
- }
- runOnTagpro(function() {
- tagpro.ready(function() {
- runOnBody(addToPage);
- });
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement