Challengers Tower - Inventory Export

Adds an Export button to the Inventory page that copies a plain-text list of all items with tier, quantity, and equipped status

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Challengers Tower - Inventory Export
// @description  Adds an Export button to the Inventory page that copies a plain-text list of all items with tier, quantity, and equipped status
// @namespace    https://challengerstower.com
// @version      1.7.0
// @author       Zach
// @license      MIT
// @match        *://challengerstower.com/*
// @run-at       document-end
// @grant        none
// ==/UserScript==

function ctCopy(str) {
  var ta = document.createElement("textarea");
  ta.value = str;
  ta.style.position = "fixed";
  ta.style.left = "-9999px";
  document.body.appendChild(ta);
  ta.select();
  var ok = false;
  try { ok = document.execCommand("copy"); } catch(e) {}
  document.body.removeChild(ta);
  return ok;
}

function ctGetAlpineData(el) {
  // Try different Alpine versions' internal property names
  if(el._x_dataStack && el._x_dataStack[0]) return el._x_dataStack[0];
  if(el.__x && el.__x.$data) return el.__x.$data;
  if(el._x_data) return el._x_data;
  try {
    if(typeof Alpine !== "undefined" && Alpine.$data) return Alpine.$data(el);
  } catch(e) {}
  return null;
}

function ctFindGroups() {
  var els = document.querySelectorAll("[x-data]");
  for(var i=0; i<els.length; i++) {
    var data = ctGetAlpineData(els[i]);
    if(data && data.groups && data.groups.length > 0) {
      return data.groups;
    }
  }
  return null;
}

function ctScrapeDOM() {
  // Fallback: scrape from rendered DOM, inventory section only
  var results = [];
  var seen = {};

  // Find the inventory section by looking for the "Inventory" heading
  var headings = document.querySelectorAll("h3");
  var inventorySection = null;
  for(var i=0; i<headings.length; i++) {
    if(headings[i].textContent.trim() === "Inventory") {
      inventorySection = headings[i].closest(".bg-white, .bg-gray-800");
      break;
    }
  }

  var container = inventorySection || document;
  var nameEls = container.querySelectorAll("span.font-medium");

  nameEls.forEach(function(el) {
    var name = el.textContent.trim();
    if(!name || seen[name]) return;

    var card = el.closest(".bg-gray-100, .rounded-lg");
    if(!card) return;
    var cardText = card.textContent || "";

    var tierMatch = cardText.match(/\bT([0-9]+)\b/);
    var tier = tierMatch ? "T" + tierMatch[1] : "";
    var isUlt = /\bULT\b/.test(cardText) ? " [ULT]" : "";
    var equipped = /Equipped/i.test(cardText) ? " [Equipped]" : "";
    var upgradeMatch = cardText.match(/T[0-9]+:\s*[0-9]+%/);
    var progress = upgradeMatch ? " (" + upgradeMatch[0] + ")" : "";

    seen[name] = true;
    results.push(name + (tier ? " [" + tier + "]" : "") + isUlt + progress + equipped);
  });

  return results.length > 0 ? results : null;
}

function ctScrape() {
  var groups = ctFindGroups();
  if(groups) {
    return groups.map(function(g) {
      var tier = "T" + g.highest_tier;
      var isUlt = g.item.ultimate ? " [ULT]" : "";
      var count = g.total_count > 1 ? " x" + g.total_count : "";
      var progress = g.maxed_out ? " [MAX]" : " (T" + g.next_tier + ": " + Math.round(g.progress) + "%)";
      var equipped = g.equipped_tiers.length > 0 ? " [Equipped]" : "";
      return g.item.name + " [" + tier + "]" + isUlt + count + progress + equipped;
    });
  }
  // Fall back to DOM scraping
  return ctScrapeDOM();
}

function ctAddButton() {
  if(document.getElementById("ct-inv-export")) return;
  if(!/\/items/.test(window.location.href)) return;

  var btn = document.createElement("button");
  btn.id = "ct-inv-export";
  btn.textContent = "Export Inventory";
  btn.style.cssText = "position:fixed;bottom:80px;right:16px;z-index:99999;padding:10px 16px;background:#4f8ef7;color:#fff;border:none;border-radius:8px;font-weight:bold;font-size:14px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,0.35);";

  btn.onclick = function() {
    var lines = ctScrape();
    if(!lines) {
      btn.textContent = "Not ready yet";
      btn.style.background = "#e74c3c";
      setTimeout(function() {
        btn.textContent = "Export Inventory";
        btn.style.background = "#4f8ef7";
      }, 2000);
      return;
    }
    var out = ["=== CT Inventory (" + lines.length + " items) ==="].concat(lines).join("\n");
    console.log(out);
    var ok = ctCopy(out);
    btn.textContent = ok ? "Copied! (" + lines.length + ")" : "See console";
    btn.style.background = ok ? "#2ecc71" : "#e74c3c";
    setTimeout(function() {
      btn.textContent = "Export Inventory";
      btn.style.background = "#4f8ef7";
    }, 3000);
  };

  document.body.appendChild(btn);
}

var ctAttempts = 0;
var ctPoll = setInterval(function() {
  ctAttempts++;
  if(!/\/items/.test(window.location.href)) {
    clearInterval(ctPoll);
    return;
  }
  if(document.body && (ctFindGroups() || ctAttempts >= 40)) {
    ctAddButton();
    clearInterval(ctPoll);
  }
}, 500);