IdlePixel+ New Card Interface

Improved interface for opening new cards, receiving cards in trade & trading cards

当前为 2024-04-19 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         IdlePixel+ New Card Interface
// @namespace    lbtechnology.info
// @version      1.2.0
// @description  Improved interface for opening new cards, receiving cards in trade & trading cards
// @author       Zlef
// @license      MIT
// @match        *://idle-pixel.com/login/play*
// @grant        none
// @icon         https://d1xsc8x7nc5q8t.cloudfront.net/images/tcg_back_50.png
// @require      https://greasyfork.org/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905
// ==/UserScript==

(function() {
    'use strict';

    class TCGRevamp extends IdlePixelPlusPlugin {
        constructor() {
            super("TCG Interface Changes", {
                about: {
                    name: GM_info.script.name,
                    version: GM_info.script.version,
                    author: GM_info.script.author,
                    description: GM_info.script.description
                }
            });

            this.showPopup = false;
            this.messageStart = "You got a"
            this.trade = false;
            this.card = "";
            this.refreshTCG = "";
            this.currentPopup = null;
            this.savedUsernamesTCG = null;
            this.previousTradeUsername = null;
            this.loadUsernames();


            this.overlay = document.createElement('div');
            this.overlay.id = 'newCardOverlay';
            this.overlay.style.position = 'fixed';
            this.overlay.style.top = '0';
            this.overlay.style.left = '0';
            this.overlay.style.width = '100%';
            this.overlay.style.height = '100%';
            this.overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
            this.overlay.style.zIndex = '1000';
            this.overlay.style.display = 'flex';
            this.overlay.style.justifyContent = 'center';
            this.overlay.style.alignItems = 'center';
        }

        onLogin() {
            this.originalTcgGiveCard = Modals.open_loot_dialogue;
            Modals.open_tcg_give_card = (modal_id, card) => {
                this.newTradePopup(card);
            }

            if (!CardData.data) {
                CardData.fetchData();
            }
            this.monitorRevealTCG();
        }

        saveUsernames(){
            const saveData = JSON.stringify({usernames: this.savedUsernamesTCG});
            localStorage.setItem(`savedUsernamesTCG`, saveData)

        }

        loadUsernames(){
            const savedUsernamesTCG = localStorage.getItem(`savedUsernamesTCG`)

            if (savedUsernamesTCG){
                this.savedUsernamesTCG = JSON.parse(savedUsernamesTCG).usernames
            } else {
                this.savedUsernamesTCG = null;
            }

        }

        monitorRevealTCG() {
            const originalWebSocketSend = WebSocket.prototype.send;
            const self = this;
            WebSocket.prototype.send = function(data) {
                try {
                    originalWebSocketSend.call(this, data);
                    if (data === 'REVEAL_TCG_CARD') {
                        self.showPopup = true;
                    }
                } catch (error) {
                    console.error('Error in overridden WebSocket send:', error);
                }
            };
        }

        onMessageReceived(data){
            const originalOpenImageModal = Modals.open_image_modal;
            const self = this;

            if (data.includes("OPEN_DIALOGUE")){
                Modals.open_image_modal = function(title, imgUrl, description, footerText, closeBtnText, anotherBtnText, isModalDismissible) {

                    if (description.includes("You were given a card from")) {

                        const usernameRegex = /You were given a card from (.*?)<br \/>/;
                        const cardRegex = /<span class='color-grey'>(.*?)<\/span>/;

                        const usernameMatch = description.match(usernameRegex);
                        const cardMatch = description.match(cardRegex);

                        let username = "";
                        let card = "";

                        if (usernameMatch && usernameMatch.length > 1) {
                            username = usernameMatch[1];
                        }

                        if (cardMatch && cardMatch.length > 1) {
                            self.card = cardMatch[1]
                        }


                        self.messageStart = `${username} sent you a`
                        self.trade = true;
                        self.showPopup = true;
                    } else {
                        originalOpenImageModal.call(this, title, imgUrl, description, footerText, closeBtnText, anotherBtnText, isModalDismissible);
                    }
                }
            }

            if (data.includes("REFRESH_TCG")){
                this.refreshTCG = data;
                if (this.showPopup){
                    if (this.trade){
                        this.getCardInfo();
                        // this.trade = false;
                        this.card = "";
                    } else {
                        this.getCardInfo();
                    }

                    this.showPopup = false;
                    this.messageStart = "You got a "
                }
            }
        }

        chunks(array, chunkSize) {
            const result = [];
            for (let i = 0; i < array.length; i += chunkSize) {
                const chunk = array.slice(i, i + chunkSize);
                result.push(chunk);
            }
            return result;
        }

        getCardInfoTrade(cardid) {
            const cardData = this.refreshTCG.replace('REFRESH_TCG=', '').split('~');
            // cardData looks like this: [ "21882", "tcg_thief_icon", "false", "21881", "tcg_raw_swordfish", "false", "21880", "tcg_raw_tuna", "false", "21805", … ]
            const index = cardData.indexOf(cardid);
            const isThisYourCard = [cardData[index], cardData[index + 1], cardData[index + 2]];

            const isHolo = isThisYourCard[2] === 'true';
            const cardHTML = CardData.getCardHTML(isThisYourCard[0], isThisYourCard[1], isHolo);
            return cardHTML;

        }

        updateUserListDisplay() {
            // Clear existing contents
            this.userListContainer.innerHTML = '';

            // Create table
            const table = document.createElement('table');
            table.className = 'table table-hover';

            // Table header
            const thead = document.createElement('thead');
            thead.innerHTML = '<tr><th scope="col">Saved users</th><th scope="col"></th></tr>';
            table.appendChild(thead);

            // Table body
            const tbody = document.createElement('tbody');
            table.appendChild(tbody);

            const usernames = this.savedUsernamesTCG;
            const minimumRows = 0; // Tested leaving blank rows, didn't like.
            const rowsToCreate = Math.max(minimumRows, usernames.length);

            // Everything else (I cba to comment atm)
            for (let i = 0; i < rowsToCreate; i++) {
                const tr = document.createElement('tr');
                const usernameCell = document.createElement('td');

                usernameCell.style.cursor = 'pointer';
                usernameCell.style.verticalAlign = 'middle';
                usernameCell.style.fontSize = '1.2em';
                usernameCell.addEventListener('click', () => {
                    const usernameInput = document.getElementById('recipientUsernameInput');
                    if (usernameInput) {
                        usernameInput.value = usernames[i];
                    }
                });

                const actionCell = document.createElement('td');
                actionCell.align = "right";

                if (i < usernames.length) {
                    usernameCell.textContent = usernames[i];
                    const deleteButton = document.createElement('button');
                    deleteButton.textContent = 'Delete';
                    deleteButton.className = 'btn btn-danger btn-sm';
                    deleteButton.onclick = () => this.deleteUsername(usernames[i]);
                    actionCell.appendChild(deleteButton);
                }

                tr.appendChild(usernameCell);
                tr.appendChild(actionCell);
                tbody.appendChild(tr);
            }

            this.userListContainer.appendChild(table);
        }

        deleteUsername(username) {
            const index = this.savedUsernamesTCG.indexOf(username);
            if (index !== -1) {
                this.savedUsernamesTCG.splice(index, 1);
                this.saveUsernames();
                this.updateUserListDisplay();
            }
        }


        newTradePopup(card) {
            const cardHTML = this.getCardInfoTrade(card).replace(/onclick='[^']+'/g, '');

            // tradePopup CSS
            const tradePopupStyles = `
			<style>
				#tradePopup {
					display: flex;
					flex-direction: column;
					align-items: center;
					width: 100%; /* Full width */
					max-width: 800px; /* Match the paint doodle's width */
					margin: 0 auto; /* Center the popup */
					background-color: #fff; /* White background */
					border-radius: 8px; /* Rounded corners */
					box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); /* Box shadow for some depth */
					padding: 20px; /* Padding around the content */
					box-sizing: border-box; /* Ensures padding is included in width */
				}
				#tradePopup .tradePopup-row {
					display: flex;
					width: 100%;
				}
				#tradePopup .tradePopup-col {
					flex: 1; /* Each column takes up equal space */
					box-sizing: border-box; /* Ensures padding is included in width */
				}
				#tradePopup .tradePopup-card-container {
					flex: none;
					width: 35%;
				}
				#tradePopup .tradePopup-button-container {
					display: flex;
					justify-content: right;
					width: 100%; /* Full width of the popup */
				}
				#tradePopup button {
					padding: 10px 20px; /* Padding inside the buttons */
					cursor: pointer; /* Pointer cursor on hover */
					margin-right: 10px;
				}
                #userListContainer {
					max-height: 230px; /* Adjust to the desired max-height */
					overflow-y: auto; /* Enables scrollbar when content overflows */
				}
			</style>
			`;
            // Add the tradePopup styles to the document head
            document.head.insertAdjacentHTML('beforeend', tradePopupStyles);

            // Element setup
            const popupBox = document.createElement('div');
            popupBox.id = 'tradePopup';

            // Create a row to contain columns
            const rowDiv = document.createElement('div');
            rowDiv.className = 'tradePopup-row';
            popupBox.appendChild(rowDiv);

            // Column for the card
            const cardColDiv = document.createElement('div');
            cardColDiv.className = 'tradePopup-col tradePopup-card-container'; // Bootstrap column class
            rowDiv.appendChild(cardColDiv);

            // Column for the transaction form
            const formColDiv = document.createElement('div');
            formColDiv.className = 'tradePopup-col'; // Bootstrap column class
            rowDiv.appendChild(formColDiv);

            // Card container
            const cardContainer = document.createElement('div');
            cardContainer.innerHTML = cardHTML; // Insert the card HTML
            cardColDiv.appendChild(cardContainer);

            // Modal title
            const title = document.createElement('h5');
            title.textContent = "Who do you want to send this card to?";
            title.className = "modal-title";
            formColDiv.appendChild(title);

            // Input group for username and add user button
            const inputGroup = document.createElement('div');
            inputGroup.className = 'input-group mb-3';

            // Create input for username
            const usernameInput = document.createElement('input');
            usernameInput.type = 'text';
            usernameInput.className = 'form-control';
            usernameInput.id = 'recipientUsernameInput';
            usernameInput.placeholder = 'Enter username';
            if (this.previousTradeUsername){
                usernameInput.value = this.previousTradeUsername;
            }

            // Append input to input group
            inputGroup.appendChild(usernameInput);

            // Add User button next to the input
            const addUserButton = document.createElement('button');
            addUserButton.textContent = 'SAVE USER';
            addUserButton.className = 'btn btn-secondary';
            addUserButton.type = 'button';

            inputGroup.appendChild(addUserButton);
            formColDiv.appendChild(inputGroup);


            // User list container TODO
            this.userListContainer = document.createElement('div');
            this.userListContainer.id = 'userListContainer';
            formColDiv.appendChild(this.userListContainer);
            this.updateUserListDisplay();

            // Buttons
            const sendCardButton = document.createElement('button');
            sendCardButton.textContent = 'SEND CARD';
            sendCardButton.className = 'btn btn-primary';
            sendCardButton.type = 'button';

            const closeButton = document.createElement('button');
            closeButton.textContent = 'CLOSE';
            closeButton.className = 'btn btn-secondary';
            closeButton.type = 'button';

            // Buttons container
            const buttonContainer = document.createElement('div');
            buttonContainer.className = 'tradePopup-button-container';
            buttonContainer.appendChild(sendCardButton);
            buttonContainer.appendChild(closeButton);
            popupBox.appendChild(buttonContainer);

            // Define button actions
            const actions = [
                {
                    button: sendCardButton,
                    handler: () => {
                        const recipientUsername = usernameInput.value.trim();
                        this.previousTradeUsername = recipientUsername;
                        IdlePixelPlus.sendMessage(`GIVE_TCG_CARD=${recipientUsername}~${card}`);
                    }
                },
                {
                    button: closeButton,
                    handler: () => {
                        // Can be left blank
                    },
                    closeOnAction: true
                },
                {
                    button: addUserButton,
                    handler: () => {
                        const username = usernameInput.value;
                        if (username && !this.savedUsernamesTCG.includes(username)) {
                            this.savedUsernamesTCG.push(username);
                            this.saveUsernames();
                            this.updateUserListDisplay();
                        }
                    },
                    closeOnAction: false
                }
            ];


            this.launchPopup(popupBox, actions);
        }

        getCardInfo() {
            const cardData = this.refreshTCG.replace('REFRESH_TCG=', '').split('~');
            let isHolo = 'false';

            if (this.trade) {
                const cardName = this.card.replace(/ \(holo\)$/, '');
                const index = cardData.indexOf(cardName);
                if (index !== -1) {
                    const id = cardData[index - 1];
                    const nameKey = cardData[index];
                    const holo = cardData[index + 1];
                    isHolo = this.card.includes("(holo)") ? 'true' : holo;
                    this.displayNewCard(id, nameKey, isHolo);
                }
            } else {
                // Default to the first card for non trade
                if (cardData.length >= 3) {
                    const id = cardData[0];
                    const nameKey = cardData[1];
                    isHolo = cardData[2];
                    this.displayNewCard(id, nameKey, isHolo);
                }
            }
        }

        displayNewCard(cardId, cardNameKey, holo) {
            const cardName = cardNameKey.replace('tcg_', '').replace(/_/g, ' ').replace(" icon", "");
            const isHolo = holo === 'true';
            let bloodyVowels = "";

            const vowels = ['a', 'e', 'i', 'o', 'u'];
            if (vowels.some(vowel => cardName.toLowerCase().startsWith(vowel))) {
                bloodyVowels = "n";
            }

            const message = isHolo ? `${this.messageStart} holo ${cardName} card!` : `${this.messageStart}${bloodyVowels} ${cardName} card!`;

            const cardHTML = CardData.getCardHTML(cardId, cardNameKey, isHolo);

            this.newCardOverlay(message, cardHTML);

        }

        newCardOverlay(message, cardHTML) {
            // Element setup
            const popupBox = document.createElement('div');
            popupBox.id = 'newCardPopupBox';
            popupBox.style.width = '300px';
            popupBox.style.margin = '0 auto';
            popupBox.style.backgroundColor = '#fff';
            popupBox.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
            popupBox.style.borderRadius = '8px';
            popupBox.style.padding = '20px';
            popupBox.style.textAlign = 'center';

            const messageP = document.createElement('p');
            messageP.textContent = message;
            messageP.style.fontSize = '18px';
            messageP.style.fontWeight = 'bold';

            const cardContainer = document.createElement('div');
            cardContainer.innerHTML = cardHTML;
            cardContainer.firstChild.style.marginTop = '0px';

            const cardTitle = cardContainer.querySelector('.tcg-card-title');
            const cardInnerText = cardContainer.querySelector('.tcg-card-inner-text');
            if (cardTitle) {
                cardTitle.style.textAlign = 'left';
            }
            if (cardInnerText) {
                cardInnerText.style.textAlign = 'left';
            }

            const openAnotherButton = document.createElement('button');
            openAnotherButton.textContent = 'OPEN ANOTHER';
            openAnotherButton.style.padding = '10px 20px';
            openAnotherButton.style.fontSize = '16px';
            openAnotherButton.style.cursor = 'pointer';
            openAnotherButton.style.marginRight = '10px';

            const tcg_unknown = IdlePixelPlus.getVarOrDefault("tcg_unknown", 0, "int");
            openAnotherButton.disabled = tcg_unknown == 1; // was but never fully tested to understand wtf was going on (var might not update quickly enough, would explain why I went with 1?) <=1

            const closeButton = document.createElement('button');
            closeButton.textContent = 'CLOSE';
            closeButton.style.padding = '10px 20px';
            closeButton.style.fontSize = '16px';
            closeButton.style.cursor = 'pointer';

            // Define button actions
            const actions = [
                {
                    button: openAnotherButton,
                    handler: () => {
                        IdlePixelPlus.sendMessage("REVEAL_TCG_CARD");
                    }
                },
                {
                    button: closeButton,
                    handler: () => {
                    },
                    closeOnAction: true // True by default but stated for future reference
                }
            ];

            // Append elements to main popup
            popupBox.appendChild(messageP);
            popupBox.appendChild(cardContainer);
            if (!this.trade) {
                popupBox.appendChild(openAnotherButton);
            }
            popupBox.appendChild(closeButton);
            this.trade = false;

            this.launchPopup(popupBox, actions);
        }

        launchPopup(popup, actions) {

            if (this.currentPopup) {
                this.overlay.removeChild(this.currentPopup);
                this.currentPopup = null;
            }

            this.currentPopup = popup;

            this.overlay.appendChild(popup);
            document.body.appendChild(this.overlay);

            const adjustPopupPosition = () => {
                const viewportHeight = window.innerHeight;
                const popupHeight = popup.offsetHeight;
                const scrollOffset = window.pageYOffset || document.documentElement.scrollTop;
                const topPosition = (viewportHeight - popupHeight) / 2 + scrollOffset;
                popup.style.position = 'absolute';
                popup.style.top = `${topPosition > 0 ? topPosition : 0}px`;
            };

            adjustPopupPosition();
            // window.addEventListener('scroll', adjustPopupPosition);
            window.addEventListener('resize', adjustPopupPosition);


            // Apply actions (button clicks)
            actions.forEach(action => {
                const button = action.button;
                button.addEventListener('click', () => {
                    action.handler();
                    // Only remove the overlay if specified by the action
                    if (action.closeOnAction !== false) {
                        document.body.removeChild(this.overlay);
                        window.removeEventListener('resize', adjustPopupPosition);
                    }
                });
            });

            this.overlay.addEventListener('click', (event) => {
                if (event.target === this.overlay) {
                    document.body.removeChild(this.overlay);
                    window.removeEventListener('resize', adjustPopupPosition);
                }
            });

            popup.addEventListener('click', (event) => {
                event.stopPropagation();
            });

            this.oldpopup = popup.id;
        }

    }

    const plugin = new TCGRevamp();
    IdlePixelPlus.registerPlugin(plugin);

})();