Subeta Sorting

This script sorts NPC shops prices by profit.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         Subeta Sorting
// @namespace    http://artees.pw/
// @description  This script sorts NPC shops prices by profit.
// @version      1.8
// @author       Artees
// @match        *://subeta.net/shop.php?shop*
// @require      https://greasyfork.org/scripts/30529-subeta-searching/code/Subeta%20Searching.js?version=203734
// @grant        none
// @icon         https://subeta.net/favicon.ico
// @icon64       https://img.subeta.net/items/bottle_apothecary_06.gif
// ==/UserScript==

const KEY_MIN_PROFIT = "minProfit",
    KEY_MAX_ITEMS = "maxItems",
    KEY_BUYING = "buying",
    KEY_SHOP_URL = "shopUrl";

var curItem = parseItemsForSorting(),
    sortedItems = [],
    minProfit = getFromStorage(localStorage, KEY_MIN_PROFIT, 0),
    maxItems = getFromStorage(localStorage, KEY_MAX_ITEMS, 10),
    buyCountdown = null,
    isBuying = getFromStorage(localStorage, KEY_BUYING, false),
    buyingIntervalId = 0,
    buyingTimeoutId = 0,
    isSortingComplete = false,
    pricedCount = 0;

function parseItemsForSorting() {
    var itemDivs = document.getElementsByClassName("two wide column");
    if (itemDivs.length === 0 || itemDivs[0].getElementsByTagName("form").length === 0) return null;
    function getPriceField(itemDiv) {
        return new PriceField(itemDiv);
    }
    return parseItems(itemDivs, getPriceField);
}

function Item(itemDiv, priceField) {
    var start = itemDiv.innerHTML.indexOf("<br>"),
        end = itemDiv.innerHTML.indexOf("<br>", start + 4);
    this.itemDiv = itemDiv;
    this.name = replaceAll(itemDiv.innerHTML.substring(start + 4, end), "	", "");
    this.name = replaceAll(this.name, "\n", "");
    this.cacheKey = "c " + this.name;
    this.next = null;
    this.isPriced = false;
    this.priceField = priceField;
    this.resetPrice = function () {
        var end = this.priceField.value.indexOf(CY) + CY.length;
        return this.priceField.value.substring(0, end);
    };
    this.priceField.value = this.resetPrice();
    this.getPrice = function () {
        var priceString = replaceAll(this.resetPrice(), ",", "");
        return parseInt(priceString);
    };
    this.actualPrice = this.getPrice();
    var isComplete = true;
    this.onSelect = function() {
        isComplete = true;
        forEachItem(getNext);
        if (isComplete) {
            isSortingComplete = true;
            switchBuying();
        }
        return !isComplete;
    };
    function getNext(i) {
        if (i === null || !i.isPriced) {
            curItem = i;
            isComplete = false;
            return false;
        }
        return true;
    }
}

function PriceField(itemDiv) {
    var label = itemDiv.getElementsByTagName("b")[0];
    return {
        get value() {
            return label.innerText;
        },
        set value(value) {
            label.innerText = value;
        },
        get style() {
            return label.style;
        },
        set style(value) {
            label.style = value;
        }
    };
}

createUI();

function createUI() {
    var s = document.getElementsByClassName("shop_item_container")[0];
    if (s === undefined) {
        goBackAfterBuying();
        return;
    }
    var ui = s.insertBefore(document.createElement("div"), s.firstChild);
    ui.appendChild(document.createTextNode("Delay: "));
    var itemDelayInput = document.createElement("INPUT");
    itemDelayInput.value = itemDelay + " sec.";
    itemDelayInput.onchange = function () {
        itemDelay = parseInt(this.value);
        localStorage[KEY_ITEM_DELAY] = itemDelay;
    };
    ui.appendChild(itemDelayInput);
    ui.appendChild(createSpace());
    var button = document.createElement("a");
    button.className = "ui button tiny";
    button.innerText = "Purge Cache";
    button.onclick = function () {
        forEachItem(function (i) {
            localStorage.removeItem(i.cacheKey);
            location.reload();
            return true;
        });
    };
    ui.appendChild(button);
    ui.appendChild(document.createElement("br"));
    var autobuy = document.createElement("input");
    autobuy.type = "checkbox";
    autobuy.checked = isBuying;
    autobuy.onchange = function () {
        isBuying = this.checked;
        localStorage[KEY_BUYING] = isBuying;
        switchBuying();
    };
    ui.appendChild(autobuy);
    ui.appendChild(document.createTextNode(" Autobuy"));
    ui.appendChild(createSpace());
    ui.appendChild(document.createTextNode("Min profit: "));
    var minProfitInput = document.createElement("INPUT");
    minProfitInput.value = minProfit + CY;
    minProfitInput.onchange = function () {
        minProfit = parseInt(this.value);
        localStorage[KEY_MIN_PROFIT] = minProfit;
        switchBuying();
    };
    ui.appendChild(minProfitInput);
    ui.appendChild(createSpace());
    ui.appendChild(document.createTextNode("Number of items: "));
    var maxItemsInput = document.createElement("INPUT");
    maxItemsInput.value = maxItems;
    maxItemsInput.onchange = function () {
        maxItems = parseInt(this.value);
        localStorage[KEY_MAX_ITEMS] = maxItems;
        switchBuying();
    };
    ui.appendChild(maxItemsInput);
    ui.appendChild(createSpace());
    buyCountdown = document.createTextNode("Disabled");
    ui.appendChild(buyCountdown);
}

loadCache();

function loadCache() {
    forEachItem(function (i) {
        if (i === null) return false;
        if (localStorage[i.cacheKey] !== undefined) {
            var profit = getFromStorage(localStorage, i.cacheKey, 0) - i.getPrice();
            setProfit(i, profit);
        }
        return true;
    });
    sortPrices();
}

function setProfit(item, profit) {
    item.priceField.value = profit + CY;
    item.priceField.style.color = profit > 0 ? "green" : "red";
    item.isPriced = true;
    pricedCount++;
}

if (curItem !== null && curItem.onSelect()) {
    search();
}

function filterPrices(prices) {
    if (prices.length > 0) {
        for (var i = 0; i < prices.length; i++) {
            var price = prices[i];
            if (price.isNPC()) continue;
            localStorage[curItem.cacheKey] = price.valueOf();
            var profit = price.valueOf() - curItem.getPrice();
            setProfit(curItem, profit);
            break;
        }
    } else {
        curItem.priceField.value = "(!) " + curItem.resetPrice() + CY;
        curItem.priceField.style.color = "orange";
    }
    sortPrices();
}

function sortPrices() {
    sortedItems = [];
    forEachItem(function (i) {
        if (i === null) return false;
        sortedItems.push(i);
        return true;
    });
    sortedItems.sort(comparePrices);
    function comparePrices(a, b) {
        if (!b.isPriced) return -1e100;
        if (!a.isPriced) return 1e100;
        return b.getPrice() - a.getPrice();
    }
    for (var i = 0; i < sortedItems.length; i++) {
        sortedItems[i].itemDiv.parentNode.appendChild(sortedItems[i].itemDiv);
    }
    switchBuying();
}

function switchBuying() {
    if (buyCountdown === null) return;
    highlightItems();
    function highlightItems() {
        for (var i = 0; i < sortedItems.length; i++) {
            var item = sortedItems[i];
            item.priceField.style.fontWeight =
                (item.isPriced && item.getPrice() < minProfit) ? "normal" : "bold";
        }
    }
    var item = getMostProfitableItem();
    function getMostProfitableItem() {
        var spText = document.getElementsByClassName("widget-login-sp")[0].innerText,
            sp = parseInt(replaceAll(spText, ",", ""));
        for (var i = 0; i < sortedItems.length; i++) {
            if (sortedItems[i].actualPrice <= sp) {
                return sortedItems[i];
            }
        }
        return null;
    }
    if (!isBuying || !isSortingComplete || maxItems <= 0 || item === null ||
        item.getPrice() < minProfit) {
        clearInterval(buyingIntervalId);
        clearTimeout(buyingTimeoutId);
        if (isBuying && isSortingComplete && maxItems > 0 && item !== null) {
            buyCountdown.data = "Go to random shop...";
            goToNextShop();
        }
        else {
            buyCountdown.data =
                (isSortingComplete || !isBuying) ? "Disabled" : getProgress();
        }
        if (item === null) {
            buyCountdown.data = "Not enough sP!";
        }
        return;
    }
    function getProgress() {
        var k = 8,
            p = Math.floor(pricedCount/k),
            l = Math.floor(sortedItems.length/k) - p;
        return "Sorting " + new Array(p).join("█") + new Array(l).join("░");
    }
    var count = 5;
    buyCountdown.data = count.toString();
    buyingIntervalId = setInterval(buyItem, 1000);
    function buyItem() {
        count--;
        buyCountdown.data = count.toString();
        if (count > 0) return;
        clearInterval(buyingIntervalId);
        buyCountdown.data = "Buying...";
        sessionStorage[KEY_SHOP_URL] = location.href;
        buyingTimeoutId = setTimeout(click, 1000);
        function click() {
            maxItems--;
            localStorage[KEY_MAX_ITEMS] = maxItems;
            item.itemDiv.getElementsByClassName("ui image")[0].click();
        }
    }
}

function goToNextShop() {
    var shops = [36, 31, 28, 30, 32, 41, 11, 14, 34, 6, 29, 37, 16, 5, 19, 2, 22, 46, 49, 40,
            24, 45, 26, 23, 12, 44, 27, 39, 42, 9, 21, 4, 17, 20, 47, 25],
        url = location.href.split("="),
        iNext = Math.floor(Math.random()*shops.length),//shops.indexOf(parseInt(url[1])) + 1,
        next = url[0] + "=" + shops[(iNext >= shops.length) ? 0 : iNext].toString();
    open(next, "_self");
}

function goBackAfterBuying() {
    var url = sessionStorage[KEY_SHOP_URL];
    if (url === undefined) return;
    sessionStorage.removeItem(KEY_SHOP_URL);
    open(url, "_self");
}

onComplete();

function onComplete() {
    if (document.body.textContent.indexOf("There are no items stocked") > -1) {
        goToNextShop();
    }
}