Brick Hill Outfit Randomizer

Adds a button to randomize your avatar

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Brick Hill Outfit Randomizer
// @version      1.3
// @description  Adds a button to randomize your avatar
// @author       Noah Cool Boy
// @match        https://www.brick-hill.com/customize/
// @namespace https://greasyfork.org/users/725966
// @license MIT
// ==/UserScript==

function sleep(ms) {
    return new Promise(a => setTimeout(a, ms))
}


let int = setInterval(async () => {
    if (!document.querySelector(".avatar-buttons"))
        return await sleep(10)
    clearInterval(int)

    let outfitCard = document.querySelector(".avatar-buttons")
    let buttons = document.createElement("div")
    buttons.className = "inline"
    buttons.style.marginTop = "5px"
    buttons.innerHTML = `<button class="yellow thin" onclick="rand()"><svg class="svg-icon-large" style="height: 24px; fill: white;" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#fff;text-transform:none;text-orientation:mixed;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;isolation:auto;mix-blend-mode:normal;solid-color:#fff;solid-opacity:1" d="M1 7a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h3v6a1 1 0 0 0 1 1h3.25a1 1 0 0 0 .75-.34 1 1 0 0 0 .75.34H11v-2h-1v-4a1 1 0 0 0-1-1 1 1 0 0 0-1 1v4H6v-6a1 1 0 0 0-1-1H2V9h4a1 1 0 0 0 .66-.25A4.02 4.02 0 0 1 4.56 7H1zm12.44 0a4.02 4.02 0 0 1-2.1 1.75A1 1 0 0 0 12 9h4v2h2V8a1 1 0 0 0-1-1h-3.56z"/><path d="M8 0a3 3 0 0 0-3 3v2a3 3 0 0 0 3 3h2a3 3 0 0 0 3-3V3a3 3 0 0 0-3-3Zm0 2h2c.56 0 1 .44 1 1v2c0 .56-.44 1-1 1H8c-.56 0-1-.44-1-1V3c0-.56.44-1 1-1Z" color="#fff" font-family="sans-serif" font-weight="400" overflow="visible" style="line-height:normal;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-variant-east-asian:normal;font-feature-settings:normal;font-variation-settings:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#fff;text-transform:none;text-orientation:mixed;white-space:normal;shape-padding:0;shape-margin:0;inline-size:0;isolation:auto;mix-blend-mode:normal;solid-color:#fff;solid-opacity:1"/><path style="fill:none;stroke:#fff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round" d="M13 13h10v10H13Z"/><path style="stroke-width:4.75267;stroke-linecap:round;stroke-linejoin:round" d="M16.75 20.25a1 1 0 1 1-2 0 1 1 0 0 1 2 0zM19 18a1 1 0 1 1-2 0 1 1 0 0 1 2 0zm2.25-2.25a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"/></svg></button><div class="button-hint" style="font-weight: 600; margin-top: 5px;">RAND</div>`
    // No judging please ^
    outfitCard.appendChild(buttons)

    let types = Object.fromEntries([...document.querySelectorAll(".optional-types label")].map(v => [v.attributes.for.value, v.innerText]))
    document.rand = function () {
        let modal = document.createElement("div")
        modal.className = "modal"
        modal.innerHTML = `<div class="modal-content"><span class="close">x</span>Random Outfit Generator<hr>What would you like to randomize?<br>${Object.values(types).map(v => `<div class="option"><input type="checkbox" checked><label>${v}</label><br></div>`).join("")}<div class="colors"><input type="checkbox"><label>Body Colors</label><br></div><div class="modal-buttons"><button class="green">Randomize!</button></div></div>`
        modal.querySelector(".close").addEventListener("click", () => {
            modal.remove()
        });
        [...modal.querySelectorAll("input")].forEach(v => { // Is this an "On God" or "Fr" moment
            v.parentNode.querySelector("label").addEventListener("click", () => v.getAttribute("checked") === null ? v.setAttribute("checked", "") : v.removeAttribute("checked"))
        })
        modal.querySelector("button").addEventListener("click", () => {
            randomize(modal)
        })
        outfitCard.appendChild(modal)
    }

    function wait(ms) {
        return new Promise(a => {
            setTimeout(a, ms)
        })
    }

    function randomize(modal) {
        modal.querySelector(".close").remove()
        modal.querySelector("button").remove()
        let status = document.createElement("span")
        modal.querySelector(".modal-content").appendChild(status)
        let opts = [...modal.querySelectorAll(".option")].map(v => v.innerText.trim())
        setTimeout(async () => {
            let inventory = {}
            if (window.localStorage.randomizer_cache && window.localStorage.randomizer_cache_date && Date.now() - window.localStorage.randomizer_cache_date < 1000 * 60 * 60 * 24) {
                inventory = JSON.parse(window.localStorage.randomizer_cache)
            } else {
                for (let x = 0; x < opts.length; x++) {
                    let type = opts[x]
                    inventory[type] = []
                    let apiType = Object.keys(types).find(key => types[key] == type)
                    let cursor = ""
                    while (true) {
                        let req = new XMLHttpRequest()
                        req.open("GET", `https://api.brick-hill.com/v1/user/${document.querySelector("meta[name=user-data]").attributes["data-id"].value}/crate?limit=100&cursor=${cursor}&types[]=${apiType}`, false)
                        req.withCredentials = true
                        req.send()
                        let data = JSON.parse(req.responseText)
                        inventory[type] = inventory[type].concat(data.data.map(v => v.item.id))
                        cursor = data.next_cursor

                        await wait(5)
                        status.innerText = `Getting ${type}`

                        if (!cursor)
                            break
                    }
                }

                window.localStorage.randomizer_cache = JSON.stringify(inventory)
                window.localStorage.randomizer_cache_date = Date.now()
            }
            opts = [...modal.querySelectorAll(".option")].map(v => [v.innerText.trim(), v.querySelector("input").checked])
            opts = opts.filter(v => v[1]).map(v => v[0])
            Object.keys(inventory).filter(v => !opts.includes(v)).forEach(v => delete inventory[v])
            if (inventory.Hats) {
                inventory.Hats2 = inventory.Hats
                inventory.Hats3 = inventory.Hats
            }
            let messages = ["Shuffling the stuff", "Doing things", "Randomizing the avatar", "Making your new outfit", "Tiny computer is thinking", "Generating a combo", "The machine is hard at work"]
            status.innerText = messages[Math.floor(Math.random() * messages.length)]
            console.log(inventory)
            let outfit = {
                rebase: false,
                instructions: [],
                _token: document.querySelector("meta[name=csrf-token]").content
            }
            Object.keys(inventory).filter(v => v.length).forEach(v => outfit.instructions.push({ [inventory[v][Math.floor(Math.random() * inventory[v].length)]]: "wear" }))

            let bodyColors = modal.querySelector(".colors > input")
            if (bodyColors.checked) {
                outfit.colors = {}
                let bodyParts = ["torso", "left_arm", "right_arm", "left_leg", "right_leg", "head"]
                bodyParts.forEach(key => outfit.colors[key] = Array(6).fill(0).map(v => "0123456789ABCDEF"[Math.floor(Math.random() * 16)]).join(""))
            }

            let token = document.cookie.match(/XSRF-TOKEN=([a-zA-Z0-9%]+)/s)[1] // Turbo power brain mode
            let req = new XMLHttpRequest()
            req.open("POST", `https://api.brick-hill.com/v1/user/render/process`, false)
            req.setRequestHeader("X-XSRF-TOKEN", token)
            req.setRequestHeader("Content-Type", "application/json")
            req.withCredentials = true
            req.send(JSON.stringify(outfit))

            setTimeout(() => {
                location.reload()
            }, 1000) // Increase user satisfaction
        }, 1)
    }
}, 10);