- // ==UserScript==
- // @name IdlePixel Market Overhaul
- // @namespace com.anwinity.idlepixel
- // @version 1.0.17
- // @description Overhaul of market UI and functionality.
- // @author Anwinity
- // @license MIT
- // @match *://idle-pixel.com/login/play*
- // @grant none
- // @require https://gf.qytechs.cn/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- const XP_PER = {
- stone: 0.1,
- copper: 1,
- iron: 5,
- silver: 10,
- gold: 20,
- promethium: 100,
- titanium: 300,
-
- bronze_bar: 5,
- iron_bar: 25,
- silver_bar: 50,
- gold_bar: 100,
- promethium_bar: 500,
- titanium_bar: 2000
- };
-
- const BONEMEAL_PER = {
- bones: 1,
- big_bones: 2,
- ice_bones: 3,
- ashes: 2
- };
-
- class MarketPlugin extends IdlePixelPlusPlugin {
- constructor() {
- super("market", {
- about: {
- name: GM_info.script.name,
- version: GM_info.script.version,
- author: GM_info.script.author,
- description: GM_info.script.description
- },
- config: [
- {
- id: "condensed",
- label: "Condensed UI",
- type: "boolean",
- default: true
- },
- {
- id: "sortMethod",
- label: "Sort Method",
- type: "select",
- default: "default",
- options: [
- {value: "default", label: "Default"},
- {value: "timeDESC", label: "Time (Newest First)"},
- {value: "timeASC", label: "Time (Newest Last)"},
- {value: "priceASC", label: "Price (Cheapest First)"},
- {value: "priceDESC", label: "Price (Cheapest Last)"},
- ]
- },
- {
- id: "highlightBest",
- label: "Highlight Best",
- type: "boolean",
- default: true
- },
- {
- id: "autoMax",
- label: "Autofill Max Buy",
- type: "boolean",
- default: false
- },
- ]
- });
- this.lastBrowsedItem = "all";
- this.lastCategoryFilter = "all";
- }
-
- onConfigsChanged() {
- this.applyCondensed(this.getConfig("condensed"));
- }
-
- applyCondensed(condensed) {
- if(condensed) {
- $("#panel-player-market").addClass("condensed");
- $("#modal-market-select-item").addClass("condensed");
- }
- else {
- $("#panel-player-market").removeClass("condensed");
- $("#modal-market-select-item").removeClass("condensed");
- }
- }
-
- onLogin() {
- const self = this;
-
- $("head").append(`
- <style id="styles-market">
-
- #market-table {
- margin-top: 0.5em !important;
- }
- #market-table tr.cheaper {
- background-color: rgb(50, 205, 50, 0.25);
- }
- #market-table tr.cheaper > td {
- background-color: rgb(50, 205, 50, 0.25);
- }
- #panel-player-market.condensed > center {
- display: flex;
- flex-direction: row;
- justify-content: center;
- }
- #panel-player-market.condensed div.player-market-slot-base {
- height: 400px;
- }
- #panel-player-market.condensed div.player-market-slot-base hr {
- margin-top: 2px;
- margin-bottom: 4px;
- }
- #panel-player-market.condensed div.player-market-slot-base br + #panel-player-market.condensed div.player-market-slot-base br {
- display: none;
- }
- #panel-player-market.condensed div.player-market-slot-base[id^="player-market-slot-occupied"] button {
- padding: 2px;
- }
-
- #panel-player-market.condensed #market-table th {
- padding: 2px 4px;
- }
-
- #panel-player-market.condensed #market-table td {
- padding: 2px 4px;
- }
-
- #modal-market-select-item.condensed #modal-market-select-item-section .select-item-tradables-catagory {
- margin: 6px 6px;
- padding: 6px 6px;
- }
-
- #modal-market-select-item.condensed #modal-market-select-item-section .select-item-tradables-catagory hr {
- margin-top: 2px;
- margin-bottom: 2px;
- }
- #market-category-filters {
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: start;
- flex-wrap: wrap;
- margin: 0.25em;
- }
- #market-category-filters > button {
- display: inline-block;
- margin: 0.25em;
- }
- #market-category-filters > button.active {
- background-color: #74DBDB;
- }
-
- </style>
- `);
- // modal-market-configure-item-to-sell-amount
- const sellModal = $("#modal-market-configure-item-to-sell");
- const sellAmountInput = sellModal.find("#modal-market-configure-item-to-sell-amount");
- sellAmountInput.after(`
- <button type="button" onclick="IdlePixelPlus.plugins.market.applyOneAmountSell()">1</button>
- <button type="button" onclick="IdlePixelPlus.plugins.market.applyMaxAmountSell()">max</button>
- <button type="button" onclick="IdlePixelPlus.plugins.market.applyMaxAmountSell(true)">max-1</button>
- `);
- const sellPriceInput = sellModal.find("#modal-market-configure-item-to-sell-price-each").after(`
- <button type="button" onclick="IdlePixelPlus.plugins.market.applyMinPriceSell()">min</button>
- <button type="button" onclick="IdlePixelPlus.plugins.market.applyMidPriceSell()">mid</button>
- <button type="button" onclick="IdlePixelPlus.plugins.market.applyMaxPriceSell()">max</button>
- <br /><br />
- Total: <span id="modal-market-configure-item-to-sell-total"></span>
- `);
-
- sellAmountInput.on("input change", () => this.applyTotalSell());
- sellPriceInput.on("input change", () => this.applyTotalSell());
-
- const buyModal = $("#modal-market-purchase-item");
- const buyAmountInput = buyModal.find("#modal-market-purchase-item-amount-input");
- buyAmountInput.after(`
- <button type="button" onclick="IdlePixelPlus.plugins.market.applyOneAmountBuy()">1</button>
- <button type="button" onclick="IdlePixelPlus.plugins.market.applyMaxAmountBuy()">max</button>
- <br /><br />
- Total: <span id="modal-market-purchase-item-total"></span>
- <br />
- Owned: <item-display data-format="number" data-key="coins"></item-display>
- `);
- buyAmountInput.on("input change", () => this.applyTotalBuy());
-
- // wrap Market.browse_get_table to capture last selected
- const original_market_browse = Market.browse_get_table;
- Market.browse_get_table = function(item) {
- return self.browseGetTable(item)
- .always(() => {
- self.filterTable();
- });
- }
-
- $("#market-table").css("margin-top", "24px");
- $("#market-table").parent().before(`<div id="market-category-filters"><div>`);
-
- // wrap Market.load_tradables to populate category filters
- const original_load_tradables = Market.load_tradables;
- Market.load_tradables = function(data) {
- original_load_tradables.apply(this, arguments);
- self.createFilterButtons();
- }
- self.createFilterButtons();
-
- $(`#panel-player-market button[onclick^="Market.clicks_browse_player_market_button"]`)
- .first()
- .after(`<button id="refresh-market-table-button" type="button" style="margin-left: 0.5em" onclick="IdlePixelPlus.plugins.market.refreshMarket(true);">Refresh</button>`);
-
- //$("button.market-remove-button").after('<br><br><br><button onclick="" class="market-rebrowse-button">Browse</button>');
-
- this.onConfigsChanged();
- }
-
- browseGetTable(item) {
- //console.log(`browseGetTable("${item}")`);
- const self = this;
- this.lastBrowsedItem = item;
- if(item != "all") {
- self.lastCategoryFilter = "all";
- }
- if(item == "all") {
- $("#market-category-filters").show();
- }
- else {
- $("#market-category-filters").hide();
- }
-
- // A good chunk of this is taking directly from Market.browse_get_table
- hide_element("market-table");
- show_element("market-loading");
- let best = {};
- let bestList = {};
- return $.get(`../../market/browse/${item}/`).done(function(data) {
- const xpMultiplier = DonorShop.has_donor_active(IdlePixelPlus.getVar("donor_bonus_xp_timestamp")) ? 1.1 : 1;
- //console.log(data);
- data.forEach(datum => {
- const priceAfterTax = datum.market_item_price_each * 1.01;
- switch(datum.market_item_category) {
- case "bars":
- case "ores": {
- let perCoin = (priceAfterTax / (xpMultiplier*XP_PER[datum.market_item_name]));
- datum.perCoin = perCoin;
- datum.perCoinLabel = `${perCoin.toFixed(perCoin < 10 ? 2 : 1)} coins/xp`;
- if(!best[datum.market_item_category]) {
- best[datum.market_item_category] = perCoin;
- bestList[datum.market_item_category] = [datum];
- }
- else {
- if(perCoin == best[datum.market_item_category]) {
- bestList[datum.market_item_category].push(datum);
- }
- else if(perCoin < best[datum.market_item_category]) {
- bestList[datum.market_item_category] = [datum];
- best[datum.market_item_category] = perCoin;
- }
- }
- break;
- }
- case "logs": {
- let perCoin = (priceAfterTax / Cooking.getHeatPerLog(datum.market_item_name));
- datum.perCoin = perCoin;
- datum.perCoinLabel = `${perCoin.toFixed(perCoin < 10 ? 2 : 1)} coins/heat`;
- if(!best[datum.market_item_category]) {
- best[datum.market_item_category] = perCoin;
- bestList[datum.market_item_category] = [datum];
- }
- else {
- if(perCoin == best[datum.market_item_category]) {
- bestList[datum.market_item_category].push(datum);
- }
- else if(perCoin < best[datum.market_item_category]) {
- bestList[datum.market_item_category] = [datum];
- best[datum.market_item_category] = perCoin;
- }
- }
- break;
- }
- case "raw_fish":
- case "cooked_fish":{
- let perCoin = (priceAfterTax / Cooking.get_energy(datum.market_item_name));
- datum.perCoin = perCoin;
- datum.perCoinLabel = `${perCoin.toFixed(perCoin < 10 ? 2 : 1)} coins/energy`;
- if(!best[datum.market_item_category]) {
- best[datum.market_item_category] = perCoin;
- bestList[datum.market_item_category] = [datum];
- }
- else {
- if(perCoin == best[datum.market_item_category]) {
- bestList[datum.market_item_category].push(datum);
- }
- else if(perCoin < best[datum.market_item_category]) {
- bestList[datum.market_item_category] = [datum];
- best[datum.market_item_category] = perCoin;
- }
- }
- break;
- }
- case "bones": {
- let perCoin = (priceAfterTax / BONEMEAL_PER[datum.market_item_name]);
- datum.perCoin = perCoin;
- datum.perCoinLabel = `${perCoin.toFixed(perCoin < 10 ? 2 : 1)} coins/bonemeal`;
- if(!best[datum.market_item_category]) {
- best[datum.market_item_category] = perCoin;
- bestList[datum.market_item_category] = [datum];
- }
- else {
- if(perCoin == best[datum.market_item_category]) {
- bestList[datum.market_item_category].push(datum);
- }
- else if(perCoin < best[datum.market_item_category]) {
- bestList[datum.market_item_category] = [datum];
- best[datum.market_item_category] = perCoin;
- }
- }
- break;
- }
- default: {
- datum.perCoin = Number.MAX_SAFE_INTEGER;
- datum.perCoinLabel = "";
- break;
- }
- }
- });
- Object.values(bestList).forEach(bestCatList => bestCatList.forEach(datum => datum.best=true));
- const sortMethod = self.getConfig("sortMethod");
- switch(sortMethod) {
- case "timeDESC": {
- data = data.sort((a, b) => b.market_item_post_timestamp - a.market_item_post_timestamp);
- break;
- }
- case "timeASC": {
- data = data.sort((a, b) => a.market_item_post_timestamp - b.market_item_post_timestamp);
- break;
- }
- case "priceASC": {
- data = data.sort((a, b) => {
- if(a.perCoin != b.perCoin && typeof a.perCoin==="number" && typeof b.perCoin==="number") {
- return a.perCoin - b.perCoin;
- }
- return a.market_item_price_each - b.market_item_price_each;
- });
-
- // DEBUG
- //data.filter(x => x.market_item_category == "cooked_fish").forEach(d => {
- // console.log(`${d.market_item_name} ${d.perCoin} ${d.market_item_price_each}`);
- //});
- //
-
- break;
- }
- case "priceDESC": {
- data = data.sort((a, b) => {
- if(a.perCoin != b.perCoin && typeof a.perCoin==="number" && typeof b.perCoin==="number") {
- return b.perCoin - a.perCoin;
- }
- return b.market_item_price_each - a.market_item_price_each;
- });
- break;
- }
- }
- // console.log(data);
- let html = "<tr><th>ITEM</th><th></th><th>AMOUNT</th><th>PRICE EACH</th><th>CATEGORY</th><th>EXPIRES IN</th></tr>";
- // in case you want to add any extra data to the table but still use this script
- if(typeof window.ModifyMarketDataHeader === "function") {
- html = window.ModifyMarketDataHeader(html);
- }
- data.forEach(datum => {
- let market_id = datum.market_id;
- let player_id = datum.player_id;
- let item_name = datum.market_item_name;
- let amount = datum.market_item_amount;
- let price_each = datum.market_item_price_each;
- let category = datum.market_item_category;
- let timestamp = datum.market_item_post_timestamp;
- let perCoinLabel = datum.perCoinLabel;
- let best = datum.best && self.getConfig("highlightBest");
-
- let your_entry = "";
- if(Items.getItem("player_id") == player_id) {
- your_entry = "<span class='color-grey font-small'><br /><br />(Your Item)</span>";
- }
-
- let rowHtml = "";
- rowHtml += `<tr onclick="Modals.market_purchase_item('${market_id}', '${item_name}', '${amount}', '${price_each}'); IdlePixelPlus.plugins.market.applyMaxAmountBuyIfConfigured();" class="hover${ best ? ' cheaper' : '' }">`;
- rowHtml += `<td>${Items.get_pretty_item_name(item_name)}${your_entry}</td>`;
- rowHtml += `<td><img src="https://d1xsc8x7nc5q8t.cloudfront.net/images/${item_name}.png" /></td>`;
- rowHtml += `<td>${amount}</td>`;
- rowHtml += `<td><img src="https://d1xsc8x7nc5q8t.cloudfront.net/images/coins.png" /> ${Market.get_price_after_tax(price_each)}`;
- if(perCoinLabel) {
- rowHtml += `<br /><span style="font-size: 80%; opacity: 0.8">${perCoinLabel}</span>`;
- }
- rowHtml += `</td>`;
- rowHtml += `<td>${category}</td>`;
- rowHtml += `<td>${Market._get_expire_time(timestamp)}</td>`;
- rowHtml += `</tr>`;
-
- // in case you want to add any extra data to the table but still use this script
- if(typeof window.ModifyMarketDataRow === "function") {
- rowHtml = window.ModifyMarketDataRow(datum, rowHtml);
- }
- html += rowHtml;
- });
-
- document.getElementById("market-table").innerHTML = html;
- hide_element("market-loading");
- show_element("market-table");
- });
-
- }
-
- createFilterButtons() {
- const filters = $("#market-category-filters");
- filters.empty();
- if(Market.tradables) {
- const categories = [];
- Market.tradables.forEach(tradable => {
- if(!categories.includes(tradable.category)) {
- categories.push(tradable.category);
- }
- });
- filters.append(`<button data-category="all" onclick="IdlePixelPlus.plugins.market.filterTable('all')">All</button>`);
- categories.forEach(cat => {
- filters.append(`<button data-category="${cat}" onclick="IdlePixelPlus.plugins.market.filterTable('${cat}')">${cat.replace(/_/g, " ").replace(/(^|\s)\w/g, s => s.toUpperCase())}</button>`);
- });
- }
- }
-
- filterTable(category) {
-
- if(category) {
- this.lastCategoryFilter = category;
- }
- else {
- category = this.lastCategoryFilter || "all";
- }
-
- $("#market-category-filters button.active").removeClass("active");
- $(`#market-category-filters button[data-category="${category}"]`).addClass("active");
-
- const rows = $("#market-table tbody tr.hover");
- if(category=="all") {
- rows.show();
- }
- else {
- rows.each(function() {
- const row = $(this);
- const rowCategory = row.find("td:nth-child(5)").text();
- if(category == rowCategory) {
- row.show();
- }
- else {
- row.hide();
- }
- });
-
- }
- }
-
- refreshMarket(disableButtonForABit) {
- if(this.lastBrowsedItem) {
- Market.browse_get_table(this.lastBrowsedItem);
- if(disableButtonForABit) { // prevent spam clicking it
- $("#refresh-market-table-button").prop("disabled", true);
- setTimeout(() => {
- $("#refresh-market-table-button").prop("disabled", false);
- }, 700);
- }
- }
- }
-
- applyOneAmountBuy() {
- $("#modal-market-purchase-item #modal-market-purchase-item-amount-input").val(1);
- this.applyTotalBuy();
- }
-
- applyMaxAmountBuyIfConfigured() {
- if(this.getConfig("autoMax")) {
- this.applyMaxAmountBuy();
- }
- }
-
- applyMaxAmountBuy(minus1=false) {
- const coinsOwned = IdlePixelPlus.getVarOrDefault("coins", 0, "int");
- const price = parseInt($("#modal-market-purchase-item #modal-market-purchase-item-price-each").val().replace(/[^\d]+/g, ""));
- const maxAffordable = Math.floor(coinsOwned / price);
- const maxAvailable = parseInt($("#modal-market-purchase-item #modal-market-purchase-item-amount-left").val().replace(/[^\d]+/g, ""));
- let max = Math.min(maxAffordable, maxAvailable);
- if(minus1) {
- max--;
- }
- if(max < 0) {
- max = 0;
- }
- $("#modal-market-purchase-item #modal-market-purchase-item-amount-input").val(max);
- this.applyTotalBuy();
- }
-
- parseIntKMBT(s) {
- if(typeof s === "number") {
- return Math.floor(s);
- }
- s = s.toUpperCase().replace(/[^\dKMBT]+/, "");
- if(s.endsWith("K")) {
- s = s.replace(/K$/, "000");
- }
- else if(s.endsWith("M")) {
- s = s.replace(/M$/, "000000");
- }
- else if(s.endsWith("B")) {
- s = s.replace(/B$/, "000000000");
- }
- else if(s.endsWith("T")) {
- s = s.replace(/T$/, "000000000000");
- }
- return parseInt(s);
- }
-
- applyTotalBuy() {
- const amount = this.parseIntKMBT($("#modal-market-purchase-item #modal-market-purchase-item-amount-input").val());
- const price = this.parseIntKMBT($("#modal-market-purchase-item #modal-market-purchase-item-price-each").val().replace("Price each: ", ""));
- const total = amount*price;
- const totalElement = $("#modal-market-purchase-item-total");
- if(isNaN(total)) {
- totalElement.text("");
- }
- else {
- totalElement.text(total.toLocaleString());
- const coinsOwned = IdlePixelPlus.getVarOrDefault("coins", 0, "int");
- if(total > coinsOwned) {
- totalElement.css("color", "red");
- }
- else {
- totalElement.css("color", "");
- }
- }
- }
-
- currentItemSell() {
- return $("#modal-market-configure-item-to-sell").val();
- }
-
- applyOneAmountSell() {
- const item = this.currentItemSell();
- const owned = IdlePixelPlus.getVarOrDefault(item, 0, "int");
- $("#modal-market-configure-item-to-sell-amount").val(Math.min(owned, 1));
- this.applyTotalSell();
- }
-
- applyMaxAmountSell(minus1=false) {
- const item = this.currentItemSell();
- let max = IdlePixelPlus.getVarOrDefault(item, 0, "int");
- if(minus1) {
- max--;
- }
- if(max < 0) {
- max = 0;
- }
- $("#modal-market-configure-item-to-sell-amount").val(max);
- this.applyTotalSell();
- }
-
- applyMinPriceSell() {
- const min = parseInt($("#modal-market-configure-item-to-sell-label-lower-limit").text().replace(/[^\d]/g, ""));
- $("#modal-market-configure-item-to-sell-price-each").val(min);
- this.applyTotalSell();
- }
-
- applyMidPriceSell() {
- const min = parseInt($("#modal-market-configure-item-to-sell-label-lower-limit").text().replace(/[^\d]/g, ""));
- const max = parseInt($("#modal-market-configure-item-to-sell-label-upper-limit").text().replace(/[^\d]/g, ""));
- const mid = Math.floor((min+max)/2);
- $("#modal-market-configure-item-to-sell-price-each").val(mid);
- this.applyTotalSell();
- }
-
- applyMaxPriceSell() {
- const max = parseInt($("#modal-market-configure-item-to-sell-label-upper-limit").text().replace(/[^\d]/g, ""));
- $("#modal-market-configure-item-to-sell-price-each").val(max);
- this.applyTotalSell();
- }
-
- applyTotalSell() {
- const amount = this.parseIntKMBT($("#modal-market-configure-item-to-sell-amount").val());
- const price = this.parseIntKMBT($("#modal-market-configure-item-to-sell-price-each").val());
- const total = amount*price;
- if(isNaN(total)) {
- $("#modal-market-configure-item-to-sell-total").text("");
- }
- else {
- $("#modal-market-configure-item-to-sell-total").text(total.toLocaleString());
- }
- // TODO total w/ tax
- }
-
- onVariableSet(key, valueBefore, valueAfter) {
-
- }
-
- }
-
- const plugin = new MarketPlugin();
- IdlePixelPlus.registerPlugin(plugin);
-
- })();