// ==UserScript==
// @name IdlePixel+ New Card Interface
// @namespace lbtechnology.info
// @version 1.2.3
// @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.inCombat = false;
this.inCombatTrade = false;
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';
this.overlay.addEventListener('click', (event) => {
if (event.target === this.overlay) {
this.closePopup();
}
});
window.addEventListener('resize', this.adjustPopupPosition.bind(this));
}
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")){
console.log("Open dialogue message received");
Modals.open_image_modal = function(title, imgUrl, description, footerText, closeBtnText, anotherBtnText, isModalDismissible) {
if (description.includes("You were given a card from")) {
console.log("Opening custom dialogue");
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 {
console.log("Opening original dialogue");
originalOpenImageModal.call(this, title, imgUrl, description, footerText, closeBtnText, anotherBtnText, isModalDismissible);
}
};
}
if (data.includes("REFRESH_TCG")){
this.refreshTCG = data;
// console.log("In REFRESH_TCG, checking for inCombat");
if (this.trade && this.inCombat){
this.showPopup = false;
this.inCombatTrade = true;
// console.log("inCombatTrade set to True");
}
if (this.showPopup){
if (this.trade){
this.getCardInfo();
this.trade = false;
this.card = "";
} else {
this.getCardInfo();
}
this.showPopup = false;
this.messageStart = "You got a";
}
}
if (data.includes('START_RAID')){
this.inCombat = true;
// console.log("In raid");
}
if (data.includes('RAIDS_TEAM_INFO_DESTROYED') || data.includes('RAIDS REWARD')){
// console.log("Exited raid");
this.inCombat = false;
if (this.inCombatTrade){
// console.log("inCombatTrade is true, opening modal");
this.inCombatTrade = false;
this.openSimplePopup("Trading Card Game", "You received card(s) during in combat!");
}
}
}
onCombatStart(){
this.inCombat = true;
}
onCombatEnd(){
this.inCombat = false;
if (this.inCombatTrade){
this.inCombatTrade = false;
this.openSimplePopup("Trading Card Game", "You received card(s) during in combat!");
}
}
getCardInfoTrade(cardid) {
const cardData = this.refreshTCG.replace('REFRESH_TCG=', '').split('~');
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;
}
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 {
if (cardData.length >= 3) {
const id = cardData[0];
const nameKey = cardData[1];
isHolo = cardData[2];
this.displayNewCard(id, nameKey, isHolo);
}
}
}
getCardInfoUnified(cardPart, identifier) {
const cardData = this.refreshTCG.replace('REFRESH_TCG=', '').split('~');
const index = cardData.indexOf(identifier);
let id, nameKey, holo, isHolo;
if (identifier === 'card_id') {
id = cardPart;
nameKey = cardData[index + 1];
holo = cardData[index + 2];
} else if (identifier === 'card_key') {
id = cardData[index - 1];
nameKey = cardPart;
holo = cardData[index + 1];
if (this.isTrade) {
nameKey = this.refactorCardKey(nameKey);
holo = this.card.includes("(holo)") ? 'true' : holo;
}
} else {
console.error('Invalid card target type');
return;
}
isHolo = (holo === 'true');
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);
}
updateUserListDisplay() {
this.userListContainer.innerHTML = '';
const table = document.createElement('table');
table.className = 'table table-hover';
const thead = document.createElement('thead');
thead.innerHTML = '<tr><th scope="col">Saved users</th><th scope="col"></th></tr>';
table.appendChild(thead);
const tbody = document.createElement('tbody');
table.appendChild(tbody);
const usernames = this.savedUsernamesTCG;
const minimumRows = 0;
const rowsToCreate = Math.max(minimumRows, usernames.length);
for (let i = 0; rowsToCreate > i; 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, '');
const tradePopupStyles = `
<style>
#tradePopup {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
max-width: 800px;
margin: 0 auto;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
padding: 20px;
box-sizing: border-box;
}
#tradePopup .tradePopup-row {
display: flex;
width: 100%;
}
#tradePopup .tradePopup-col {
flex: 1;
box-sizing: border-box;
}
#tradePopup .tradePopup-card-container {
flex: none;
width: 35%;
}
#tradePopup .tradePopup-button-container {
display: flex;
justify-content: right;
width: 100%;
}
#tradePopup button {
padding: 10px 20px;
cursor: pointer;
margin-right: 10px;
}
#userListContainer {
max-height: 230px;
overflow-y: auto;
}
</style>
`;
document.head.insertAdjacentHTML('beforeend', tradePopupStyles);
const popupBox = document.createElement('div');
popupBox.id = 'tradePopup';
const rowDiv = document.createElement('div');
rowDiv.className = 'tradePopup-row';
popupBox.appendChild(rowDiv);
const cardColDiv = document.createElement('div');
cardColDiv.className = 'tradePopup-col tradePopup-card-container';
rowDiv.appendChild(cardColDiv);
const formColDiv = document.createElement('div');
formColDiv.className = 'tradePopup-col';
rowDiv.appendChild(formColDiv);
const cardContainer = document.createElement('div');
cardContainer.innerHTML = cardHTML;
cardColDiv.appendChild(cardContainer);
const title = document.createElement('h5');
title.textContent = "Who do you want to send this card to?";
title.className = "modal-title";
formColDiv.appendChild(title);
const inputGroup = document.createElement('div');
inputGroup.className = 'input-group mb-3';
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;
}
inputGroup.appendChild(usernameInput);
const addUserButton = document.createElement('button');
addUserButton.textContent = 'SAVE USER';
addUserButton.className = 'btn btn-secondary';
addUserButton.type = 'button';
inputGroup.appendChild(addUserButton);
formColDiv.appendChild(inputGroup);
this.userListContainer = document.createElement('div');
this.userListContainer.id = 'userListContainer';
formColDiv.appendChild(this.userListContainer);
this.updateUserListDisplay();
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';
const buttonContainer = document.createElement('div');
buttonContainer.className = 'tradePopup-button-container';
buttonContainer.appendChild(sendCardButton);
buttonContainer.appendChild(closeButton);
popupBox.appendChild(buttonContainer);
const actions = [
{
button: sendCardButton,
handler: () => {
const recipientUsername = usernameInput.value.trim();
this.previousTradeUsername = recipientUsername;
IdlePixelPlus.sendMessage(`GIVE_TCG_CARD=${recipientUsername}~${card}`);
}
},
{
button: closeButton,
handler: () => {
this.closePopup();
},
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);
}
newCardOverlay(message, cardHTML) {
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;
const closeButton = document.createElement('button');
closeButton.textContent = 'CLOSE';
closeButton.style.padding = '10px 20px';
closeButton.style.fontSize = '16px';
closeButton.style.cursor = 'pointer';
const actions = [
{
button: openAnotherButton,
handler: () => {
IdlePixelPlus.sendMessage("REVEAL_TCG_CARD");
}
},
{
button: closeButton,
handler: () => {
this.closePopup();
},
closeOnAction: true
}
];
popupBox.appendChild(messageP);
popupBox.appendChild(cardContainer);
if (!this.trade) {
popupBox.appendChild(openAnotherButton);
}
popupBox.appendChild(closeButton);
this.trade = false;
this.launchPopup(popupBox, actions);
}
openSimplePopup(message, footer) {
const popupBox = document.createElement('div');
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 footerP = document.createElement('p');
footerP.textContent = footer;
footerP.style.fontSize = '16px';
const closeButton = document.createElement('button');
closeButton.textContent = 'CLOSE';
closeButton.style.padding = '10px 20px';
closeButton.style.fontSize = '16px';
closeButton.style.cursor = 'pointer';
closeButton.addEventListener('click', () => {
this.closePopup();
});
popupBox.appendChild(messageP);
popupBox.appendChild(footerP);
popupBox.appendChild(closeButton);
this.launchPopup(popupBox, []);
}
launchPopup(popup, actions) {
if (this.currentPopup) {
if (this.overlay.contains(this.currentPopup)) {
this.overlay.removeChild(this.currentPopup);
}
this.currentPopup = null;
}
this.currentPopup = popup;
this.overlay.appendChild(popup);
document.body.appendChild(this.overlay);
this.adjustPopupPosition();
actions.forEach(action => {
const button = action.button;
button.addEventListener('click', () => {
action.handler();
if (action.closeOnAction !== false) {
this.closePopup();
}
});
});
}
adjustPopupPosition() {
if (!this.currentPopup) return;
const viewportHeight = window.innerHeight;
const popupHeight = this.currentPopup.offsetHeight;
const scrollOffset = window.pageYOffset || document.documentElement.scrollTop;
const topPosition = (viewportHeight - popupHeight) / 2 + scrollOffset;
this.currentPopup.style.position = 'absolute';
this.currentPopup.style.top = `${topPosition > 0 ? topPosition : 0}px`;
}
closePopup() {
if (this.overlay.contains(this.currentPopup)) {
this.overlay.removeChild(this.currentPopup);
}
document.body.removeChild(this.overlay);
this.currentPopup = null;
}
}
const plugin = new TCGRevamp();
IdlePixelPlus.registerPlugin(plugin);
})();