Warzone Order Clipboard

Copy and paste orders in Warzone (Ctrl+C to copy and Ctrl+V to paste)

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Warzone Order Clipboard
// @namespace    https://greasyfork.org/en/users/884770-andenrx
// @version      1.2
// @description  Copy and paste orders in Warzone (Ctrl+C to copy and Ctrl+V to paste)
// @author       andenrx
// @match        https://www.warzone.com/SinglePlayer*
// @match        https://www.warzone.com/MultiPlayer*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net
// @run-at       document-idle
// @grant        none
// ==/UserScript==
 
(async function() {
    'use strict';
    let m1 = window.location.href.match(/MultiPlayer\?GameID=\d+/);
    let m2 = window.location.href.match(/SinglePlayer\?Level=\d+/);
    const gameid = 1
    const DAYS_TO_STORE = 100;
 
    let clipboard;
 
    function copy() {
        clipboard = (window.UJS_Hooks[min.TurnBuilder][min.OrderList] || []).map(order_container => order_container[min.Order]);
        save(gameid, clipboard);
        console.log("Copied", clipboard);
    }
 
    function paste() {
        clipboard = clipboard || load(gameid);
        if (clipboard) {
            window.UJS_Hooks[min.TurnBuilder][min.LoadOrders](clipboard);
            console.log("Pasted", clipboard);
        } else {
            console.log("Nothing in clipboard");
        }
    }
 
    function save(gameid, orders) {
        let storage_id = "userscript_order_clipboard_" + gameid;
        update_registry(storage_id);
        window.localStorage.setItem(
            storage_id,
            JSON.stringify(orders.map(normalize))
        )
    }
    function load(gameid) {
        let storage_id = "userscript_order_clipboard_" + gameid;
        if (storage_id in window.localStorage) {
            update_registry(storage_id);
            return JSON.parse(window.localStorage.getItem(storage_id)).map(denormalize);
        }
    }
 
    function is_wz_object(obj) {
        return obj && typeof(obj) === "object" && min.Type in obj;
    }
 
    function normalize(order) {
        if (!is_wz_object(order))
            return order;
        let type_data = type_table[order[min.Type].name];
        let normalized_order = {type: type_data.name};
        type_data.properties.forEach(
            (prop, index) => {
                normalized_order[index] = normalize(order[prop]);
            }
        );
        return normalized_order;
    };
 
    function denormalize(normalized_order) {
        if (!(normalized_order && typeof(normalized_order) === "object" && normalized_order.type))
            return normalized_order;
        let type_data = type_table[min.from_name[normalized_order.type]];
        delete normalized_order.type;
        console.assert(type_data.type, "Unable to find order type " + type_data.name);
        let order = new type_data.type();
        for (let i = 0; i < type_data.properties.length; i++) {
            order[type_data.properties[i]] = denormalize(normalized_order[i]);
        }
        return order;
    }
 
    function clean_registry() {
        let registry = JSON.parse(localStorage.getItem("userscript_order_clipboard_registry")) || {};
        let now = Date.now();
        for (let id in registry) {
            let last_access = registry[id];
            let days_elapsed = (now - last_access) / (1000 * 60 * 60 * 24);
            if (days_elapsed >= DAYS_TO_STORE) {
                console.log("Removing " + id + " as more than " + DAYS_TO_STORE + " days have passed", days_elapsed);
                localStorage.removeItem(id);
                delete registry[id];
            }
        }
        localStorage.setItem("userscript_order_clipboard_registry", JSON.stringify(registry));
    }
 
    function update_registry(storage_id) {
        let registry = JSON.parse(localStorage.getItem("userscript_order_clipboard_registry")) || {};
        registry[storage_id] = Date.now();
        localStorage.setItem("userscript_order_clipboard_registry", JSON.stringify(registry));
    }
 
    async function get_ujs_script() {
        let ujs_script = Array.from(
            document.scripts
        ).map(
            script => script.src
        ).filter(
            script => script.match(/^https:\/\/warzonecdn.com\/js\/Release\/ujs/)
        );
        console.assert(ujs_script.length > 0, "Failed to find ujs script");
        console.assert(ujs_script.length <= 1, "Found multiple ujs scripts");
        ujs_script = await $.ajax(ujs_script[0]);
        ujs_script = ujs_script.replaceAll("\n", "");
        return ujs_script;
    }
 
    function waitFor(condition) {
        return new Promise(resolve => {
            (function delayed() {
                if (condition()) resolve();
                else setTimeout(delayed);
            })();
        });
    }
 
    let min = {};
    let type_table = {};
 
    waitFor(() => window.UJS_Hooks).then(async () => {
        clean_registry();
 
        let ujs_script = await get_ujs_script();
 
        min.from_name = Object.fromEntries(
            Array.from(
                ujs_script.matchAll(/([\w$_]+)\.(__name__|.)=\["warlight","shared","(GameOrder\w*)"\];/g),
                match => [match[3], match[1]]
            )
        );
 
        min.Type = ujs_script.match(new RegExp(`,([\\w$_]+):${min.from_name.GameOrder.replace("$", "\\$")},`))[1];
        let match = ujs_script.match(/this\.([\w$_]+)\.([\w$_]+)\(\[\]\),/);
        min.TurnBuilder = match[1];
        min.LoadOrders = match[2];
        min.PreviousTurn = ujs_script.match(/="GetLatestGameInfo\.5";0>=this\.[\w$_]+\.[\w$_]+\?\([\w$_]+\.([\w$_]+)=null/)[1];
        min.OrderList = ujs_script.match(/"Not fully deployed"\);return this\.([\w$_]+)/)[1];
        match = ujs_script.match(/\(null!=this\.[\w$_]+\.([\w$_]+)\.([\w$_]+),"Root\.Links\.Latest is null"\)/);
        min.Links = match[1];
        min.Latest = match[2];
        min.Order = ujs_script.match(/\(this\.([\w$_]+),[\w$_]+\)\)return"The card that was played on one of the previous turns is no longer active."/)[1];
 
        function crawl(id) {
            let obj = {};
            obj.name = ujs_script.match(new RegExp(`${id.replace("$", "\\$")}\\.(__name__|.)=\\[("\\w+",)*"(\\w+)"\\];`))[3];
            min.from_name[obj.name] = id;
            let body = ujs_script.match(new RegExp(
                  "("
                +     `function ${id.replace("$", "\\$")}`  // "function f"
                +     `|${id.replace("$", "\\$")}=function` // or "f = function"
                + ")"
                + "\\(([\\w$_,])*\\)"                       // "(arg1, arg2)"
                + "{(.*?)}"                                 // "{ body }"
            ))[3];
            obj.properties = [];
            Array.from(body.matchAll(/this\.([\w$_]+)=(new ([\w$_]+))?/g)).forEach(
                match => {
                    obj.properties.push(match[1]);
                    if (match[3] && !(match[3] in type_table)) {
                        crawl(match[3]);
                    }
                }
            );
            type_table[id] = obj;
        }
        Array.from(
            ujs_script.matchAll(/([\w$_]+)\.(__name__|.)=\["warlight","shared","(GameOrder\w*)"\];/g),
            match => match[1]
        ).forEach(crawl);
 
        function grab_types(obj) {
            if (!is_wz_object(obj))
                return;
            let type = obj[min.Type].name;
            if (type in type_table && !type_table[type].type) {
                type_table[type].type = obj[min.Type];
                Object.values(obj).forEach(grab_types);
            }
        }
        (window.UJS_Hooks[min.TurnBuilder][min.OrderList] || []).map(order_container => order_container[min.Order]).forEach(grab_types);
        if (window.UJS_Hooks[min.Links][min.Latest][min.PreviousTurn]) {
            window.UJS_Hooks[min.Links][min.Latest][min.PreviousTurn][min.OrderList].forEach(grab_types);
        }
 
        $(window).on("keydown.userscript", null, null, event => {
            event.keyCode == 67 && event.ctrlKey && copy();
            event.keyCode == 86 && event.ctrlKey && paste();
        });
 
        console.log("Userscript loaded");
    });
})();