IdlePixel TCG Dex (Lux Fork)

Organizational script for the Criptoe Trading Card Game

目前為 2025-02-12 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name IdlePixel TCG Dex (Lux Fork)
  3. // @namespace luxferre.dev
  4. // @version 1.0.3
  5. // @description Organizational script for the Criptoe Trading Card Game
  6. // @author GodofNades & Lux-Ferre
  7. // @match *://idle-pixel.com/login/play*
  8. // @grant none
  9. // @license MIT
  10. // @require https://gf.qytechs.cn/scripts/441206-idlepixel/code/IdlePixel+.js?anticache=20220905
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. "use strict";
  15.  
  16. let playername = "";
  17.  
  18. class tcgDex extends IdlePixelPlusPlugin {
  19. constructor() {
  20. super("tcgDex", {
  21. about: {
  22. name: GM_info.script.name + " (ver: " + GM_info.script.version + ")",
  23. version: GM_info.script.version,
  24. author: GM_info.script.author,
  25. description: GM_info.script.description,
  26. },
  27. config: [
  28. {
  29. label:
  30. "------------------------------------------------<br/>Notification<br/>------------------------------------------------",
  31. type: "label"
  32. },
  33. {
  34. id: "tcgNotification",
  35. label:
  36. "Enable TCG Card Buying Available Notification<br/>(Default: Enabled)",
  37. type: "boolean",
  38. default: true
  39. },
  40. {
  41. id: "newCardTimer",
  42. label:
  43. "New Card Timer<br/>(How long do you want a card to show as new, in minutes.)",
  44. type: "int",
  45. default: 15
  46. },
  47. {
  48. id: "enableSend",
  49. label: "Enable auto send of duplicate cards to the player in the next option.",
  50. type: "boolean",
  51. default: false
  52. },
  53. {
  54. id: "sendTo",
  55. label: "Player to send duplicate cards to automatically.",
  56. type: "string",
  57. default: null
  58. }
  59. ]
  60. })
  61. this.login_loaded = false
  62. this.dupe_sending = false;
  63. this.newest_card_ids = new Map()
  64. this.categoriesTCG = []
  65. this.row_bg_colours = {
  66. "ORE": "#734d26",
  67. "BAR": "#3d3d29",
  68. "SEED": "#1a3300",
  69. "WOOD": "#663300",
  70. "LEAF": "#669900",
  71. "GEM": "#990099",
  72. "FISH": "#3333cc",
  73. "MONSTER": "#000000",
  74. "GEAR": "#800000",
  75. "LEGENDARY": "#ffffff",
  76. "BREEDING": "#ffb31a",
  77. "LIMITED": "#ffffe6",
  78. }
  79. this.row_text_colours = {
  80. "ORE": "white",
  81. "BAR": "white",
  82. "SEED": "white",
  83. "WOOD": "white",
  84. "LEAF": "white",
  85. "GEM": "white",
  86. "FISH": "white",
  87. "MONSTER": "white",
  88. "GEAR": "white",
  89. "LEGENDARY": "black",
  90. "BREEDING": "black",
  91. "LIMITED": "black",
  92. }
  93.  
  94. this.load_fa()
  95. }
  96.  
  97. load_fa(){
  98. const fontAwesomeLink = document.createElement('link')
  99. fontAwesomeLink.rel = 'stylesheet'
  100. fontAwesomeLink.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css'
  101. document.head.appendChild(fontAwesomeLink)
  102. }
  103.  
  104. getCategoryData() {
  105. let uniqueDescriptionTitles = [];
  106. const descriptionTitlesSet = new Set();
  107. let i = 1;
  108.  
  109. Object.values(CardData.data).forEach((card) => {
  110. const descriptionTitle = card["description_title"];
  111. const properTitles =
  112. descriptionTitle.charAt(0).toUpperCase() +
  113. descriptionTitle.slice(1).toLowerCase();
  114.  
  115. if (!descriptionTitlesSet.has(descriptionTitle)) {
  116. descriptionTitlesSet.add(descriptionTitle);
  117.  
  118. uniqueDescriptionTitles.push({
  119. id: `[${descriptionTitle}]`,
  120. desc: descriptionTitle,
  121. label: `${properTitles}`,
  122. });
  123. i++;
  124. }
  125. })
  126.  
  127. // Sort categories alphabetically with legendaries first
  128. uniqueDescriptionTitles.sort((a, b) => {
  129. if (a.desc === "LEGENDARY") return -1
  130. if (b.desc === "LEGENDARY") return 1
  131. return a.desc.localeCompare(b.desc)
  132. })
  133.  
  134. return uniqueDescriptionTitles
  135. }
  136.  
  137. ensureNewSettingExists() {
  138. const settings = JSON.parse(
  139. localStorage.getItem(`${playername}.tcgSettings`)
  140. );
  141. if (settings && typeof settings.new === "undefined") {
  142. settings.new = true;
  143. localStorage.setItem(
  144. `${playername}.tcgSettings`,
  145. JSON.stringify(settings)
  146. );
  147. }
  148. }
  149.  
  150. initializeDatabase() {
  151. const dbName = `IdlePixel_TCG_DB.${playername}`;
  152. const version = 2;
  153. const request = indexedDB.open(dbName, version);
  154.  
  155. request.onerror = (event) => {
  156. console.error("Database error: ", event.target.error);
  157. };
  158.  
  159. request.onupgradeneeded = (event) => {
  160. const db = event.target.result;
  161. const objectStoreName = `current_cards`;
  162. if (!db.objectStoreNames.contains(objectStoreName)) {
  163. db.createObjectStore(objectStoreName, {keyPath: "cardNum"})
  164. } else {
  165. db.deleteObjectStore(objectStoreName)
  166. db.createObjectStore(objectStoreName, {keyPath: "cardNum"})
  167. }
  168. };
  169.  
  170. request.onsuccess = (event) => {
  171. this.db = event.target.result;
  172. };
  173. }
  174.  
  175. card_counts(currentCards){
  176. if(this.categoriesTCG.length === 0){console.log("Cards counted without categories!");return;}
  177. const counts = {}
  178.  
  179. this.categoriesTCG.forEach((category) => {
  180. counts[category.desc] = {
  181. uniHolo: 0,
  182. ttlHolo: 0,
  183. uniNormal: 0,
  184. ttlNormal: 0,
  185. possHolo: 0,
  186. possNormal: 0,
  187. possUniHolo: 0,
  188. possUniNormal: 0,
  189. };
  190. });
  191.  
  192. const overall_counts = {
  193. overallUniHolo: 0,
  194. overallHolo: 0,
  195. overallTTL: 0,
  196. overallUniNormal: 0,
  197. overallNormal: 0,
  198. };
  199.  
  200. Object.values(CardData.data).forEach((card) => {
  201. const category = this.categoriesTCG.find(
  202. (c) => c.id === `[${card.description_title}]`
  203. );
  204. if (category) {
  205. counts[category.desc].uniHolo++;
  206. counts[category.desc].uniNormal++;
  207. overall_counts.overallTTL++;
  208. }
  209. });
  210.  
  211. const uniHoloSetOverall = new Set();
  212. const uniNormalSetOverall = new Set();
  213.  
  214. currentCards.forEach((card) => {
  215. const new_id = `${card.id}${card.holo? "_h":""}`
  216. if (!this.newest_card_ids[new_id] || card.cardNum > this.newest_card_ids[new_id]) {
  217. this.newest_card_ids[new_id] = card.cardNum;
  218. }
  219.  
  220. const category = Object.entries(CardData.data).find(
  221. (c) => c[0] === card.id
  222. );
  223. if (category) {
  224. if (card.holo) {
  225. counts[category[1].description_title].possHolo++;
  226. counts[category[1].description_title].ttlHolo++;
  227. overall_counts.overallHolo++;
  228. if (!uniHoloSetOverall.has(card.id)) {
  229. uniHoloSetOverall.add(card.id);
  230. overall_counts.overallUniHolo++;
  231. counts[category[1].description_title].possUniHolo++;
  232. }
  233. } else {
  234. counts[category[1].description_title].possNormal++;
  235. counts[category[1].description_title].ttlNormal++;
  236. overall_counts.overallNormal++;
  237. if (!uniNormalSetOverall.has(card.id)) {
  238. uniNormalSetOverall.add(card.id);
  239. overall_counts.overallUniNormal++;
  240. counts[category[1].description_title].possUniNormal++;
  241. }
  242. }
  243. }
  244. });
  245. return {counts, overall_counts};
  246. }
  247.  
  248. async identifyAndRemoveAbsentCards(db, objectStoreName, currentCards) {
  249. try {
  250. const dbCards = await this.fetchAllCardsFromDB(db, objectStoreName);
  251.  
  252. const current_card_nums = currentCards.map(card => card.cardNum)
  253.  
  254. dbCards.forEach((dbCard) => {
  255. const card_num = dbCard.cardNum
  256. if (!current_card_nums.includes(card_num)) {
  257. //console.log(`Card not found in current cards, removing: ${dbCardKey}`);
  258. this.removeCardFromDB(db, objectStoreName, card_num);
  259. }
  260. });
  261. } catch (error) {
  262. console.error('Error in identifyAndRemoveAbsentCards:', error);
  263. }
  264. }
  265.  
  266. removeCardFromDB(db, objectStoreName, cardKey) {
  267. const transaction = db.transaction([objectStoreName], "readwrite");
  268. const objectStore = transaction.objectStore(objectStoreName);
  269. const request = objectStore.delete(cardKey);
  270. request.onerror = (event) => {
  271. console.error("Error removing card from DB:", event.target.error);
  272. };
  273. request.onsuccess = () => {
  274. //console.log(`Card removed from DB: ${cardKey}`);
  275. };
  276. }
  277.  
  278. updateTcgSettings(categoryId, state) {
  279. const settings = JSON.parse(
  280. localStorage.getItem(`${playername}.tcgSettings`)
  281. );
  282. settings[categoryId] = state;
  283. localStorage.setItem(
  284. `${playername}.tcgSettings`,
  285. JSON.stringify(settings)
  286. );
  287. }
  288.  
  289. getTcgSetting(categoryId) {
  290. const settings = JSON.parse(
  291. localStorage.getItem(`${playername}.tcgSettings`)
  292. );
  293. return settings[categoryId];
  294. }
  295.  
  296. tcgBuyerNotifications() {
  297. let tcgTimerCheck = IdlePixelPlus.getVarOrDefault("tcg_timer", 0, "int");
  298. let tcgUnlocked = IdlePixelPlus.getVarOrDefault("tcg_active", 0, "int");
  299. const notifDiv = document.createElement("div");
  300. notifDiv.id = `notification-tcg-timer`;
  301. notifDiv.onclick = function () {
  302. switch_panels("panel-criptoe-tcg");
  303. Modals.open_buy_tcg();
  304. };
  305. notifDiv.className = "notification hover";
  306. notifDiv.style = "margin-right: 4px; margin-bottom: 4px; display: none";
  307. notifDiv.style.display = "inline-block";
  308.  
  309. let elem = document.createElement("img");
  310. elem.setAttribute("src", window.get_image("images/ash_50.png"))
  311. const notifIcon = elem;
  312. notifIcon.className = "w20";
  313.  
  314. const notifDivLabel = document.createElement("span");
  315. notifDivLabel.id = `notification-tcg-timer-label`;
  316. notifDivLabel.innerText = " Loading...";
  317. notifDivLabel.className = "color-white";
  318.  
  319. notifDiv.append(notifIcon, notifDivLabel);
  320. document.querySelector("#notifications-area").prepend(notifDiv);
  321. if (tcgUnlocked == 0 || !this.getConfig("tcgNotification")) {
  322. document.querySelector("#notification-tcg-timer").style.display =
  323. "none";
  324. }
  325. }
  326.  
  327. updateTCGNotification() {
  328. let tcgTimerCheck = IdlePixelPlus.getVarOrDefault("tcg_timer", 0, "int");
  329. let tcgUnlocked = IdlePixelPlus.getVarOrDefault("tcg_active", 0, "int");
  330. if (this.getConfig("tcgNotification") && tcgUnlocked != 0) {
  331. document.getElementById("notification-tcg-timer").style.display =
  332. "inline-block";
  333. if (tcgTimerCheck > 0) {
  334. let timerLabel = format_time(tcgTimerCheck);
  335. document.getElementById(
  336. "notification-tcg-timer-label"
  337. ).innerText = ` ${timerLabel}`;
  338. } else {
  339. document.getElementById(
  340. "notification-tcg-timer-label"
  341. ).innerText = ` Time to buy cards!`;
  342. }
  343. } else {
  344. document.getElementById("notification-tcg-timer").style.display =
  345. "none";
  346. }
  347. }
  348.  
  349. async checkForAndHandleDuplicates() {
  350. const sendTo = IdlePixelPlus.plugins.tcgDex.getConfig("sendTo");
  351. const enableSend = IdlePixelPlus.plugins.tcgDex.getConfig("enableSend");
  352. const cards = await this.fetchAllCardsFromDB(this.db, 'current_cards');
  353. const cardOccurrences = new Map();
  354.  
  355. if (!this.dupe_sending && sendTo !== playername) {
  356. this.dupe_sending = true;
  357. cards.forEach((card) => {
  358. const key = `${card.id}-${card.holo}`;
  359. if (cardOccurrences.has(key)) {
  360. cardOccurrences.get(key).push(card);
  361. } else {
  362. cardOccurrences.set(key, [card]);
  363. }
  364. });
  365.  
  366. cardOccurrences.forEach((occurrences, key) => {
  367. if (occurrences.length > 1) {
  368. occurrences.sort((a, b) => b.cardNum - a.cardNum);
  369.  
  370. for (let i = 0; i < (occurrences.length - 1); i++) {
  371. const duplicate = occurrences[i];
  372.  
  373. if (enableSend && sendTo) {
  374. websocket.send(`GIVE_TCG_CARD=${sendTo}~${duplicate.cardNum}`);
  375. }
  376. }
  377. }
  378. });
  379.  
  380. setTimeout(function () {
  381. CardData.fetchData();
  382. setTimeout(function () {
  383. this.dupe_sending = false;
  384. }.bind(this), 10000);
  385. }.bind(this), 20000);
  386. }
  387. }
  388.  
  389. async fetchAllCardsFromDB(db, objectStoreName) {
  390. return new Promise((resolve, reject) => {
  391. const transaction = db.transaction([objectStoreName], "readonly");
  392. const objectStore = transaction.objectStore(objectStoreName);
  393. const request = objectStore.getAll();
  394.  
  395. request.onerror = (event) => {
  396. console.error("Error fetching cards from DB:", event.target.error);
  397. reject(event.target.error);
  398. };
  399.  
  400. request.onsuccess = () => {
  401. resolve(request.result);
  402. };
  403. });
  404. }
  405.  
  406. cardStyling() {
  407. const style = document.createElement("style");
  408. style.id = "styles-tcg-dex";
  409. style.textContent = `
  410. .tcg-card-inner {
  411. text-align: center;
  412. margin: 5px 18px;
  413. border: 2px solid black;
  414. background-color: #FEFEFE;
  415. box-shadow: 1px 1px 5px;
  416. padding: 25px 25px;
  417. }
  418.  
  419. .tcg-card {
  420. width: 200px;
  421. height: 300px;
  422. display: inline-block;
  423. border-radius: 10pt;
  424. box-shadow: 1px 1px 5px;
  425. margin: 5px;
  426. color: black;
  427. }
  428.  
  429. .tcg-card-title {
  430. font-weight: bold;
  431. font-size: 12pt;
  432. margin-left: 18px;
  433. margin-top: 4px;
  434. }
  435.  
  436. .tcg-card-inner-text {
  437. margin: 0px 18px;
  438. border: 1px solid black;
  439. border-radius: 5pt;
  440. background-color: #FEFEFE;
  441. padding: 5px 5px;
  442. font-size: 8pt;
  443. margin-top: 10px;
  444. margin-bottom: 4px;
  445. }
  446.  
  447. .tcg-card-rarity {
  448. font-weight: bold;
  449. font-size: 12pt;
  450. margin-right: 4px;
  451. text-align: right;
  452. font-style: italic;
  453. }
  454.  
  455. .tcg-card-type {
  456. font-weight: bold;
  457. font-size: 12pt;
  458. margin-left: 4px;
  459. text-align: left;
  460. }
  461.  
  462. .tcg-category-text {
  463. font-weight: bold;
  464. font-size: 12px;
  465. color: black;
  466. }
  467.  
  468. .tcgDex-card-container-open {
  469. margin-bottom: 20px;
  470. }
  471.  
  472. .tcgDex-card-container-closed {
  473. margin-bottom: 5px;
  474. }
  475. .tcgdex_category_label_row {
  476. display: inline-flex;
  477. width: 100%;
  478. height: 30px;
  479. font-weight: bolder;
  480. user-select: none;
  481. }
  482. .tcgdex_category_label_button {
  483. flex: 0 0 5%;
  484. align-content: center;
  485. text-align: center;
  486. }
  487. .tcgdex_category_label_cat {
  488. flex: 0 0 35%;
  489. align-content: center;
  490. }
  491. .tcgdex_category_label_total {
  492. flex: 0 0 10%;
  493. align-content: center;
  494. padding-left: 5px;
  495. }
  496. .tcgdex_category_label_counts {
  497. flex: 0 0 33%;
  498. align-content: center;
  499. }
  500. .tcgdex_category_label_counts_outer {
  501. flex: 0 0 25%;
  502. display: inline-flex;
  503. }
  504. .tcgdex_category_label_counts_label {
  505. padding-left: 5px;
  506. flex: 0 0 34%;
  507. align-content: center;
  508. }
  509. .tcgdex_category_new_label {
  510. flex: 0 0 55%;
  511. align-content: center;
  512. }
  513. .tcgdex_category_new_total {
  514. flex: 0 0 10%;
  515. align-content: center;
  516. }
  517. .tcgdex_category_new_values_holo {
  518. flex: 0 0 15%;
  519. align-content: center;
  520. }
  521. .tcgdex_category_new_values {
  522. flex: 0 0 15%;
  523. align-content: center;
  524. }
  525. `;
  526. document.head.appendChild(style);
  527. }
  528.  
  529. create_totals_bar_frag(overall_counts) {
  530. const template = document.getElementById("tcg_category_total_template")
  531. let clone = template.content.cloneNode(true)
  532.  
  533. clone.querySelector(`.ttl-cards-label`).textContent = `Total: ${overall_counts.overallHolo + overall_counts.overallNormal} `;
  534. clone.querySelector(`.uni-holo-label`).textContent = `U: ${overall_counts.overallUniHolo}/${overall_counts.overallTTL}`;
  535. clone.querySelector(`.ttl-holo-label`).textContent = `T: ${overall_counts.overallHolo}`;
  536. clone.querySelector(`.uni-normal-label`).textContent = `U: ${overall_counts.overallUniNormal}/${overall_counts.overallTTL}`;
  537. clone.querySelector(`.ttl-normal-label`).textContent = `T: ${overall_counts.overallNormal}`;
  538.  
  539. return clone;
  540. }
  541.  
  542. create_new_bar_frag() {
  543. const template = document.getElementById("tcg_category_new_template")
  544. let clone = template.content.cloneNode(true)
  545.  
  546. let loadVis = JSON.parse(localStorage.getItem(`${playername}.tcgSettings`))['new']
  547.  
  548. let category_div = clone.getElementById("tcgDex-New_Card-Container")
  549.  
  550. category_div.classList.add(loadVis ? "tcgDex-card-container-open" : "tcgDex-card-container-closed")
  551.  
  552. clone.querySelector(".tcg_category_container_inner").setAttribute("style", IdlePixelPlus.plugins.tcgDex.getTcgSetting("new") ? "" : "display: none;")
  553.  
  554. clone.querySelector(".tcg_new_timer_label").textContent = `New Cards (last ${this.new_card_timer} mins)`;
  555.  
  556. clone.querySelector(".fas").classList.add(IdlePixelPlus.plugins.tcgDex.getTcgSetting("new") ? "fa-eye-slash" : "fa-eye")
  557.  
  558. category_div.addEventListener("click", (event) => {
  559. if (event.target.closest(".tcg_category_container_inner")) {return;}
  560. const ele = event.currentTarget
  561. const category_inner = ele.querySelector(".tcg_category_container_inner")
  562. const isVisible = getComputedStyle(category_inner).display !== "none"
  563.  
  564. if (isVisible) {
  565. category_inner.style.display = "none"
  566. ele.querySelector(".fas").classList.remove("fa-eye-slash")
  567. ele.querySelector(".fas").classList.add("fa-eye")
  568. ele.classList.add("tcgDex-card-container-closed")
  569. ele.classList.remove("tcgDex-card-container-open")
  570. } else {
  571. category_inner.style.display = ""
  572. ele.querySelector(".fas").classList.add("fa-eye-slash")
  573. ele.querySelector(".fas").classList.remove("fa-eye")
  574. ele.classList.remove("tcgDex-card-container-closed")
  575. ele.classList.add("tcgDex-card-container-open")
  576. }
  577.  
  578. IdlePixelPlus.plugins.tcgDex.updateTcgSettings("new", !isVisible);
  579. });
  580.  
  581. return clone;
  582. }
  583.  
  584. draw_card_categories(card_container_frag, all_counts) {
  585. this.categoriesTCG.forEach((category) => {
  586. const template = document.getElementById("tcg_category_template");
  587. let row_frag = this.create_row_fragment(template, category);
  588.  
  589. card_container_frag.appendChild(row_frag);
  590. });
  591.  
  592. for (const [cat, counts] of Object.entries(all_counts)) {
  593. card_container_frag.querySelector(`#tcgDex-${cat}-Container .ttl-cards-label`).textContent = `Total: ${counts.possHolo + counts.possNormal}`;
  594. card_container_frag.querySelector(`#tcgDex-${cat}-Container .uni-holo-label`).textContent = `U: ${counts.possUniHolo}/${counts.uniHolo}`;
  595. card_container_frag.querySelector(`#tcgDex-${cat}-Container .ttl-holo-label`).textContent = `T: ${counts.ttlHolo}`;
  596. card_container_frag.querySelector(`#tcgDex-${cat}-Container .uni-normal-label`).textContent = `U: ${counts.possUniNormal}/${counts.uniNormal}`;
  597. card_container_frag.querySelector(`#tcgDex-${cat}-Container .ttl-normal-label`).textContent = `T: ${counts.ttlNormal}`;
  598. }
  599. }
  600.  
  601. create_card_template(){
  602. const card_template_str = `
  603. <template id="tcg_card_template">
  604. <div id="" onclick="Modals.open_tcg_give_card(null, this.getAttribute('data-card-id'))" style="" class='tcg-card hover'>
  605. <div class='row d-flex justify-content-around w-100'>
  606. <div class='col text-start' style="max-width:80%; margin-right:0; padding-right:1px; padding-left:0">
  607. <div class='tcg-card-title' style="white-space:nowrap; text-overflow:clip; overflow:hidden;"></div>
  608. </div>
  609. <div class='col-auto text-end' style="margin-top:4px; padding: 0; max-width:19%">
  610. <span class="dupe_count" style="font-weight: bolder;"></span>
  611. </div>
  612. </div>
  613. <div class='tcg-card-inner'>
  614. <img src="" class='w50'>
  615. </div>
  616. <div class='tcg-card-inner-text'>
  617. <span class='tcg-category-text'></span>
  618. <br>
  619. <br>
  620. <span class='tcg_card_zalgo'>𓀚𓁁𓂧𓃢𓃴𓄜𓈤𓈤𓊙𓐈𓀚𓁁𓂧𓃢𓃴<br>𓄜𓈤𓈤𓊙𓐈𓀚𓁁𓂧𓃢𓃴𓄜𓈤𓈤𓊙𓐈<br>𓀚𓁁𓂧𓃢𓃴𓄜𓈤𓈤𓊙𓐈𓀚𓁁𓂧𓃢𓃴<br>𓄜𓈤𓈤𓊙𓐈𓀚𓁁𓂧𓃢𓃴𓄜𓈤𓈤𓊙𓐈</span>
  621. </div>
  622. <div class="row" style="display: flex; flex-wrap:nowrap">
  623. <div class="col" style="flex: 0 0 50%; padding-right:0px;">
  624. <span class="tcg-card-type"></span>
  625. </div>
  626. <div class="col" style="flex: 0 0 50%; text-align: end; padding-left: 0px;flex-wrap:nowrap;">
  627. <span class="tcg-card-rarity"></span>
  628. </div>
  629. </div>
  630. </div>
  631. </template>
  632. `
  633. $("body").append($(card_template_str))
  634. }
  635.  
  636. create_row_template(){
  637. const row_template_str = `
  638. <template id="tcg_category_template">
  639. <div class="tcgdex_card_container">
  640. <div class="tcgdex_category_label_row">
  641. <div class="col tcgdex_category_label_button">
  642. <i class="fas"></i>
  643. </div>
  644. <div class="col tcgdex_category_label_cat">
  645. <span class="labelSpan"></span>
  646. </div>
  647. <div class="col tcgdex_category_label_total">
  648. <span class="ttl-cards-label"></span>
  649. </div>
  650. <div class="col tcgdex_category_label_counts_outer">
  651. <div class="tcgdex_category_label_counts_label">Holo:</div>
  652. <div class="tcgdex_category_label_counts">
  653. <span class="ttl-holo-label"></span>
  654. </div>
  655. <div class="tcgdex_category_label_counts">
  656. <span class="uni-holo-label"></span>
  657. </div>
  658. </div>
  659. <div class="col tcgdex_category_label_counts_outer">
  660. <div class="tcgdex_category_label_counts_label">Normal:</div>
  661. <div class="tcgdex_category_label_counts">
  662. <span class="ttl-normal-label"></span>
  663. </div>
  664. <div class="tcgdex_category_label_counts">
  665. <span class="uni-normal-label"></span>
  666. </div>
  667. </div>
  668. </div>
  669. <br>
  670. <div class="tcg_category_container_inner" id="tcgDex-LIMITED-Container-Inner" style="display: none;"></div>
  671. </div>
  672. </template>
  673. `
  674. $("body").append($(row_template_str))
  675. }
  676.  
  677. create_total_row_template(){
  678. const row_template_str = `
  679. <template id="tcg_category_total_template">
  680. <div class="tcgdex_category_label_row" style="background-color:cyan; color:black;">
  681. <div class="col tcgdex_category_label_button"></div>
  682. <div class="col tcgdex_category_label_cat">T = Total &amp; U = Unique</div>
  683. <div class="col tcgdex_category_label_total" style="border-left: 1px solid black;">
  684. <span class="ttl-cards-label"></span>
  685. </div>
  686. <div class="col tcgdex_category_label_counts_outer" style="border-left: 1px solid black;">
  687. <div class="tcgdex_category_label_counts_label">Holo:</div>
  688. <div class="tcgdex_category_label_counts">
  689. <span class="ttl-holo-label"></span>
  690. </div>
  691. <div class="tcgdex_category_label_counts">
  692. <span class="uni-holo-label"></span>
  693. </div>
  694. </div>
  695. <div class="col tcgdex_category_label_counts_outer" style="border-left: 1px solid black;">
  696. <div class="tcgdex_category_label_counts_label">Normal:</div>
  697. <div class="tcgdex_category_label_counts">
  698. <span class="ttl-normal-label"></span>
  699. </div>
  700. <div class="tcgdex_category_label_counts">
  701. <span class="uni-normal-label"></span>
  702. </div>
  703. </div>
  704. </div>
  705. </template>
  706. `
  707. $("body").append($(row_template_str))
  708. }
  709.  
  710. create_new_row_template(){
  711. const row_template_str = `
  712. <template id="tcg_category_new_template">
  713. <div id="tcgDex-New_Card-Container">
  714. <div class="tcgdex_category_label_row" style="background-color:gray; color:black;">
  715. <div class="col tcgdex_category_label_button">
  716. <i class="fas"></i>
  717. </div>
  718. <div class="col tcgdex_category_new_label">
  719. <span class="tcg_new_timer_label"></span>
  720. </div>
  721. <div class="col tcgdex_category_new_total"></div>
  722. <div class="col tcgdex_category_new_values_holo"></div>
  723. <div class="col tcgdex_category_new_values"></div>
  724. </div>
  725. <br>
  726. <div class="tcg_category_container_inner"></div>
  727. </div>
  728. </template>
  729. `
  730. $("body").append($(row_template_str))
  731. }
  732.  
  733. onLogin() {
  734. this.new_card_timer = IdlePixelPlus.plugins.tcgDex.getConfig("newCardTimer")
  735. this.create_card_template()
  736. this.create_row_template()
  737. this.create_total_row_template()
  738. this.create_new_row_template()
  739. CToe.loadCards = function () {};
  740. IdlePixelPlus.plugins['tcgDex'].cardStyling();
  741. if (!CardData.data) {
  742. CardData.fetchData();
  743. }
  744. playername = IdlePixelPlus.getVarOrDefault("username", "", "string");
  745. setTimeout(() => {
  746. this.categoriesTCG = this.getCategoryData();
  747. if (!localStorage.getItem(`${playername}.tcgSettings`)) {
  748. let defaultSettings = this.categoriesTCG.reduce((settings, category) => {
  749. settings[category.desc] = true;
  750. return settings;
  751. }, {});
  752. defaultSettings.new = true;
  753. localStorage.setItem(
  754. `${playername}.tcgSettings`,
  755. JSON.stringify(defaultSettings)
  756. );
  757. } else {
  758. IdlePixelPlus.plugins.tcgDex.ensureNewSettingExists();
  759. }
  760. this.initializeDatabase();
  761. this.tcgBuyerNotifications();
  762. this.updateTCGNotification();
  763.  
  764. this.card_order = new Map()
  765. let order = 1;
  766. Object.keys(CardData.data).forEach((card_name) => {
  767. this.card_order.set(`${card_name}_h`, order++);
  768. this.card_order.set(`${card_name}`, order++);
  769. });
  770. this.login_loaded = true
  771. }, 1000);
  772. }
  773.  
  774. onVariableSet(key, valueBefore, valueAfter) {
  775. if (this.login_loaded) {
  776. if (key.startsWith("tcg") && valueBefore != valueAfter) {
  777. IdlePixelPlus.plugins.tcgDex.updateTCGNotification();
  778. }
  779. }
  780. }
  781.  
  782. onConfigChange() {
  783. if (this.login_loaded) {
  784. IdlePixelPlus.plugins.tcgDex.updateTCGNotification();
  785. this.new_card_timer = IdlePixelPlus.plugins.tcgDex.getConfig("newCardTimer")
  786. }
  787. }
  788.  
  789. create_card_fragment(template, card){
  790. const id = card.cardNum
  791. const holo = card.holo
  792. const card_data = CardData.data[card.id]
  793.  
  794. const rarity_map = {
  795. common: "Common",
  796. uncommon: "Uncommon",
  797. rare: "Rare",
  798. very_rare: "Very Rare",
  799. legendary: "Legendary"
  800. }
  801.  
  802. let clone = template.content.cloneNode(true)
  803.  
  804. let tcg_outer = clone.querySelector(".tcg-card")
  805. tcg_outer.setAttribute("data-card-id", id)
  806. tcg_outer.setAttribute("data-card-cat", card_data.description_title)
  807.  
  808. const styles = `${card_data.border_css}${card_data.background_css}`
  809. tcg_outer.setAttribute("style", styles)
  810.  
  811. const label = card_data.label.replaceAll('MOONSTONE', 'M. STONE').replaceAll('PROMETHIUM', 'PROM.').replaceAll('WOODEN ARROWS', 'WOOD ARROWS').replaceAll('STINGER ', 'STING ')
  812.  
  813. clone.querySelector(".tcg-card-title").innerText = label
  814. clone.querySelector(".tcg-card-rarity").innerText = `(${rarity_map[card_data.rarity]})`
  815. clone.querySelector("img").setAttribute("src", `https://cdn.idle-pixel.com/images/${card_data.image}`)
  816. clone.querySelector(".tcg-category-text").innerText = `[${card_data.description_title}]`
  817.  
  818. if (card.count){
  819. clone.querySelector(".dupe_count").innerText = `x${card.count}`
  820. }
  821.  
  822.  
  823. if(holo){
  824. tcg_outer.id = `${card.id}_Holo`
  825. clone.querySelector(".tcg-card-type").innerText = " Holo"
  826. clone.querySelector(".tcg-card-inner").classList.add("holo")
  827. clone.querySelector(".tcg_card_zalgo").classList.add("shine")
  828. } else {
  829. tcg_outer.id = `${card.id}_Normal`
  830. clone.querySelector(".tcg-card-type").innerText = " Normal"
  831. clone.querySelector(".tcg_card_zalgo").classList.add("color-red")
  832. }
  833.  
  834. return clone;
  835. }
  836.  
  837. create_row_fragment(template, category){
  838. let loadVis = IdlePixelPlus.plugins.tcgDex.getTcgSetting(category.desc);
  839. let rowBGColor = this.row_bg_colours[category.desc];
  840. let rowTextColor = this.row_text_colours[category.desc];
  841.  
  842. let clone = template.content.cloneNode(true)
  843.  
  844. let category_div = clone.querySelector(".tcgdex_card_container")
  845. category_div.id = `tcgDex-${category.desc}-Container`
  846. category_div.classList.add(loadVis ? "tcgDex-card-container-open" : "tcgDex-card-container-closed")
  847.  
  848. let category_inner = clone.querySelector(".tcg_category_container_inner")
  849. category_inner.id = `tcgDex-${category.desc}-Container-Inner`;
  850. category_inner.setAttribute("style", IdlePixelPlus.plugins.tcgDex.getTcgSetting(category.desc) ? "" : "display: none;")
  851.  
  852. clone.querySelector(".tcgdex_category_label_row").setAttribute("style", `background-color: ${rowBGColor}; color: ${rowTextColor};`)
  853.  
  854. clone.querySelector(".fas").classList.add(IdlePixelPlus.plugins.tcgDex.getTcgSetting(category.desc) ? "fa-eye-slash" : "fa-eye")
  855.  
  856. clone.querySelector(".tcgdex_category_label_counts_outer").setAttribute("style", `border-left: 1px solid ${rowTextColor};`)
  857. clone.querySelector(".tcgdex_category_label_total").setAttribute("style", `border-left: 1px solid ${rowTextColor};`)
  858.  
  859. clone.querySelector(".labelSpan").innerHTML = category.label
  860.  
  861. category_div.addEventListener("click", (event) => {
  862. if (event.target.closest(".tcg_category_container_inner")) {return;}
  863. const ele = event.currentTarget
  864. const category_inner = ele.querySelector(".tcg_category_container_inner")
  865. const isVisible = getComputedStyle(category_inner).display !== "none"
  866.  
  867. if (isVisible) {
  868. category_inner.style.display = "none"
  869. ele.querySelector(".fas").classList.remove("fa-eye-slash")
  870. ele.querySelector(".fas").classList.add("fa-eye")
  871. ele.classList.add("tcgDex-card-container-closed")
  872. ele.classList.remove("tcgDex-card-container-open")
  873. } else {
  874. category_inner.style.display = ""
  875. ele.querySelector(".fas").classList.add("fa-eye-slash")
  876. ele.querySelector(".fas").classList.remove("fa-eye")
  877. ele.classList.remove("tcgDex-card-container-closed")
  878. ele.classList.add("tcgDex-card-container-open")
  879. }
  880.  
  881. IdlePixelPlus.plugins.tcgDex.updateTcgSettings(
  882. category.desc,
  883. !isVisible
  884. );
  885. });
  886.  
  887. return clone;
  888. }
  889.  
  890. draw_cards(currentCards, card_type_count){
  891. document.getElementById("tcg-area-context").innerHTML = ""
  892.  
  893. const template = document.getElementById("tcg_card_template")
  894. let card_container_frag = document.createDocumentFragment()
  895.  
  896. const {counts, overall_counts} = this.card_counts(currentCards)
  897.  
  898. card_container_frag.appendChild(this.create_totals_bar_frag(overall_counts))
  899. card_container_frag.appendChild(this.create_new_bar_frag())
  900. IdlePixelPlus.plugins['tcgDex'].draw_card_categories(card_container_frag, counts);
  901.  
  902. document.getElementById("tcg-area-context").appendChild(card_container_frag)
  903.  
  904. const card_sorter = {}
  905. this.categoriesTCG.forEach((category) => {
  906. card_sorter[category.desc] = []
  907. })
  908.  
  909. for (const [card, count] of Object.entries(card_type_count)) {
  910. const split_idx = card.lastIndexOf("_")
  911. const card_id = card.slice(0, split_idx)
  912. const holo = card.slice(split_idx + 1) === "Holo"
  913.  
  914. const order_key = holo? `${card_id}_h`: card_id
  915.  
  916. const card_data = {
  917. id: card_id,
  918. holo: holo,
  919. cardNum: this.newest_card_ids[order_key],
  920. count: count,
  921. }
  922.  
  923. const card_fragment = this.create_card_fragment(template, card_data)
  924. const card_category = card_fragment.querySelector(".tcg-card").getAttribute("data-card-cat")
  925.  
  926. const card_position = this.card_order.get(order_key)
  927. card_sorter[card_category][card_position] = card_fragment
  928. }
  929.  
  930. for (const [cat, cards] of Object.entries(card_sorter)) {
  931. let cat_frag = document.createDocumentFragment()
  932. cards.forEach(card_frag => {
  933. cat_frag.appendChild(card_frag)
  934. })
  935. document.getElementById(`tcgDex-${cat}-Container-Inner`).appendChild(cat_frag)
  936. }
  937. }
  938.  
  939. handle_new_cards(newCards){
  940. newCards.sort((a, b) => b.received_datetime - a.received_datetime);
  941. const new_card_container = document.getElementById("tcgDex-New_Card-Container").querySelector(".tcg_category_container_inner")
  942. new_card_container.innerHTML = "";
  943. const template = document.getElementById("tcg_card_template")
  944. let card_container_frag = document.createDocumentFragment()
  945.  
  946. for (const card of Object.values(newCards)) {
  947. card.count = false
  948. const card_fragment = this.create_card_fragment(template, card);
  949.  
  950. card_container_frag.appendChild(card_fragment)
  951. }
  952.  
  953. new_card_container.appendChild(card_container_frag)
  954. card_container_frag.innerHTML = ""
  955. }
  956.  
  957. find_new_cards(currentCards){
  958. const new_cards = []
  959. const objectStoreName = `current_cards`;
  960. const transaction = this.db.transaction([objectStoreName], "readwrite");
  961. const objectStore = transaction.objectStore(objectStoreName);
  962.  
  963. currentCards.forEach((card) => {
  964. const key = card.cardNum
  965. const getRequest = objectStore.get(key);
  966. getRequest.onsuccess = (event) => {
  967. let result = event.target.result;
  968. if (result) {
  969. let now = new Date();
  970. let timeBefore = new Date(now.getTime() - this.new_card_timer * 60 * 1000);
  971.  
  972. let receivedDateTime = result.received_datetime;
  973. if (receivedDateTime > timeBefore) {
  974. new_cards.push({
  975. cardNum: result.cardNum,
  976. id: result.id,
  977. holo: result.holo,
  978. received_datetime: receivedDateTime,
  979. });
  980. }
  981. } else {
  982. const cardData = {
  983. cardNum: card.cardNum,
  984. id: card.id,
  985. holo: card.holo,
  986. received_datetime: new Date(),
  987. };
  988. const addRequest = objectStore.add(cardData);
  989. addRequest.onerror = (event) => {
  990. console.error("Error adding new card:", event.target.error);
  991. };
  992. addRequest.onsuccess = (event) => {
  993. // console.log("Successfully adding new card:", event.target.result);
  994. };
  995. new_cards.push({
  996. cardNum: card.cardNum,
  997. id: card.id,
  998. holo: card.holo,
  999. received_datetime: new Date(),
  1000. });
  1001. }
  1002. };
  1003. getRequest.onerror = (event) => {
  1004. console.error("Error fetching card record:", event.target.error);
  1005. };
  1006. });
  1007. return new_cards;
  1008. }
  1009.  
  1010. parse_card_stream(parts){
  1011. const current_cards = []
  1012. const card_type_count = {};
  1013.  
  1014. for (let i = 0; i < parts.length; i += 3) {
  1015. const cardNum = parts[i];
  1016. const cardKey = parts[i + 1];
  1017. const isHolo = parts[i + 2] === "true";
  1018. const idHolo = isHolo ? "Holo" : "Normal";
  1019. const countKey = `${cardKey}_${idHolo}`;
  1020.  
  1021. current_cards.push({
  1022. id: cardKey,
  1023. cardNum: parseInt(cardNum),
  1024. holo: isHolo,
  1025. })
  1026.  
  1027. // Increment or initialise count for card type
  1028. card_type_count[countKey] = (card_type_count[countKey] ?? 0) + 1
  1029. }
  1030.  
  1031. return {current_cards, card_type_count};
  1032. }
  1033.  
  1034. onMessageReceived(data) {
  1035. if (data.startsWith("REFRESH_TCG")) {
  1036. const parts = data.replace("REFRESH_TCG=", "").split("~")
  1037.  
  1038. const {current_cards, card_type_count} = this.parse_card_stream(parts)
  1039.  
  1040. const new_cards = this.find_new_cards(current_cards);
  1041.  
  1042. void this.identifyAndRemoveAbsentCards(this.db, `current_cards`, current_cards)
  1043.  
  1044. this.draw_cards(current_cards, card_type_count);
  1045.  
  1046. setTimeout(() => this.handle_new_cards(new_cards), 2000)
  1047.  
  1048. void this.checkForAndHandleDuplicates()
  1049. }
  1050. }
  1051. }
  1052.  
  1053. const plugin = new tcgDex();
  1054. IdlePixelPlus.registerPlugin(plugin);
  1055. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址