The West Perseus Toolkit

Useful tools for The West.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        The West Perseus Toolkit
// @author      Mr. Perseus
// @namespace   tw-perseus
// @description Useful tools for The West.
// @include     https://*.the-west.*/game.php*
// @include     http://*.the-west.*/game.php*
// @include     https://*.tw.innogames.*/game.php*
// @include     http://*.tw.innogames.*/game.php*
// @version     1.3.0
// @grant       none
// ==/UserScript==

(function(fn) {
    const script = document.createElement('script');
    script.setAttribute('type', 'application/javascript');
    script.textContent = `(${fn})();`;
    document.body.appendChild(script);
    document.body.removeChild(script);
})(() => {
    $(document).ready(() => {
        const TWPT = {
            base64: {
                menuImage:
                    "url('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAABmgAwAEAAAAAQAAABkAAAAA/+0AOFBob3Rvc2hvcCAzLjAAOEJJTQQEAAAAAAAAOEJJTQQlAAAAAAAQ1B2M2Y8AsgTpgAmY7PhCfv/AABEIABkAGQMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7/2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/3QAEAAL/2gAMAwEAAhEDEQA/APHLTT9Z1bU7101HUApunA2t8qgHp0rqbXwHq0ttuOo6kGI67zXpX7PEA1bw5rGmGCF3S6llRmAGMOSQTjjI5r0iNLRtEvpBEgCNEA2QNoO7+eBXwmPzDFqs4wdld213sfQUIUlH3o3/AOCfJHjfw54i8PKsrajqSLhHG9iMgkeorpvt93/z9Tf9917h+0FdaMPAWvQ3yJNeyWkK2I8v5rVgvLFv4geDtFeBbx6frXuZZiJ1qT53do4qyXNeKsf/0PP/AIdePv8AhHW1TTGvmtop7lvMHK5AbPb612yfEjRfIZBfwFWxkZPOOmRXmV1/rR9Kb2r5fE5LSxE/aOTR7VLHSpR5eVMu/E74gx6zaPZ28jTGQqhbDEkZ6fhUGW/55tUEf+tH1FSV3YXDQwkOSBz1q0q0rs//2Q==')",
            },
            version: '1.3.0',
            settingsKey: 'TWPT_preferences',
            defaultPreferences: {
                CinemaSkipButton: true,
                ZoomMap: true,
                DisablePremiumNotifications: true,
                NineTimesFifteenButton: true,
                HideDrawingMap: true,
                ChatImprovements: true,
                AlwaysShowFortBattleRecruitment: true,
            },
            preferences: {},
            currentZoom: 1,
        };

        TWPT.Updater = {
            init() {
                setTimeout(TWPT.Updater.load, 5000);
            },

            load() {
                $.getScript(
                    'https://rawcdn.githack.com/mr-perseus/tw-js-library/master/script-updater.js',
                    () => {
                        if (scriptUpdater.TWPT > TWPT.version) {
                            const updateMessage = new west.gui.Dialog(
                                'Update: The West Perseus Toolkit',
                                `<span>Update Available<br><br><b>v${scriptUpdater.TWPT}:</b><br>${scriptUpdater.TWPTNew}</span>`,
                                west.gui.Dialog.SYS_WARNING,
                            )
                                .addButton('Update', () => {
                                    updateMessage.hide();
                                    window.location.href =
                                        'https://greasyfork.org/scripts/370137-the-west-perseus-toolkit/code/The%20West%20Perseus%20Toolkit.user.js';
                                })
                                .addButton('cancel')
                                .show();
                        }
                    },
                );
            },
        };

        TWPT.Settings = {
            init() {
                const storage = JSON.parse(
                    localStorage.getItem(TWPT.settingsKey),
                );
                TWPT.preferences = storage || TWPT.defaultPreferences;

                const div = $('<div class="ui_menucontainer" />');
                const link = $(
                    '<div id="TWPT_Menu" class="menulink" title="The West Perseus Toolkit" />',
                )
                    .css('background-image', TWPT.base64.menuImage)
                    .css('background-position', '0px 0px')
                    .mouseenter(function() {
                        $(this).css('background-position', '-2px 0px');
                    })
                    .mouseleave(function() {
                        $(this).css('background-position', '0px 0px');
                    });

                $(link).on('click', () => {
                    TWPT.Settings.refreshMenu();
                });

                $('#ui_menubar').append(
                    div
                        .append(link)
                        .append('<div class="menucontainer_bottom" />'),
                );
            },

            refreshMenu() {
                const win = wman
                    .open('TWPTSettings', 'TWPT Settings', 'noreload')
                    .setMaxSize(1268, 838)
                    .setMiniTitle('TWPT Settings');
                const scrollPane = new west.gui.Scrollpane();

                const setTitle = function(name) {
                    scrollPane.appendContent(
                        `<p><span style="font-size: 130%; font-weight: bold; font-style: italic; display: inline-block; margin-top: 20px;">${name}</span></p>`,
                    );
                };

                const setCheckBox = function(prefName, text) {
                    const checkbox = new west.gui.Checkbox(text);
                    checkbox.setId(`TWPT_${prefName}`);
                    if (TWPT.preferences[prefName]) {
                        checkbox.toggle();
                    }
                    checkbox.setCallback(() => {
                        TWPT.preferences[prefName] = checkbox.isSelected();
                        localStorage.setItem(
                            TWPT.settingsKey,
                            JSON.stringify(TWPT.preferences),
                        );
                        TWPT.Settings.refreshMenu();
                        new UserMessage(
                            'Okay. Please refresh your page.',
                            'success',
                        ).show();
                    });
                    scrollPane.appendContent(checkbox.getMainDiv());
                    scrollPane.appendContent('<br/>');
                };

                setTitle('Enabled Features');
                setCheckBox(
                    'CinemaSkipButton',
                    'Enable the Cinema Skip button (allows to skip cinema videos after 5 seconds).',
                );
                setCheckBox(
                    'ZoomMap',
                    'Enable the Zoom feature (hover the minimap icon on the top right and scroll up / down to zoom out / in).',
                );
                setCheckBox(
                    'DisablePremiumNotifications',
                    'Suppress energy refill and automation premium notifications.',
                );
                setCheckBox(
                    'NineTimesFifteenButton',
                    'Add a button to job windows which allows you to start 9x 15 second jobs at once.',
                );
                setCheckBox(
                    'HideDrawingMap',
                    'Hides "Drawing Map" flag which is buggy sometimes.',
                );
                setCheckBox(
                    'ChatImprovements',
                    'Enable Chat improvements: show online / idle status in Saloon chat.',
                );
                setCheckBox(
                    'AlwaysShowFortBattleRecruitment',
                    'Always show the recruitment button in fort battle windows. (even if you have no permissions)',
                );
                setTitle('Feedback');
                scrollPane.appendContent(
                    '<ul style="margin-left:15px;line-height:18px;">' +
                        '<li>Send a message to <a target="_blank" href="https://www.the-west.de/?ref=west_invite_linkrl&player_id=83071&world_id=1&hash=0dc5">Mr. Perseus on world DE1</a></li>' +
                        '<li>Contact me on <a target="_blank" href="https://greasyfork.org/forum/messages/add/Mr. Perseus">Greasy Fork</a></li>' +
                        '<li>Send me a message on the <a target="_blank" href="https://forum.beta.the-west.net/index.php?conversations/add&to=Mr.%20Perseus">The West Beta Forum</a> or the <a target="_blank" href="https://forum.the-west.de/index.php?conversations/add&to=Mr.%20Perseus">German The West Forum</a></li>' +
                        '</ul><br />Check out other scripts on <a target="_blank" href="https://greasyfork.org/de/users/179973-mr-perseus">Greasyfork</a>.',
                );

                setTitle(
                    'Import / Export settings from various userscripts (to clipboard)',
                );

                const exportButton = new west.gui.Button(
                    'Export settings',
                    () => {
                        const settings = {};

                        const settingsKeysToStore = [
                            'TWDW_preferences',
                            'TWPT_preferences',
                            'TWToolkit_preferences',
                            'TWCalc_Wardrobe',
                            'TWCalc_Settings',
                            'TWDS_h_',
                            'TWDS_settings',
                            'twir_features',
                        ];

                        Object.entries(localStorage).forEach(([key, value]) => {
                            if (
                                settingsKeysToStore.some((settingToStore) =>
                                    key.startsWith(settingToStore),
                                )
                            ) {
                                settings[key] = value;
                            }
                        });

                        console.log('exporting settings', settings);

                        navigator.clipboard
                            .writeText(JSON.stringify(settings))
                            .then(
                                () => {
                                    new UserMessage(
                                        'Successfully copied settings to clipboard. Please save it somewhere.',
                                        'success',
                                    ).show();
                                },
                                (error) => {
                                    console.error('TWPT error', error);
                                    new UserMessage(
                                        'Failed saving to clipboard. (see console for further details)',
                                        'error',
                                    ).show();
                                },
                            );
                    },
                );
                scrollPane.appendContent(exportButton.getMainDiv());

                scrollPane.appendContent(
                    '<div style="margin-left: 20px;display:inline-block">Enter here: <input id="TWPT_import"/></div>',
                );
                const importButton = new west.gui.Button(
                    'Import settings',
                    () => {
                        try {
                            const stringImport = $('#TWPT_import').val();

                            const settings = JSON.parse(stringImport);

                            Object.entries(settings).forEach(([key, value]) => {
                                console.log(`Importing ${key} = ${value}`);
                                localStorage[key] = value;
                            });

                            new UserMessage(
                                'Successfully imported settings.',
                                'success',
                            ).show();
                        } catch (error) {
                            console.error('TWPT error', error);
                            new UserMessage(
                                'Failed saving to clipboard. (see console for further details)',
                                'error',
                            ).show();
                        }
                    },
                );
                scrollPane.appendContent(importButton.getMainDiv());

                win.appendToContentPane(scrollPane.getMainDiv());
            },
        };

        TWPT.CinemaSkipButton = {
            init() {
                const button = new west.gui.Button('Skip ad', () => {
                    CinemaWindow.controller('rewards');
                });

                CinemaWindow.backup_twpt_cotroller = CinemaWindow.controller;
                CinemaWindow.controller = function(key) {
                    button.setVisible(false);
                    button.disable();

                    // Uncomment the following line if you want to access rewards directly.
                    // if (key === "video") return CinemaWindow.backup_twpt_cotroller("rewards");

                    if (key === 'video') {
                        let count = 15;
                        const countDown = () => {
                            if (count > 0) {
                                button.setCaption(`Skip ad (${count})`);
                                setTimeout(countDown, 1000);
                                count -= 1;
                            } else {
                                button.setCaption('Skip ad');
                                button.enable();
                            }
                        };
                        button.setVisible(true);
                        countDown();
                    }

                    // If there is no ad available you should be able to get the rewards.
                    if (key === 'noVideo') {
                        return CinemaWindow.backup_twpt_cotroller('rewards');
                    }

                    return CinemaWindow.backup_twpt_cotroller(key);
                };

                CinemaWindow.backup_twpt_open = CinemaWindow.open;
                CinemaWindow.open = function(townId) {
                    CinemaWindow.backup_twpt_open(townId);
                    const header = $(this.window.divMain).find(
                        '.tw2gui_inner_window_title',
                    );
                    button.divMain.setAttribute(
                        'style',
                        'margin-left: 20px; margin-top: -20px; position: absolute',
                    );
                    button.divMain.lastChild.setAttribute(
                        'style',
                        'line-height: normal; padding-top: 10px;',
                    );
                    button.setVisible(false);
                    header.append(button.getMainDiv());
                };
            },
        };

        TWPT.ZoomMap = {
            init() {
                $(window).bind('mousewheel', (event) => {
                    if ($('#ui_minimap').is(':hover')) {
                        if (event.originalEvent.wheelDelta >= 0) {
                            if (TWPT.currentZoom < 1.95)
                                TWPT.currentZoom += 0.1;
                        } else if (TWPT.currentZoom > 0.75)
                            TWPT.currentZoom -= 0.1;

                        document.getElementById('map').style.zoom =
                            TWPT.currentZoom;
                    }
                });
            },
        };

        TWPT.DisablePremiumNotifications = {
            init() {
                Premium.checkForEnergyPremium = function(
                    callback,
                    failCallback,
                ) {
                    if (typeof failCallback !== 'undefined')
                        return failCallback();

                    return () => {};
                };
                Premium.checkForAutomationPremium = function(
                    callback,
                    failCallback,
                ) {
                    if (typeof failCallback !== 'undefined')
                        return failCallback();

                    return () => {};
                };
            },
        };

        TWPT.NineTimesFifteenButton = {
            init() {
                JobWindow.backup_twpt_initView = JobWindow.initView;
                JobWindow.initView = function() {
                    JobWindow.backup_twpt_initView.apply(this, arguments);
                    const button = new west.gui.Button('9x 15s', () => {
                        button.disable();
                        const jobAmountNum = this.window.divMain.getElementsByClassName(
                            'job-amount-num',
                        )[0];
                        const numberBefore = jobAmountNum.innerHTML;
                        jobAmountNum.innerHTML = '9';
                        $(this.window.divMain)
                            .find('.job_durationbar.job_durationbar_short')
                            .click();
                        setTimeout(() => {
                            button.enable();
                            jobAmountNum.innerHTML = numberBefore;
                        }, 5000);
                    });

                    const buttonDiv = button.getMainDiv();
                    buttonDiv.style['z-index'] = '5';
                    buttonDiv.style.bottom = '25px';
                    buttonDiv.style.left = '300px';
                    this.window.divMain
                        .querySelector('div.tw2gui_window_content_pane')
                        .appendChild(button.getMainDiv());
                };
            },
        };

        TWPT.HideDrawingMap = {
            init() {
                GameLoader.backup_twpt_async = GameLoader.async;
                GameLoader.async = function(message, signal, callback, max) {
                    GameLoader.backup_twpt_async(
                        message,
                        signal,
                        callback,
                        max,
                        true,
                        true,
                    );
                };
            },
        };

        TWPT.ChatImprovements = {
            init() {
                Chat.Resource.Client.prototype.isStranger = function() {
                    return false;
                };
            },
        };

        TWPT.AlwaysShowFortBattleRecruitment = {
            init() {
                FortBattleWindow.backup_twpt_getInfoArea =
                    FortBattleWindow.backup_twpt_getInfoArea ||
                    FortBattleWindow.getInfoArea;
                FortBattleWindow.getInfoArea = function() {
                    this.preBattle.battleData.canSetPrivilege = true;
                    return FortBattleWindow.backup_twpt_getInfoArea.apply(
                        this,
                        arguments,
                    );
                };
            },
        };

        try {
            TWPT.Updater.init();
            TWPT.Settings.init();
            Object.keys(TWPT.preferences).forEach((property) => {
                if (TWPT.preferences[property]) {
                    try {
                        TWPT[property].init();
                    } catch (err) {
                        console.error(
                            `TWPT Error with feature "${property}".`,
                            err,
                        );
                    }
                }
            });
        } catch (err) {
            console.error('TWPT ERROR', err);
        }
    });
});