Moar Attack Buttons

Adds a button to each weapon for faster fight initiation

Bu betiği kurabilmeniz için Tampermonkey, Greasemonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği yüklemek için Tampermonkey gibi bir uzantı yüklemeniz gerekir.

Bu betiği kurabilmeniz için Tampermonkey ya da Violentmonkey gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği kurabilmeniz için Tampermonkey ya da Userscripts gibi bir kullanıcı betiği eklentisini kurmanız gerekmektedir.

Bu betiği indirebilmeniz için ayrıca Tampermonkey gibi bir eklenti kurmanız gerekmektedir.

Bu komut dosyasını yüklemek için bir kullanıcı komut dosyası yöneticisi uzantısı yüklemeniz gerekecek.

(Zaten bir kullanıcı komut dosyası yöneticim var, kurmama izin verin!)

Bu stili yüklemek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için Stylus gibi bir uzantı kurmanız gerekir.

Bu stili yükleyebilmek için Stylus gibi bir uzantı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

Bu stili yüklemek için bir kullanıcı stili yöneticisi uzantısı kurmanız gerekir.

Bu stili yükleyebilmek için bir kullanıcı stili yöneticisi uzantısı yüklemeniz gerekir.

(Zateb bir user-style yöneticim var, yükleyeyim!)

// ==UserScript==
// @name         Moar Attack Buttons
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Adds a button to each weapon for faster fight initiation
// @author       xentac
// @match        https://www.torn.com/page.php?sid=attack*
// @grant        none
// @run-at document-end
// ==/UserScript==

(function () {
  "use strict";

  // Constants for script identification and selection
  const CUSTOM_BTN_CLASS = "script-bridge-trigger";
  const COMBAT_BTN_SELECTOR =
    '[class*="modal____"] [class*="dialogWrapper___"] button[type="submit"]';

  /**
   * Finds the primary target button and triggers its click handler,
   * then cleans up all script-generated buttons.
   */
  function executeCombatSequence() {
    const targetButton = document.querySelector(COMBAT_BTN_SELECTOR);
    for (const c of targetButton.classList) {
      if (c.startsWith("btnTimer__")) {
        console.warn(
          '[Bridge Script] Target button ("Start fight" or "Join Fight") has a timer.',
        );
        return;
      }
    }

    if (targetButton) {
      targetButton.click();
    } else {
      console.warn(
        '[Bridge Script] Target button ("Start fight" or "Join Fight") was not found in the DOM.',
      );
      return;
    }

    // Cleanup: Purge all buttons created by this script
    document
      .querySelectorAll(`.${CUSTOM_BTN_CLASS}`)
      .forEach((btn) => btn.remove());
  }

  /**
   * Iterates through available figure containers and injects the trigger overlay.
   */
  async function injectOverlays() {
    // Target weapon figures safely by their built-in prefix naming convention
    const found = await waitForElement(
      '[aria-describedby*="label_attacker_"] [class*="weaponImage___"]',
      10_000,
    );

    if (!found) {
      return;
    }

    const figures = document.querySelectorAll(
      '[aria-describedby*="label_attacker_"] [class*="weaponImage___"]',
    );

    figures.forEach((figure) => {
      // Avoid duplicate injections if the DOM re-evaluates
      if (figure.querySelector(`.${CUSTOM_BTN_CLASS}`)) return;

      // Ensure the parent container supports relative positioning context
      if (window.getComputedStyle(figure).position === "static") {
        figure.style.position = "relative";
      }

      // Construct structural overlay button
      const overlayBtn = document.createElement("button");
      overlayBtn.className = CUSTOM_BTN_CLASS;
      overlayBtn.textContent = "Fight";

      // Inline styling to ensure visual layout visibility directly over the figure asset
      Object.assign(overlayBtn.style, {
        position: "absolute",
        top: "0",
        left: "0",
        width: "100%",
        height: "100%",
        zIndex: "9999",
        padding: "4px 8px",
        fontSize: "11px",
        color: "#fff",
        border: "1px solid #666",
        borderRadius: "3px",
        cursor: "pointer",
        backgroundColor: "rgba(51, 51, 51, 0.4)",
      });

      overlayBtn.addEventListener("click", (e) => {
        //e.stopPropagation(); // Stop event bubbling to underlying React elements
        executeCombatSequence();
      });

      figure.appendChild(overlayBtn);
    });
  }

  function waitForElement(selector, timeoutMs = 15_000) {
    return new Promise((resolve) => {
      const existing = document.querySelector(selector);
      if (existing) {
        return resolve(existing);
      }

      const observer = new MutationObserver((_, obs) => {
        const el = document.querySelector(selector);
        if (el) {
          obs.disconnect();
          resolve(el);
        }
      });

      observer.observe(document.documentElement, {
        childList: true,
        subtree: true,
      });

      if (timeoutMs > 0) {
        setTimeout(() => {
          observer.disconnect();
          log.debug(`Timeout waiting for element selector: '${selector}'`);
          resolve(null);
        }, timeoutMs);
      }
    });
  }

  // Initial check
  injectOverlays();
})();