// ==UserScript== // @id twitch-plays-control@meiguro.com // @name Twitch Plays Pokémon Touch Controller // @version 0.3.4 // @author Meiguro http://meiguro.com/ // @namespace https://github.com/Meiguro/twitch-plays-control // @description Add Touch controls to Twitch Plays Pokemon touch-enabled games. // @include /^https?://(www|beta)?\.?twitch.tv/twitch_?plays.*$/ // @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js // @require http://ajax.googleapis.com/ajax/libs/jqueryui/1.11.2/jquery-ui.min.js // @grant unsafeWindow, GM_addStyle, GM_info // @run-at document-start // @updateURL https://raw.githubusercontent.com/Meiguro/twitch-plays-control/master/twitch-plays-control.meta.js // @installURL https://raw.githubusercontent.com/Meiguro/twitch-plays-control/master/twitch-plays-control.user.js // @downloadURL https://raw.githubusercontent.com/Meiguro/twitch-plays-control/master/twitch-plays-control.user.js // ==/UserScript== /** * v0.3.4 CHANGELOG ༼ つ ◕_◕ ༽つ * * - Fixed to support Greasemonkey's new sandbox security * * v0.3.3 * * - Fixed auto send to work with new Twitch UI * * Enjoy! * * - Meiguro */ (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o').addClass('tpc-mouse-box'); var $coordTooltip = touch.$coordTooltip = $('
').addClass('tpc-coord-tooltip'); var $chatSettings = settings.$chatSettings = $(Settings.ChatSelector); var $controlSettings = settings.$controlSettings = $('
').addClass('tpc-control-settings'); $player.css({ position: 'relative' }); $chatSettings.css({ position: 'absolute' }); $mouseBox.css({ cursor: 'pointer', border: '2px solid rgba(150, 150, 150, 0.2)', borderRadius: '5px' }); GM_addStyle( '.tpc-mouse-box .tpc-coord-tooltip { display: none; color: #444; font-weight: normal } ' + '.tpc-mouse-box:hover .tpc-coord-tooltip { display: block }'); $coordTooltip.css({ padding: '0px 5px', background: 'rgba(255, 255, 255, 0.8)', borderRadius: '2px' }); $mouseBox.empty(); $mouseBox.append($coordTooltip); $controlSettings.empty(); $controlSettings.append( '
' + '
Touch Control Settings
' + '
' + '
' + '
' + '
' + '
' + '
Chat Server
' + '
' + '
'); $controlSettings.find('.tpc-control-sliders') .append(touch.xSlider = dd.ui.slider( 'tpc-x-slider tpc-slider', 'Touch-box x-position', { min: 0, max: 1, step: 0.0005 }, this.config, 'screen.position.0', function() { touch.updateMouseBox(true); })) .append(touch.ySlider = dd.ui.slider( 'tpc-y-slider tpc-slider', 'Touch-box y-position', { min: 0, max: 1, step: 0.0005 }, this.config, 'screen.position.1', function() { touch.updateMouseBox(true); })) .append(touch.scaleSlider = dd.ui.slider( 'tpc-scale-slider tpc-slider', 'Touch-box scale', { min: 0, max: 1, step: 0.0005 }, this.config, 'screen.scale', function() { touch.updateMouseBox(true); })); $controlSettings.find('.tpc-control-checkboxes') .append(touch.enabledCheckbox = dd.ui.checkbox( 'tpc-enabled-checkbox', 'Enable touch control', this.config, 'enabled', function() { touch.$mouseBox.css({ display: self.config.enabled ? 'block' : 'none' }); })) .append(touch.borderCheckbox = dd.ui.checkbox( 'tpc-border-checkbox', 'Show border box', this.config, 'showBorder', function() { touch.$mouseBox.css({ border: self.config.showBorder ? '2px solid rgba(255, 255, 255, 0.5)' : 'none' }); })) .append(touch.coordTooltipCheckbox = dd.ui.checkbox( 'tpc-tooltip-checkbox', 'Show coord tooltip', this.config, 'showCoordTooltip', function() { if (self.config.showCoordTooltip) { touch.$mouseBox.append(touch.$coordTooltip); } else { touch.$coordTooltip.remove(); } })) .append(touch.crossCheckbox = dd.ui.checkbox( 'tpc-cross-checkbox', 'Use cross pointer', this.config, 'showCross', function() { touch.$mouseBox.css({ cursor: self.config.showCross ? 'crosshair' : 'default' }); })) .append(touch.autoSendCheckbox = dd.ui.checkbox( 'tpc-auto-send-checkbox', 'Auto-send touches', this.config, 'autoSend')) .append(touch.dropletsCheckbox = dd.ui.checkbox( 'tpc-droplet-checkbox', 'Show touch droplets', this.config, 'showDroplets')); $controlSettings.find('.tpc-control-last') .append(touch.resetButton = dd.ui.button( 'tpc-reset-button', 'Reset controller settings', this.onPressReset.bind(this))); $controlSettings.find('.tpc-control-chat') .append('

') .append(chat.chatServerButton = dd.ui.button( 'tpc-chat-server-button', 'Change chat server', this.onPressChangeChatServer.bind(this))); $player.append($mouseBox); $chatSettings.append($controlSettings); $mouseBox.on('click', this.onClick.bind(this)); $mouseBox.on('mousemove', this.onMove.bind(this)); settings.update(true); this.loaded = true; this.start(); setTimeout(function() { $('.chat-room .loading-mask').remove(); }, 5000); }; Control.prototype.loadable = function() { var $ = mywindow.jQuery; if (typeof $ !== 'function') { return false; } var hasPlayer = $(Control.PlayerSelector).length; var hasChatSettings = $(Settings.ChatSelector).length; return hasPlayer || hasChatSettings; }; Control.prototype.onload = function() { this.init(); if (typeof GM_info === 'object') { console.log(GM_info.script.name + ' v' + GM_info.script.version + ' loaded!'); } else { console.log('Twitch Plays Control loaded!'); } }; var control = new Control(); setInterval(control.update.bind(control), Control.DefaultConfig.delay); control.update(); mywindow.TPControl = control; },{"chat":2,"dd":5,"dd-ui":4,"entity":6,"gm-shims":7,"settings":8,"touch":9,"util2":10,"window":11}],2:[function(require,module,exports){ var util2 = require('util2'); var Component = require('component'); var mywindow = require('window'); var Chat = function(def) { Component.call(this, def); }; util2.inherit(Chat, Component, Chat.prototype); Chat.InputSelector = '.ember-text-area'; Chat.ButtonSelector = '.send-chat-button'; Chat.HiddenSelector = '.chat-hidden-overlay'; Chat.LogSelector = '.chat-messages .tse-content'; Chat.ServerAddress = '199.9.252.26:6667'; Chat.prototype.name = 'chat'; Chat.prototype.start = function() { }; Chat.prototype.getChatSession = function() { if (this.chatSession) { return this.chatSession; } var App = mywindow.App; if (App && App.Room) { return (this.chatSession = App.Room._getTmiSession().fulfillmentValue); } }; Chat.prototype.getChatConnection = function() { var chatSession = this.getChatSession(); var connections = chatSession._connections; var cluster = connections.prod || connections.event; if (cluster) { return cluster; } for (var k in connections) { return connections[k]; } }; Chat.prototype.getCurrentChatAddress = function() { var chatSession = this.getChatSession(); if (!chatSession) { return; } var connection = this.getChatConnection(); if (!connection) { return; } var addr = connection._addrs[connection._currentAddressIndex]; if (!addr) { return; } return addr.host + ':' + addr.port; }; Chat.prototype.connectServer = function(address) { var addr = address.split(':'); var chatSession = this.getChatSession(); var connection = this.getChatConnection(); if (!connection) { window.alert('TPC: Couldn\'t obtain a chat connection to manipulate!'); return; } connection.close(); connection._addrs = [{ host: addr[0], port: addr[1] }]; connection._currentAddressIndex = 0; connection._numSocketConnectAttempts = 0; connection.open(); var msg = 'connecting to chat server ' + address + '...'; $(Chat.LogSelector + ' .ember-view:last') .before('
' + msg + '
'); }; Chat.prototype.isChatVisible = function() { return $(Chat.InputSelector).length && !$(Chat.HiddenSelector).is(':visible'); }; Chat.prototype.setInput = function(input, broadcast) { $(Chat.InputSelector).val(input).focus().change().blur(); if (this.config.autoSend) { $(Chat.ButtonSelector).click(); } if (!this.isChatVisible() && broadcast !== false) { localStorage.TPCInput = input; } }; Chat.prototype.updateInput = function() { if (!this.entity.loaded) { return; } if (!this.config.enabled) { return; } if (!this.isChatVisible()) { return; } var input = localStorage.TPCInput; delete localStorage.TPCInput; if (typeof input === 'string' && input.length > 0) { this.setInput(input, false); } }; Chat.prototype.updateChatSession = function() { if (this.chatSession) { return; } var TMI = mywindow.TMI; if (!TMI) { return; } if (this.entity.loaded) { this.getChatSession(); } }; Chat.prototype.update = function() { this.updateChatSession(); this.updateInput(); }; Chat.prototype.onPressChangeChatServer = function(e) { var defaultAddress = Chat.ServerAddress; var currentAddress = this.getCurrentChatAddress(); var address = window.prompt( 'Enter chat server address:' + (currentAddress ? '\nCurrent ' + currentAddress : '\nExample ' + defaultAddress), defaultAddress) || ''; address = address.replace(/\s/, ''); if (address.length === 0) { return; } if (!address.match(':')) { address += ':6667'; } this.connectServer(address); }; module.exports = Chat; },{"component":3,"util2":10,"window":11}],3:[function(require,module,exports){ var Component = function(def) { this.state = def || {}; }; Component.prototype.component = function(name) { return this.entity.component(name); }; Component.prototype.start = function() {}; Component.prototype.update = function() {}; module.exports = Component; },{}],4:[function(require,module,exports){ var dd = require('dd'); dd.ui = dd.ui || {}; dd.ui.checkbox = function(id, label, obj, ref, onChange) { var $elem = $('

'); var $input = $elem.find('input'); dd.bindGetSet($elem, $input.prop.bind($input, 'checked')); $input.on('change', dd.bindElement($elem, obj, ref, onChange)); return $elem; }; dd.ui.slider = function(klass, label, options, obj, ref, onChange) { var $elem = $('

'); var $slider = $('
'); $slider.slider(options); $elem.prepend($slider); dd.bindGetSet($elem, $slider.slider.bind($slider, 'value')); $slider.slider({ slide: dd.bindElement($elem, obj, ref, onChange) }); return $elem; }; dd.ui.button = function(klass, label, onPress) { var $elem = $(''); $elem.on('click', onPress); return $elem; }; module.exports = dd.ui; },{"dd":5}],5:[function(require,module,exports){ var dd = {}; dd.last = function(a) { return a[a.length - 1]; }; dd.toKey = function(obj, key) { return obj instanceof Array ? parseInt(key) : key; }; dd.getByKeys = function(obj, keys, offset) { for (var i = 0, ii = keys.length - (offset || 0); i < ii; ++i) { obj = obj[dd.toKey(obj, keys[i])]; } return obj; }; dd.setByKey = function(obj, key, value) { obj[dd.toKey(obj, key)] = value; }; dd.set = function(obj, ref, value) { var keys = ref.split('.'); dd.setByKey(dd.getByKeys(obj, keys, 1), dd.last(keys), value); }; dd.get = function(obj, ref) { return dd.getByKeys(obj, ref.split('.')); }; dd.bindGetSet = function($elem, get, set) { $elem.$get = get; $elem.$set = set || get; }; dd.bindElement = function($elem, obj, ref, onChange) { $elem.$obj = obj; $elem.$ref = ref; $elem.$change = function(e) { if (e) { dd.set(obj, ref, $elem.$get()); } else { $elem.$set(dd.get(obj, ref)); } if (onChange) { onChange(e); } if (dd.onChange) { dd.onChange(e); } }; $elem.$change(); return $elem.$change; }; module.exports = dd; },{}],6:[function(require,module,exports){ var Entity = function(def) { this.state = def || {}; this._components = []; this._componentsByName = {}; }; Entity.prototype.component = function(name) { return this._componentsByName[name]; }; Entity.prototype.eachComponent = function(callback) { this._components.forEach(callback.bind(this)); }; Entity.prototype.addComponent = function(component) { if (component.entity) { component.entity.removeComponent(component); } this._componentsByName[component.name] = component; this._components.push(component); component.entity = this; component.config = this.config; }; Entity.prototype.removeComponent = function(component) { var index = this._components.indexOf(component); if (index === -1) { return; } this._components.splice(index, 1); delete this._componentsByName[component.name]; delete component.entity; delete component.config; }; Entity.prototype.start = function() { for (var i = 0, ii = this._components.length; i < ii; ++i) { this._components[i].start(); } }; Entity.prototype.update = function(force) { for (var i = 0, ii = this._components.length; i < ii; ++i) { this._components[i].update(force); } }; module.exports = Entity; },{}],7:[function(require,module,exports){ (function (global){ if (typeof GM_addStyle === 'undefined') { global.GM_addStyle = function(style) { $('head:first').append($('