IdlePixel TCG Dex (Lux Fork)

Organizational script for the Criptoe Trading Card Game

目前为 2025-01-15 提交的版本。查看 最新版本

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