Twitch touches pokemon

A tool adding a touch overlay onto the stream of twitchplayspokemon.

// ==UserScript==
// @name           Twitch touches pokemon
// @namespace      https://github.com/lostcoaster/twitch-touches-pokemon
// @author         lostcoaster
// @author         MattiasBuelens
// @version        1.6
// @description    A tool adding a touch overlay onto the stream of twitchplayspokemon.
// @grant          unsafeWindow

// this include string credits Twitch Plays Pokemon Chat Filter
// @include        /^https?://(www|beta)\.twitch\.tv\/twitchplayspokemon(\/?)$/
// @include        /^https?://(www\.)?tinytwit\.ch\/twitchplayspokemon.*$/
// @include        /^https?://(www\.)?twitchplayspokemon\.net\/?$/

// ==/UserScript==

// for bookmarklet users : javascript:(function(){document.body.appendChild(document.createElement('script')).src='https://raw.githubusercontent.com/lostcoaster/twitch-touches-pokemon/master/touch.user.js';})();

/* jshint
     lastsemic:true,
     eqeqeq:true,
     unused:true
 */
/* global
     unsafeWindow:false
 */

(function () {

'use strict';

// ----------------------------
// Greasemonkey support
// ----------------------------
// Greasemonkey userscripts run in a separate environment and cannot use global
// variables from the page directly. They need to be accessed via `unsafeWindow`

var myWindow;
try {
    myWindow = unsafeWindow;
} catch (e) {
    myWindow = window;
}

var $ = myWindow.jQuery;

var Setting = function (key, defaultValue) {
    this.key = key;
    this.defaultValue = defaultValue;
    this.value = undefined;
    this.observers = [];
};
Setting.prototype.getValue = function () {
    return (this.value !== undefined) ? this.value : this.defaultValue;
};
Setting.prototype.setValue = function (value) {
    var newValue = (value !== undefined) ? value : this.defaultValue;
    if (newValue !== this.value) {
        this.value = newValue;
        this.fireObservers(newValue);
    }
};
Setting.prototype.restoreDefault = function () {
    this.setValue(this.defaultValue);
};
Setting.prototype.load = function (settings) {
    this.setValue(settings ? settings[this.key] : undefined);
};
Setting.prototype.save = function (settings) {
    settings[this.key] = this.getValue();
};
Setting.prototype.observe = function (observer) {
    this.observers.push(observer);
};
Setting.prototype.fireObservers = function (value) {
    for (var i = 0; i < this.observers.length; i++) {
        this.observers[i].call(null, value, this.key, this);
    }
};
Setting.prototype.bind = function (input) {
    input = $(input);
    if (input.is(':checkbox')) {
        this.bindCheckbox(input);
    }
};
Setting.prototype.bindCheckbox = function (checkbox) {
    var self = this;
    // set current value
    checkbox.prop('checked', this.getValue());
    // bind checkbox to setting
    this.observe(function (newValue) {
        checkbox.prop('checked', newValue);
    });
    // bind setting to checkbox
    checkbox.change(function () {
        self.setValue(checkbox.prop('checked'));
    });
};


var forIn = function (obj, f) {
    for (var k in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, k)) {
            f(k, obj[k]);
        }
    }
};

var touch_pad = {
    parameters: {
        position_x: 0.359,
        position_y: 0.548,
        original_height: 434,
        bar_height: 30,
        ratio: 9 / 16,
        screen_height: 192,
        screen_width: 256
    },

    settings: {
        show_border: new Setting('show_border', true),
        direct_send: new Setting('direct_send', false)
    },
    settings_key: 'twitch-touches-pokemon',

    scale: 1,

    interval_handle: (window.touch_pad === undefined ? undefined : touch_pad.interval_handle),

    // reflect mouse event to coordinate output.
    coords: function (event) {
        var x = Math.floor((event.pageX - $(event.target).offset().left) / touch_pad.scale);
        var y = Math.floor((event.pageY - $(event.target).offset().top) / touch_pad.scale);
        x = Math.min(Math.max(x, 1), touch_pad.parameters.screen_width);
        y = Math.min(Math.max(y, 1), touch_pad.parameters.screen_height);
        return x + ',' + y;
    },
    // adjust position of the box, parameters are relative position of top-left corner of the box within stream screen
    // 0 <= rx,ry <= 1
    position: function (rx, ry) {
        try{
            var base = $('#player,#videoPlayer');
            if(!base.is('object')) // in tinytwitch #player is that object.
                base = base.find('object'); // but in twitch player is just a div.
            var height = base.height() - touch_pad.parameters.bar_height;
            var base_offset = base.offset();
            var real_height, real_width, left_margin, top_margin;
            if (height / base.width() > touch_pad.parameters.ratio) {
                // this is the behavior of BetterTTV, filling horizontally and leave margins on top and bottom
                real_width = base.width();
                real_height = real_width * touch_pad.parameters.ratio;
                touch_pad.scale = real_height / touch_pad.parameters.original_height;
                left_margin = 0;
                top_margin = (height - real_height) / 2;
            } else {
                // this is the normal behavior of twitch, filling vertically and leave margins on left and right.
                real_height = height;
                touch_pad.scale = real_height / touch_pad.parameters.original_height;
                real_width = real_height / touch_pad.parameters.ratio;
                left_margin = (base.width() - real_width) / 2;
                top_margin = 0;
            }
            $('.touch_overlay').offset({
                top: Math.floor(base_offset.top + top_margin + ry * real_height),
                left: Math.floor(base_offset.left + left_margin + rx * real_width)
            })
                .height(Math.floor(touch_pad.parameters.screen_height * touch_pad.scale))
                .width(Math.floor(touch_pad.parameters.screen_width * touch_pad.scale));
        } catch(err) {
            console.error(err); //don't quit
        }

    },

    aim: function () {
        touch_pad.position(touch_pad.parameters.position_x, touch_pad.parameters.position_y); // rough estimation No.2
    },

    init_settings: function () {
        forIn(touch_pad.settings, function (k, setting) {
            setting.observe(function () {
                touch_pad.save_settings();
            });
        });
    },
    load_settings: function () {
        var settings = JSON.parse(myWindow.localStorage.getItem(touch_pad.settings_key));
        forIn(touch_pad.settings, function (k, setting) {
            setting.load(settings);
        });
    },
    save_settings: function () {
        var settings = {};
        forIn(touch_pad.settings, function (k, setting) {
            setting.save(settings);
        });
        myWindow.localStorage.setItem(touch_pad.settings_key, JSON.stringify(settings));
    },
    restore_default_settings: function () {
        forIn(touch_pad.settings, function (k, setting) {
            setting.restoreDefault();
        });
    },

    init: function () {
        if ($('.touch_overlay').length === 0) {

            $('body')
                .append('<div class="touch_overlay" style="cursor:crosshair;z-index:99"></div>')
                .append('<style type="text/css">.touchborder{border:red solid 1px;}</style>');


            $('.touch_overlay').unbind()
                .mouseup(function (event) {
                    var output_text = touch_pad.coords(event);
                    var output_area = $('textarea')
                        .val(output_text)
                        .change();

                    // on tinytwitch accessing the textarea is difficult.
                    if (output_area.length === 0)
                        myWindow.prompt('Twitch touches pokemon cannot locate the chat box on this page, possibly because it is not on the offical stream page, but you can copy the following value to send it yourself.',
                            output_text);

                    if (touch_pad.settings.direct_send.getValue()) {
                        $('.send-chat-button button').click();
                    }

                });

            // add the reaiming into settings menu. idea stolen from the chat-filter : http://redd.it/1y8ukl
            $('.chat-settings')
                .append('<div class="chat-menu-header">Touch pad</div>')
                .append($('<div class="chat-menu-content"></div>')
                    .append('<p><label><input id="ttp-show-border" type="checkbox"> Show border</label></p>')
                    .append('<p><label><input id="ttp-direct-send" type="checkbox"> Send on clicking</label></p>')
                    .append($('<button>Reposition Touchpad</button>').click(function () {
                        touch_pad.aim();
                    })));
        }

        // initialize settings
        touch_pad.init_settings();
        // bind inputs to settings
        touch_pad.settings.show_border.bind('#ttp-show-border');
        touch_pad.settings.direct_send.bind('#ttp-direct-send');
        // observe settings
        touch_pad.settings.show_border.observe(function (shown) {
            $('.touch_overlay').toggleClass("touchborder", shown);
        });
        // load settings
        touch_pad.load_settings();

        //start running
        touch_pad.aim();

        if (touch_pad.interval_handle) {
            clearInterval(touch_pad.interval_handle);
        }
        //update the size every 50 ms , thanks to Meiguro's idea!
        touch_pad.interval_handle = setInterval(touch_pad.aim, 50);
    }
};

// initialize on DOM ready
$(function () {
    touch_pad.init();
});

})();
长期地址
遇到问题?请前往 GitHub 提 Issues,或加Q群1031348184

赞助商

Fishcpy

广告

Rainyun

注册一下就行

Rainyun

一年攒够 12 元