IdlePixel+ New Card Interface

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

目前为 2024-04-19 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name IdlePixel+ New Card Interface
  3. // @namespace lbtechnology.info
  4. // @version 1.2.1
  5. // @description Improved interface for opening new cards, receiving cards in trade & trading cards
  6. // @author Zlef
  7. // @license MIT
  8. // @match *://idle-pixel.com/login/play*
  9. // @grant none
  10. // @icon https://d1xsc8x7nc5q8t.cloudfront.net/images/tcg_back_50.png
  11. // @require https://greasyfork.org/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16.  
  17. class TCGRevamp extends IdlePixelPlusPlugin {
  18. constructor() {
  19. super("TCG Interface Changes", {
  20. about: {
  21. name: GM_info.script.name,
  22. version: GM_info.script.version,
  23. author: GM_info.script.author,
  24. description: GM_info.script.description
  25. }
  26. });
  27.  
  28. this.showPopup = false;
  29. this.messageStart = "You got a"
  30. this.trade = false;
  31. this.card = "";
  32. this.refreshTCG = "";
  33. this.currentPopup = null;
  34. this.savedUsernamesTCG = null;
  35. this.previousTradeUsername = null;
  36. this.loadUsernames();
  37.  
  38.  
  39. this.overlay = document.createElement('div');
  40. this.overlay.id = 'newCardOverlay';
  41. this.overlay.style.position = 'fixed';
  42. this.overlay.style.top = '0';
  43. this.overlay.style.left = '0';
  44. this.overlay.style.width = '100%';
  45. this.overlay.style.height = '100%';
  46. this.overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  47. this.overlay.style.zIndex = '1000';
  48. this.overlay.style.display = 'flex';
  49. this.overlay.style.justifyContent = 'center';
  50. this.overlay.style.alignItems = 'center';
  51. }
  52.  
  53. onLogin() {
  54. this.originalTcgGiveCard = Modals.open_loot_dialogue;
  55. Modals.open_tcg_give_card = (modal_id, card) => {
  56. this.newTradePopup(card);
  57. }
  58.  
  59. if (!CardData.data) {
  60. CardData.fetchData();
  61. }
  62. this.monitorRevealTCG();
  63. }
  64.  
  65. saveUsernames(){
  66. const saveData = JSON.stringify({usernames: this.savedUsernamesTCG});
  67. localStorage.setItem(`savedUsernamesTCG`, saveData)
  68.  
  69. }
  70.  
  71. loadUsernames(){
  72. const savedUsernamesTCG = localStorage.getItem(`savedUsernamesTCG`)
  73.  
  74. if (savedUsernamesTCG){
  75. this.savedUsernamesTCG = JSON.parse(savedUsernamesTCG).usernames
  76. } else {
  77. this.savedUsernamesTCG = [];
  78. }
  79.  
  80. }
  81.  
  82. monitorRevealTCG() {
  83. const originalWebSocketSend = WebSocket.prototype.send;
  84. const self = this;
  85. WebSocket.prototype.send = function(data) {
  86. try {
  87. originalWebSocketSend.call(this, data);
  88. if (data === 'REVEAL_TCG_CARD') {
  89. self.showPopup = true;
  90. }
  91. } catch (error) {
  92. console.error('Error in overridden WebSocket send:', error);
  93. }
  94. };
  95. }
  96.  
  97. onMessageReceived(data){
  98. const originalOpenImageModal = Modals.open_image_modal;
  99. const self = this;
  100.  
  101. if (data.includes("OPEN_DIALOGUE")){
  102. Modals.open_image_modal = function(title, imgUrl, description, footerText, closeBtnText, anotherBtnText, isModalDismissible) {
  103.  
  104. if (description.includes("You were given a card from")) {
  105.  
  106. const usernameRegex = /You were given a card from (.*?)<br \/>/;
  107. const cardRegex = /<span class='color-grey'>(.*?)<\/span>/;
  108.  
  109. const usernameMatch = description.match(usernameRegex);
  110. const cardMatch = description.match(cardRegex);
  111.  
  112. let username = "";
  113. let card = "";
  114.  
  115. if (usernameMatch && usernameMatch.length > 1) {
  116. username = usernameMatch[1];
  117. }
  118.  
  119. if (cardMatch && cardMatch.length > 1) {
  120. self.card = cardMatch[1]
  121. }
  122.  
  123.  
  124. self.messageStart = `${username} sent you a`
  125. self.trade = true;
  126. self.showPopup = true;
  127. } else {
  128. originalOpenImageModal.call(this, title, imgUrl, description, footerText, closeBtnText, anotherBtnText, isModalDismissible);
  129. }
  130. }
  131. }
  132.  
  133. if (data.includes("REFRESH_TCG")){
  134. this.refreshTCG = data;
  135. if (this.showPopup){
  136. if (this.trade){
  137. this.getCardInfo();
  138. // this.trade = false;
  139. this.card = "";
  140. } else {
  141. this.getCardInfo();
  142. }
  143.  
  144. this.showPopup = false;
  145. this.messageStart = "You got a "
  146. }
  147. }
  148. }
  149.  
  150. getCardInfoTrade(cardid) {
  151. const cardData = this.refreshTCG.replace('REFRESH_TCG=', '').split('~');
  152. // cardData looks like this: [ "21882", "tcg_thief_icon", "false", "21881", "tcg_raw_swordfish", "false", "21880", "tcg_raw_tuna", "false", "21805", … ]
  153. const index = cardData.indexOf(cardid);
  154. const isThisYourCard = [cardData[index], cardData[index + 1], cardData[index + 2]];
  155.  
  156. const isHolo = isThisYourCard[2] === 'true';
  157. const cardHTML = CardData.getCardHTML(isThisYourCard[0], isThisYourCard[1], isHolo);
  158. return cardHTML;
  159.  
  160. }
  161.  
  162. updateUserListDisplay() {
  163. // Clear existing contents
  164. this.userListContainer.innerHTML = '';
  165.  
  166. // Create table
  167. const table = document.createElement('table');
  168. table.className = 'table table-hover';
  169.  
  170. // Table header
  171. const thead = document.createElement('thead');
  172. thead.innerHTML = '<tr><th scope="col">Saved users</th><th scope="col"></th></tr>';
  173. table.appendChild(thead);
  174.  
  175. // Table body
  176. const tbody = document.createElement('tbody');
  177. table.appendChild(tbody);
  178.  
  179. const usernames = this.savedUsernamesTCG;
  180. const minimumRows = 0; // Tested leaving blank rows, didn't like.
  181. const rowsToCreate = Math.max(minimumRows, usernames.length);
  182.  
  183. // Everything else (I cba to comment atm)
  184. for (let i = 0; i < rowsToCreate; i++) {
  185. const tr = document.createElement('tr');
  186. const usernameCell = document.createElement('td');
  187.  
  188. usernameCell.style.cursor = 'pointer';
  189. usernameCell.style.verticalAlign = 'middle';
  190. usernameCell.style.fontSize = '1.2em';
  191. usernameCell.addEventListener('click', () => {
  192. const usernameInput = document.getElementById('recipientUsernameInput');
  193. if (usernameInput) {
  194. usernameInput.value = usernames[i];
  195. }
  196. });
  197.  
  198. const actionCell = document.createElement('td');
  199. actionCell.align = "right";
  200.  
  201. if (i < usernames.length) {
  202. usernameCell.textContent = usernames[i];
  203. const deleteButton = document.createElement('button');
  204. deleteButton.textContent = 'Delete';
  205. deleteButton.className = 'btn btn-danger btn-sm';
  206. deleteButton.onclick = () => this.deleteUsername(usernames[i]);
  207. actionCell.appendChild(deleteButton);
  208. }
  209.  
  210. tr.appendChild(usernameCell);
  211. tr.appendChild(actionCell);
  212. tbody.appendChild(tr);
  213. }
  214.  
  215. this.userListContainer.appendChild(table);
  216. }
  217.  
  218. deleteUsername(username) {
  219. const index = this.savedUsernamesTCG.indexOf(username);
  220. if (index !== -1) {
  221. this.savedUsernamesTCG.splice(index, 1);
  222. this.saveUsernames();
  223. this.updateUserListDisplay();
  224. }
  225. }
  226.  
  227.  
  228. newTradePopup(card) {
  229. const cardHTML = this.getCardInfoTrade(card).replace(/onclick='[^']+'/g, '');
  230.  
  231. // tradePopup CSS
  232. const tradePopupStyles = `
  233. <style>
  234. #tradePopup {
  235. display: flex;
  236. flex-direction: column;
  237. align-items: center;
  238. width: 100%; /* Full width */
  239. max-width: 800px; /* Match the paint doodle's width */
  240. margin: 0 auto; /* Center the popup */
  241. background-color: #fff; /* White background */
  242. border-radius: 8px; /* Rounded corners */
  243. box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); /* Box shadow for some depth */
  244. padding: 20px; /* Padding around the content */
  245. box-sizing: border-box; /* Ensures padding is included in width */
  246. }
  247. #tradePopup .tradePopup-row {
  248. display: flex;
  249. width: 100%;
  250. }
  251. #tradePopup .tradePopup-col {
  252. flex: 1; /* Each column takes up equal space */
  253. box-sizing: border-box; /* Ensures padding is included in width */
  254. }
  255. #tradePopup .tradePopup-card-container {
  256. flex: none;
  257. width: 35%;
  258. }
  259. #tradePopup .tradePopup-button-container {
  260. display: flex;
  261. justify-content: right;
  262. width: 100%; /* Full width of the popup */
  263. }
  264. #tradePopup button {
  265. padding: 10px 20px; /* Padding inside the buttons */
  266. cursor: pointer; /* Pointer cursor on hover */
  267. margin-right: 10px;
  268. }
  269. #userListContainer {
  270. max-height: 230px; /* Adjust to the desired max-height */
  271. overflow-y: auto; /* Enables scrollbar when content overflows */
  272. }
  273. </style>
  274. `;
  275. // Add the tradePopup styles to the document head
  276. document.head.insertAdjacentHTML('beforeend', tradePopupStyles);
  277.  
  278. // Element setup
  279. const popupBox = document.createElement('div');
  280. popupBox.id = 'tradePopup';
  281.  
  282. // Create a row to contain columns
  283. const rowDiv = document.createElement('div');
  284. rowDiv.className = 'tradePopup-row';
  285. popupBox.appendChild(rowDiv);
  286.  
  287. // Column for the card
  288. const cardColDiv = document.createElement('div');
  289. cardColDiv.className = 'tradePopup-col tradePopup-card-container'; // Bootstrap column class
  290. rowDiv.appendChild(cardColDiv);
  291.  
  292. // Column for the transaction form
  293. const formColDiv = document.createElement('div');
  294. formColDiv.className = 'tradePopup-col'; // Bootstrap column class
  295. rowDiv.appendChild(formColDiv);
  296.  
  297. // Card container
  298. const cardContainer = document.createElement('div');
  299. cardContainer.innerHTML = cardHTML; // Insert the card HTML
  300. cardColDiv.appendChild(cardContainer);
  301.  
  302. // Modal title
  303. const title = document.createElement('h5');
  304. title.textContent = "Who do you want to send this card to?";
  305. title.className = "modal-title";
  306. formColDiv.appendChild(title);
  307.  
  308. // Input group for username and add user button
  309. const inputGroup = document.createElement('div');
  310. inputGroup.className = 'input-group mb-3';
  311.  
  312. // Create input for username
  313. const usernameInput = document.createElement('input');
  314. usernameInput.type = 'text';
  315. usernameInput.className = 'form-control';
  316. usernameInput.id = 'recipientUsernameInput';
  317. usernameInput.placeholder = 'Enter username';
  318. if (this.previousTradeUsername){
  319. usernameInput.value = this.previousTradeUsername;
  320. }
  321.  
  322. // Append input to input group
  323. inputGroup.appendChild(usernameInput);
  324.  
  325. // Add User button next to the input
  326. const addUserButton = document.createElement('button');
  327. addUserButton.textContent = 'SAVE USER';
  328. addUserButton.className = 'btn btn-secondary';
  329. addUserButton.type = 'button';
  330.  
  331. inputGroup.appendChild(addUserButton);
  332. formColDiv.appendChild(inputGroup);
  333.  
  334.  
  335. // User list container TODO
  336. this.userListContainer = document.createElement('div');
  337. this.userListContainer.id = 'userListContainer';
  338. formColDiv.appendChild(this.userListContainer);
  339. this.updateUserListDisplay();
  340.  
  341. // Buttons
  342. const sendCardButton = document.createElement('button');
  343. sendCardButton.textContent = 'SEND CARD';
  344. sendCardButton.className = 'btn btn-primary';
  345. sendCardButton.type = 'button';
  346.  
  347. const closeButton = document.createElement('button');
  348. closeButton.textContent = 'CLOSE';
  349. closeButton.className = 'btn btn-secondary';
  350. closeButton.type = 'button';
  351.  
  352. // Buttons container
  353. const buttonContainer = document.createElement('div');
  354. buttonContainer.className = 'tradePopup-button-container';
  355. buttonContainer.appendChild(sendCardButton);
  356. buttonContainer.appendChild(closeButton);
  357. popupBox.appendChild(buttonContainer);
  358.  
  359. // Define button actions
  360. const actions = [
  361. {
  362. button: sendCardButton,
  363. handler: () => {
  364. const recipientUsername = usernameInput.value.trim();
  365. this.previousTradeUsername = recipientUsername;
  366. IdlePixelPlus.sendMessage(`GIVE_TCG_CARD=${recipientUsername}~${card}`);
  367. }
  368. },
  369. {
  370. button: closeButton,
  371. handler: () => {
  372. // Can be left blank
  373. },
  374. closeOnAction: true
  375. },
  376. {
  377. button: addUserButton,
  378. handler: () => {
  379. const username = usernameInput.value;
  380. if (username && !this.savedUsernamesTCG.includes(username)) {
  381. this.savedUsernamesTCG.push(username);
  382. this.saveUsernames();
  383. this.updateUserListDisplay();
  384. }
  385. },
  386. closeOnAction: false
  387. }
  388. ];
  389.  
  390.  
  391. this.launchPopup(popupBox, actions);
  392. }
  393.  
  394. getCardInfo() {
  395. const cardData = this.refreshTCG.replace('REFRESH_TCG=', '').split('~');
  396. let isHolo = 'false';
  397.  
  398. if (this.trade) {
  399. const cardName = this.card.replace(/ \(holo\)$/, '');
  400. const index = cardData.indexOf(cardName);
  401. if (index !== -1) {
  402. const id = cardData[index - 1];
  403. const nameKey = cardData[index];
  404. const holo = cardData[index + 1];
  405. isHolo = this.card.includes("(holo)") ? 'true' : holo;
  406. this.displayNewCard(id, nameKey, isHolo);
  407. }
  408. } else {
  409. // Default to the first card for non trade
  410. if (cardData.length >= 3) {
  411. const id = cardData[0];
  412. const nameKey = cardData[1];
  413. isHolo = cardData[2];
  414. this.displayNewCard(id, nameKey, isHolo);
  415. }
  416. }
  417. }
  418.  
  419. displayNewCard(cardId, cardNameKey, holo) {
  420. const cardName = cardNameKey.replace('tcg_', '').replace(/_/g, ' ').replace(" icon", "");
  421. const isHolo = holo === 'true';
  422. let bloodyVowels = "";
  423.  
  424. const vowels = ['a', 'e', 'i', 'o', 'u'];
  425. if (vowels.some(vowel => cardName.toLowerCase().startsWith(vowel))) {
  426. bloodyVowels = "n";
  427. }
  428.  
  429. const message = isHolo ? `${this.messageStart} holo ${cardName} card!` : `${this.messageStart}${bloodyVowels} ${cardName} card!`;
  430.  
  431. const cardHTML = CardData.getCardHTML(cardId, cardNameKey, isHolo);
  432.  
  433. this.newCardOverlay(message, cardHTML);
  434.  
  435. }
  436.  
  437. newCardOverlay(message, cardHTML) {
  438. // Element setup
  439. const popupBox = document.createElement('div');
  440. popupBox.id = 'newCardPopupBox';
  441. popupBox.style.width = '300px';
  442. popupBox.style.margin = '0 auto';
  443. popupBox.style.backgroundColor = '#fff';
  444. popupBox.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.5)';
  445. popupBox.style.borderRadius = '8px';
  446. popupBox.style.padding = '20px';
  447. popupBox.style.textAlign = 'center';
  448.  
  449. const messageP = document.createElement('p');
  450. messageP.textContent = message;
  451. messageP.style.fontSize = '18px';
  452. messageP.style.fontWeight = 'bold';
  453.  
  454. const cardContainer = document.createElement('div');
  455. cardContainer.innerHTML = cardHTML;
  456. cardContainer.firstChild.style.marginTop = '0px';
  457.  
  458. const cardTitle = cardContainer.querySelector('.tcg-card-title');
  459. const cardInnerText = cardContainer.querySelector('.tcg-card-inner-text');
  460. if (cardTitle) {
  461. cardTitle.style.textAlign = 'left';
  462. }
  463. if (cardInnerText) {
  464. cardInnerText.style.textAlign = 'left';
  465. }
  466.  
  467. const openAnotherButton = document.createElement('button');
  468. openAnotherButton.textContent = 'OPEN ANOTHER';
  469. openAnotherButton.style.padding = '10px 20px';
  470. openAnotherButton.style.fontSize = '16px';
  471. openAnotherButton.style.cursor = 'pointer';
  472. openAnotherButton.style.marginRight = '10px';
  473.  
  474. const tcg_unknown = IdlePixelPlus.getVarOrDefault("tcg_unknown", 0, "int");
  475. 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
  476.  
  477. const closeButton = document.createElement('button');
  478. closeButton.textContent = 'CLOSE';
  479. closeButton.style.padding = '10px 20px';
  480. closeButton.style.fontSize = '16px';
  481. closeButton.style.cursor = 'pointer';
  482.  
  483. // Define button actions
  484. const actions = [
  485. {
  486. button: openAnotherButton,
  487. handler: () => {
  488. IdlePixelPlus.sendMessage("REVEAL_TCG_CARD");
  489. }
  490. },
  491. {
  492. button: closeButton,
  493. handler: () => {
  494. },
  495. closeOnAction: true // True by default but stated for future reference
  496. }
  497. ];
  498.  
  499. // Append elements to main popup
  500. popupBox.appendChild(messageP);
  501. popupBox.appendChild(cardContainer);
  502. if (!this.trade) {
  503. popupBox.appendChild(openAnotherButton);
  504. }
  505. popupBox.appendChild(closeButton);
  506. this.trade = false;
  507.  
  508. this.launchPopup(popupBox, actions);
  509. }
  510.  
  511. launchPopup(popup, actions) {
  512.  
  513. if (this.currentPopup) {
  514. this.overlay.removeChild(this.currentPopup);
  515. this.currentPopup = null;
  516. }
  517.  
  518. this.currentPopup = popup;
  519.  
  520. this.overlay.appendChild(popup);
  521. document.body.appendChild(this.overlay);
  522.  
  523. const adjustPopupPosition = () => {
  524. const viewportHeight = window.innerHeight;
  525. const popupHeight = popup.offsetHeight;
  526. const scrollOffset = window.pageYOffset || document.documentElement.scrollTop;
  527. const topPosition = (viewportHeight - popupHeight) / 2 + scrollOffset;
  528. popup.style.position = 'absolute';
  529. popup.style.top = `${topPosition > 0 ? topPosition : 0}px`;
  530. };
  531.  
  532. adjustPopupPosition();
  533. // window.addEventListener('scroll', adjustPopupPosition);
  534. window.addEventListener('resize', adjustPopupPosition);
  535.  
  536.  
  537. // Apply actions (button clicks)
  538. actions.forEach(action => {
  539. const button = action.button;
  540. button.addEventListener('click', () => {
  541. action.handler();
  542. // Only remove the overlay if specified by the action
  543. if (action.closeOnAction !== false) {
  544. document.body.removeChild(this.overlay);
  545. window.removeEventListener('resize', adjustPopupPosition);
  546. }
  547. });
  548. });
  549.  
  550. this.overlay.addEventListener('click', (event) => {
  551. if (event.target === this.overlay) {
  552. document.body.removeChild(this.overlay);
  553. window.removeEventListener('resize', adjustPopupPosition);
  554. }
  555. });
  556.  
  557. popup.addEventListener('click', (event) => {
  558. event.stopPropagation();
  559. });
  560.  
  561. this.oldpopup = popup.id;
  562. }
  563.  
  564. }
  565.  
  566. const plugin = new TCGRevamp();
  567. IdlePixelPlus.registerPlugin(plugin);
  568.  
  569. })();