Greasy Fork 支持简体中文。

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