Advertisement
chrahunt

TagPro Neomacro+

Dec 19th, 2014
137
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2. *   TagPro Neomacro | UserScript by Popcorn.
  3. *   ========================================
  4. *   Are you tired of overly complex chat macro systems? Have you forgotten whether you bound "Yes" to I or U?
  5. *   This is Neomacro -- TagPro macro system that uses arrow keys, ctrl and 0 (zero) (you use WASD to control
  6. *   the game, don't you?) to create powerful and easy-to-remember macro codes.
  7. *  
  8. *   1.0 update: Redesigned some macros, be sure to take a look.
  9. *   1.1 update: Simplified code, added more comments
  10. *   1.2 update: Goodbye, backspace and ctrl+something!
  11. *   1.3 update: Added special handling for direction macros. Introduced 0 (zero) as a direction-giving key.
  12. *               Added more direction macros.
  13. *
  14. *   It may contain bugs. If you notice one, please leave comment below ↓↓.
  15. *  
  16. *   Install this UserScript by pressing the <> in the upper right corner of the code ↗
  17. *  
  18. *   Find the macro combinations by scrolling down the script
  19. *   (they're inside the config section, starting with "var teamMacros =", "var directionMacros ="
  20. *   and "var globalMacros =") ↓
  21. *  
  22. *   Enter the combination by pressing the keys one by one (←↑→↓ arrow keys and ● ctrl).
  23. *  
  24. *   Some combinations have aliases: for example, ←↓ is the same as ↓←, and →←→ is the same as ←→←.
  25. *  
  26. *   Here's the macro dictionary. It will help you remember the codes:
  27. *  
  28. *   Directions to enemy FC: two arrow presses, for example:
  29. *   ↑↑ Enemy FC on upper side (↑)
  30. *   ↑→ or →↑ Enemy FC on upper right (↗)
  31. *   Or a zero press:
  32. *   0 Enemy FC in the middle (●)
  33. *
  34. *   (Directions to something else see below)
  35. *
  36. *   "Yes" and "No" codes are similar to head movements (shaking/nodding):
  37. *   yes  ↑↓↑ or ↓↑↓
  38. *   no   ←→← or →←→
  39. *
  40. *     Words used in sentences:
  41. *     nouns           verbs
  42. *   ---------    ---------------
  43. *   I/me    ↓    defend        ↓
  44. *   enemy   ↑    attack        ↑
  45. *                trick/tactic  ←
  46. *                use button    →
  47. *  
  48. *   Indicative sentences: (noun)●(verb)
  49. *   examples: ↓●↓ I'll defend.
  50. *             ↑●← Enemy's trying to trick us!
  51. *
  52. *   Questions: ●(verb)
  53. *   Be aware about ●↑ Where's enemy FC?
  54. *   examples: ●↓ Is our flag safe?
  55. *             ●← What's our plan? How many defenders?
  56. *             ●→ Is the button safe?
  57. *
  58. *   Giving commands: (verb)●●
  59. *   examples: ↓●● Defend our flag!
  60. *             ←●● Trick the enemy!
  61. *
  62. *   Giving directions: (noun)0(direction)
  63. *             ↑0↑↑  Enemy on top. (↑)
  64. *             ↓0↓↓  I'm coming bottom. (↓)
  65. *             ←0←↑  Upper left is safe. (↖)
  66. *   Directions to enemy FC are unprefixed, as mentioned before.
  67. *
  68. *   Emotes (messages chosen randomly):
  69. *   ●●↑ happy
  70. *   ●●↓ angry
  71. *   ●●← really bad jokes
  72. *   ●●→ promote Neomacro ('Cos it's best, that's why.)
  73. *  
  74. *   Have fun and don't forget to report bugs.
  75. */
  76.  
  77. // ==UserScript==
  78. // @name TagPro Neomacro
  79. // @namespace http://tiny.cc/neomacro
  80. // @description Based on https://gist.github.com/steppin/5292526 by http://www.reddit.com/user/contact_lens_linux/
  81. // @require       https://gist.github.com/chrahunt/4843f0258c516882eea0/raw/8d5f273ca484e3c480ec0fde70cdc5861344388a/loopback.user.js
  82. // @include http://tagpro-*.koalabeast.com:*
  83. // @include http://maptest*.newcompte.fr:*
  84. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  85. // @author Popcorn
  86. // @version 1.3
  87. // @run-at document-start
  88. // ==/UserScript==
  89.  
  90. // Body of this function is injected into the HTML page
  91. function Script()
  92. {
  93.     /**************************
  94.     *                         *
  95.     * START OF CONFIG SECTION *
  96.     *                         *
  97.     **************************/
  98.  
  99.     // These are JS keycodes (event.keyCode)  with corresponding character
  100.     // Find them here: http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
  101.     var buttons = {
  102.         37:"←", // left arrow
  103.         38:"↑", // up arrow
  104.         39:"→", // right arrow
  105.         40:"↓", // down arrow
  106.         17:"●", // ctrl
  107.         96:"0" // numpad 0
  108.     };
  109.  
  110.     // All generated messages are prefixed with this
  111.     var messagePrefix = "";
  112.  
  113.     // Maximal interval beteween two keypresses (ms)
  114.     var keypressLimit = 1000;
  115.  
  116.     // Minimal interval beteween two messages sent (ms)
  117.     var messageLimit = 300;
  118.  
  119.     // All macros support random messages - for example, you can replace
  120.     // "↑↓↑": "Yes"
  121.     // with
  122.     // "↑↓↑": ["Yes", "Yeah", "Yea", "Whatever..."]
  123.  
  124.     // Macros sent to your team only (the T-key chat)
  125.     var teamMacros = {
  126.         // Yes/No
  127.         "←→←": ["no", "no"],
  128.         "↑↓↑": ["yes", "sí"],
  129.  
  130.         // Indicative sentences
  131.         "↓●↑": ["o", "O"],
  132.         "↓●↓": ["d", "D"],
  133.         "↓●←": "I've got regrab",
  134.         "↓●→": "I'll get the button",
  135.         "↑●↑": "Enemy's attacking!",
  136.         "↑●↓": "Enemy's defending!",
  137.         "↑●←": "Enemy's trying to trick us!",
  138.         "↑●→": "Enemy's controlling the button!",
  139.        
  140.         // Questions
  141.         "●↓" : "is our flag safe?",
  142.         "●↑" : "where is enemy fc?",
  143.         "●←" : "What's our plan? How many defenders?",
  144.         "●→" : "Who's controlling the button?",
  145.  
  146.         // Commands
  147.         "↓●●" : "2 defense, please",
  148.         "↑●●" : "Attack!",
  149.         "←●●" : "someone please get the powerup on your way out",
  150.         "→●●" : "please don't use the boost out of base",
  151.     };
  152.  
  153.     // Prefixes for giving directions. Two arrow presses indicating the direction comes after them.
  154.     // First macro is empty: it's triggered just by two arrow presses
  155.     // [Direction], [direction] and [arrow] are wildcards.
  156.     // Sent to your team only.
  157.     var directionMacros = {
  158.         "" : "[arrow][arrow] ENEMY FC IS [direction] [arrow][arrow]",
  159.         "↑0" : "Enemy on [direction]. ([arrow])",
  160.         "↓0" : "I'm coming [direction]. ([arrow])",
  161.         "←0" : "[Direction] is safe. ([arrow])",
  162.         "→0" : "[Direction] powerup is respawning soon/has respawned. ([arrow])",
  163.         "●0" : "[player_relation] got [pup_type] on [direction] @ [time].",
  164.     };
  165.  
  166.     // Macros sent to everyone (Enter-key chat)
  167.     var globalMacros = {
  168.         "●●↑" : [ // Positive emotions
  169.             "Yea!",
  170.             "We're good!",
  171.             "YES WE CAN",
  172.             "Dream team.",
  173.             "Well played."
  174.         ],
  175.         "●●↓" : [ // Angry emotions
  176.             "Shazbot!",
  177.             "Oh noez!",
  178.             "I've let you win this time.",
  179.             "C'mon team!",
  180.             "This made me angry."
  181.         ],
  182.         "●●←" : [ // Bad jokes
  183.             "WALRI never lose.",
  184.             "WALRI make the best dancers.",
  185.             "WALRI WALRI."
  186.         ],
  187.         "●●→" : "Neomacro! TagPro macro system. http://tiny.cc/neomacro" // Use often. Very often.
  188.     };
  189.  
  190.     // Aliases for some combinations. Alias comes first, real code second.
  191.     // You can setup more aliases for a macro.
  192.     // If you setup the same alias as the macro itself (for example, "←←":"←←"), the universe will explode
  193.     var aliases = {
  194.         "→←→":"←→←", // No
  195.         "↓↑↓":"↑↓↑", // Yes
  196.         "●●●":"0" // Backwards compatibility
  197.     };
  198.  
  199.     var debugMacros = {
  200.         "0●0": true
  201.     };
  202.    
  203.     /************************
  204.     *                       *
  205.     * END OF CONFIG SECTION *
  206.     *                       *
  207.     ************************/
  208.    
  209.     // hashArray output -> map names
  210.     var maps = {
  211.         "1125424804": "star",
  212.         "1726053568": "ricochet",
  213.         "-391837510": "iron",
  214.         "351716786": "shine",
  215.         "-1152475206": "dz3"
  216.     }
  217.    
  218.     // Why would you change this?
  219.     var directions = {
  220.         "↓←": ["LOWER LEFT", "↙"],
  221.         "←←": ["LEFT", "←"],
  222.         "↑←": ["UPPER LEFT", "↖"],
  223.         "↑↑": ["TOP", "↑"],
  224.         "↑→": ["UPPER RIGHT", "↗"],
  225.         "→→": ["RIGHT", "→"],
  226.         "↓→": ["LOWER RIGHT", "↘"],
  227.         "↓↓": ["BOTTOM", "↓"],
  228.         "00":  ["MIDDLE", "●"]
  229.     };
  230.  
  231.     directions["←↓"] = directions["↓←"];
  232.     directions["←↑"] = directions["↑←"];
  233.     directions["→↓"] = directions["↓→"];
  234.     directions["→↑"] = directions["↑→"];
  235.  
  236.     // Because the game's keypress handlers are more prioritized, they're circumvented using a dummy input
  237.     var handlerbtn = document.getElementById("macrohandlerbutton");
  238.     handlerbtn.focus();
  239.     handlerbtn.addEventListener('keydown', keydownHandler, false);
  240.     handlerbtn.addEventListener('keyup', keyupHandler, false);
  241.    
  242.  
  243.     document.addEventListener('keydown', documentKeydown, false);
  244.     function documentKeydown(event)
  245.     {
  246.         if(!tagpro.disableControls)
  247.         {
  248.             handlerbtn.focus(); // The handler button should be always fucused
  249.  
  250.             // Disables backspace and all ctrl interactions -- prevents leaving page by accident
  251.             if((event.keyCode==8 || event.ctrlKey) && !tagpro.disableControls)
  252.             {
  253.                 event.preventDefault();
  254.                 event.stopPropagation();
  255.                 return false;
  256.             }
  257.         }
  258.     }
  259.  
  260.     // Relasing arrow key tricks TagPro to think that you relased WASD-key too, even if you didn't
  261.     // This code prevents that from happening
  262.     function keyupHandler(event)
  263.     {
  264.         if(event.keyCode in buttons && !tagpro.disableControls)
  265.         {
  266.             event.preventDefault();
  267.             event.stopPropagation();
  268.         }
  269.     }
  270.    
  271.     // Test function for identifying a map.
  272.     function hashArray(arr) {
  273.         var arrStr = JSON.stringify(arr);
  274.         var hash = 0, i, chr, len;
  275.         if (arrStr.length === 0) return hash;
  276.         for (i = 0, len = arrStr.length; i < len; i++) {
  277.             chr   = arrStr.charCodeAt(i);
  278.             hash  = ((hash << 5) - hash) + chr;
  279.             hash |= 0; // Convert to 32bit integer
  280.         }
  281.         return hash;
  282.     }
  283.  
  284.     // Main macro keypresses handler
  285.     var lastKey = 0;
  286.     var currentMacro = "";
  287.     function keydownHandler(event)
  288.     {
  289.         if(!(event.keyCode in buttons) || tagpro.disableControls)
  290.             return;
  291.  
  292.         event.preventDefault();
  293.         event.stopPropagation();
  294.  
  295.         var now = new Date();
  296.         if((now - lastKey) > keypressLimit)
  297.         {
  298.             currentMacro = "";
  299.         }
  300.         lastKey = now;
  301.  
  302.         currentMacro += buttons[event.keyCode];
  303.  
  304.         var message = getMacro(currentMacro);
  305.         if (message && message.debugging) {
  306.             chat(hashArray(tagpro.wallMap), true);
  307.             currentMacro = "";
  308.         } else if(message) {
  309.             chat(message);
  310.             currentMacro = "";
  311.         }
  312.     }
  313.  
  314.     // Utility function to get corresponding message for macro code
  315.     function getMacro(x)
  316.     {
  317.         function capitalize(s)
  318.         {
  319.             return s[0].toUpperCase() + s.slice(1);
  320.         }
  321.  
  322.         function isDirectionMacro(s)
  323.         {
  324.             /*console.log("isDirectionMacro"); // DEBUG
  325.             console.log("directionMacro: " + s.substring(0, s.length-2)); // DEBUG
  326.             console.log("in durectionMacros: " + s.substring(0,s.length-2) in directionMacros); // DEBUG
  327.             console.log("direction: " + s.substring(s.length-2, s.length)); // DEBUG // DEBUG
  328.             console.log("in directions: " + s.substring(s.length-2, s.length) in directions); // DEBUG*/
  329.             return s.substring(0,s.length-2) in directionMacros && s.substring(s.length-2, s.length) in directions;
  330.         }
  331.  
  332.         function getDirectionMacro(s)
  333.         {
  334.             // Takes the id of another player and gives their relationship to you as a string.
  335.             function getPlayerString(otherPlayerId) {
  336.                 if (otherPlayerId == tagpro.playerId)
  337.                     return "I";
  338.                 var myTeam = tagpro.players[tagpro.playerId].team;
  339.                 if (tagpro.players[otherPlayerId].team == myTeam) {
  340.                     return "Teammate";
  341.                 } else {
  342.                     return "Enemy";
  343.                 }
  344.             }
  345.            
  346.             var directionType = s.substring(0,s.length-2);
  347.             var direction = directions[s.substring(s.length-2, s.length)];
  348.             //console.log("Direction:"); // DEBUG
  349.             //console.log(direction); // DEBUG
  350.             // Powerup direction declaration.
  351.             if (directionType == "●0") {
  352.                 // Check for recent powerup info; within 5 seconds.
  353.                 var currentTime = (new Date()).getTime();
  354.                 if (lastPowerupSeen && (currentTime - lastPowerupSeen.time <= 5e3)) {
  355.                     var playerString = getPlayerString(lastPowerupSeen.player);
  356.                     var pupString = powerupNames[lastPowerupSeen.type];
  357.                     var timeString = lastPowerupSeen.game_timer;
  358.                     return select(directionMacros[directionType])
  359.                         .replace(/\[Direction\]/g, capitalize(direction[0]))
  360.                         .replace(/\[direction\]/g, direction[0])
  361.                         .replace(/\[arrow\]/g, direction[1])
  362.                         .replace(/\[player_relation\]/g, playerString)
  363.                         .replace(/\[pup_type\]/g, pupString)
  364.                         .replace(/\[time\]/g, timeString);
  365.                 } else {
  366.                     return false;
  367.                 }
  368.             }
  369.            
  370.             // Normal direction handling.
  371.             return select(directionMacros[directionType])
  372.                 .replace(/\[Direction\]/g, capitalize(direction[0]))
  373.                 .replace(/\[direction\]/g, direction[0])
  374.                 .replace(/\[arrow\]/g, direction[1]);
  375.         }
  376.  
  377.         if(isDirectionMacro(x)) {
  378.             var text = getDirectionMacro(x);
  379.             if (text) {
  380.                 return {text:text, global:0};
  381.             } else {
  382.                 return false;
  383.             }
  384.         }
  385.  
  386.         // When array is supplied, return random message - ideal to add variety to your messages
  387.         function select(x)
  388.         {
  389.             if(x instanceof Array)
  390.                 return x[Math.floor(Math.random()*x.length)];
  391.             return x;
  392.         }
  393.  
  394.         if(x in teamMacros)
  395.             return {text:select(teamMacros[x]), global:0};
  396.  
  397.         if(x in globalMacros)
  398.             return {text:select(globalMacros[x]), global:1};
  399.        
  400.         if(x in aliases)
  401.             return getMacro(aliases[x]);
  402.        
  403.         if(x in debugMacros)
  404.             return {debugging:true};
  405.        
  406.         return false;
  407.     }
  408.  
  409.     // This functions does what expected - sends a chat message
  410.     var lastMessage = 0;
  411.     function chat(chatMessage, group)
  412.     {
  413.         if (typeof group == "undefined") group = false;
  414.         var now = new Date();
  415.         var timeDiff = now - lastMessage;
  416.         if (timeDiff > messageLimit)
  417.         {
  418.             if (!group) {
  419.                 tagpro.socket.emit("chat",
  420.                 {
  421.                     message: messagePrefix + chatMessage.text,
  422.                     toAll: chatMessage.global
  423.                 });
  424.             } else {
  425.                 tagpro.group.socket.emit("chat", chatMessage);
  426.             }
  427.             lastMessage = new Date();
  428.         }
  429.         else if (timeDiff >= 0)
  430.         {
  431.             setTimeout(function() {
  432.                 chat(chatMessage, group);
  433.             }, messageLimit - timeDiff);
  434.         }
  435.     }
  436.    
  437.     // Ids for powerup attributes on players and in socket messages.
  438.     var powerupIds = [
  439.         "bomb",
  440.         "grip",
  441.         "tagpro",
  442.         "speed"
  443.     ];
  444.    
  445.     // Names of powerups for chat.
  446.     var powerupNames = {
  447.         "bomb": "rolling bomb",
  448.         "grip": "juke juice",
  449.         "tagpro": "tagpro",
  450.         "speed": "speed pup"
  451.     };
  452.    
  453.     // The last powerup seen.
  454.     var lastPowerupSeen = false;
  455.     /* Listens for powerup information (player update frame with attribute from powerupIds set to true). If one
  456.      * is found, then it sets lastPowerupSeen to an object with properties
  457.      * - type: the type of powerup (from the powerupIds array).
  458.      * - game_timer: string with the time left in the game when the frame was seen
  459.      * - player: id of the player the update pertained to.
  460.      * - time: the current time (in ms; result of getTime() on a new date object)
  461.      */
  462.     function powerupListener(data) {
  463.         //console.log("Listening"); // DEBUG
  464.         var time = data.t;
  465.         var updates = data.u;
  466.         var pup_data = [];
  467.        
  468.         if (!updates) return;
  469.         // There should only be one, but in case there are multiple.
  470.         updates.forEach(function(update) {
  471.             for (var i = 0; i < powerupIds.length; i++) {
  472.                 var pup = powerupIds[i];
  473.                 // Check if property is present and true.
  474.                 if (update.hasOwnProperty(pup) && update[pup]) {
  475.                     //console.log("Found powerup frame."); // DEBUG
  476.                     var current_time = (new Date()).getTime();
  477.                     var time_left_ms = tagpro.gameEndsAt - current_time;
  478.                     var mins = "00" + Math.floor(time_left_ms / 6e4);
  479.                     var secs = "00" + Math.floor(time_left_ms % 6e4 / 1e3);
  480.                     var display_time = mins.substr(mins.length - 2, 2) + ":" + secs.substr(secs.length - 2, 2);
  481.                     pup_data.push({
  482.                         "type": pup,
  483.                         "game_timer": display_time,
  484.                         "player": update.id,
  485.                         "time": current_time
  486.                     });
  487.                     //console.log(pup_data); // DEBUG
  488.                     break;
  489.                 }
  490.             }
  491.         }, this);
  492.        
  493.         // Make available to outside script.
  494.         if (pup_data.length > 0) {
  495.             if (pup_data.length !== 1) {
  496.                 console.log("Powerup anomaly!"); // DEBUG
  497.             }
  498.             lastPowerupSeen = pup_data[0];
  499.         }
  500.     }
  501.    
  502.     var addSocketListener = function(id, fn) {
  503.         //console.log("Checking for socket."); // DEBUG
  504.         if (tagpro.socket) {
  505.             //console.log("Socket found, listening..."); // DEBUG
  506.             tagpro.socket.on(id, fn);
  507.         } else {
  508.             //console.log("Socket not found, retrying..."); // DEBUG
  509.             setTimeout(function() {
  510.                 addSocketListener(id, fn);
  511.             }.bind(this), 250);
  512.         }
  513.     }.bind(this);
  514.    
  515.     addSocketListener("p", powerupListener);
  516.     //console.log("Script set."); // DEBUG
  517.     var addToTagproReady = function(fn) {
  518.         if (tagpro) {
  519.             tagpro.ready(fn);
  520.         } else {
  521.             setTimeout(function() {
  522.                 addToTagproReady(fn);
  523.             }, 0);
  524.         }
  525.     }
  526.    
  527.     var waitForChat = function(fn) {
  528.         if (tagpro && tagpro.playerId) {
  529.             // Additional delay is for compatibility with chat enhancer.
  530.             setTimeout(fn, 1000);
  531.         } else {
  532.             setTimeout(function() {
  533.                 waitForChat(fn);
  534.             }, 0);
  535.         }
  536.     }
  537.    
  538.     waitForChat(function() {
  539.         if (io && io.__loopback) {
  540.             console.log("Loopback loaded.");
  541.             tagpro.socket.emit("local:chat", {
  542.                 to: "all",
  543.                 from: null,
  544.                 message: "Tagpro Neomacro Loaded!"
  545.             });
  546.         } else {
  547.             console.log("Loopback not loaded.");
  548.         }
  549.     });
  550.            
  551. }
  552.  
  553. var addToPage = function() {
  554.     // This dummy input will handle macro keypresses
  555.     var btn = document.createElement("input");
  556.     btn.style.opacity = 0;
  557.     btn.style.position = "absolute";
  558.     btn.style.top = "-100px";
  559.     btn.style.left = "-100px";
  560.     btn.id = "macrohandlerbutton";
  561.     document.body.appendChild(btn);
  562.  
  563.     // Create a script node holding this source code
  564.     var script = document.createElement('script');
  565.     script.setAttribute("type", "application/javascript");
  566.     script.textContent = '(' + Script + ')();';
  567.     document.body.appendChild(script);
  568. };
  569.  
  570. var runOnBody = function(fn) {
  571.     //console.log("Checking for body"); // DEBUG
  572.     if (document.body) {
  573.         //console.log("Body found, executing."); // DEBUG
  574.         fn();
  575.     } else {
  576.         //console.log("Body not found, retrying..."); // DEBUG
  577.         setTimeout(function() {
  578.             runOnBody(fn);
  579.         }, 150);
  580.     }
  581. };
  582.  
  583. var runOnTagpro = function(fn) {
  584.     if (typeof tagpro !== 'undefined') {
  585.         fn();
  586.     } else {
  587.         setTimeout(function() {
  588.             runOnTagpro(fn);
  589.         }, 0);
  590.     }
  591. }
  592.  
  593. runOnTagpro(function() {
  594.     tagpro.ready(function() {
  595.         runOnBody(addToPage);
  596.     });
  597. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement