Moar Attack Buttons

Adds a button to each weapon for faster fight initiation

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==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();
})();