// ==UserScript==
// @name IdlePixel+ New Card Interface
// @namespace lbtechnology.info
// @version 1.2.2
// @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_tcg_give_card;
Modals.open_tcg_give_card = (modal_id, card) => {
if (!modal_id) {
this.newTradePopup(card);
} else {
this.originalTcgGiveCard(modal_id, 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 = [];
}
}
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"
}
}
}
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";
actionCell.style.width = '80px';
if (i < usernames.length) {
usernameCell.textContent = usernames[i];
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete';
deleteButton.className = 'btn btn-danger btn-sm';
deleteButton.style.padding = '5px 10px';
deleteButton.style.height = 'auto';
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);
})();