Greasy Fork

Crosshair Mod

Custom crosshairs for Sploop.io

目前为 2025-04-18 提交的版本。查看 最新版本

// ==UserScript==
// @name         Crosshair Mod
// @namespace    http://tampermonkey.net/
// @version      3
// @description  Custom crosshairs for Sploop.io
// @author       hooder
// @license MIT
// @match        https://sploop.io/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function () {
  'use strict';
  const style = document.createElement('style');
  style.textContent = `
    * {
      font-family: 'Segoe UI', sans-serif;
    }
    #hookx-ui {
      position: fixed;
      top: 50px;
      left: 50px;
      width: 300px;
      background: rgba(20, 20, 20, 0.95);
      border-radius: 10px;
      color: white;
      z-index: 100000;
      box-shadow: 0 0 12px rgba(0,0,0,0.6);
      padding: 15px;
      cursor: move;
      user-select: none;
    }
    .hookx-select, .hookx-color {
      margin-top: 10px;
      width: 100%;
      padding: 5px;
      background: #111;
      color: white;
      border: 1px solid #333;
      border-radius: 5px;
    }
    .crosshair {
      position: fixed;
      z-index: 99999;
      pointer-events: none;
      display: none;
      transform: translate(-50%, -50%);
      will-change: transform;
    }
    .crosshair.dot {
      width: 12px;
      height: 12px;
      border-radius: 50%;
    }
    .crosshair.cross {
      width: 20px;
      height: 20px;
    }
    .crosshair.cross::before, .crosshair.cross::after {
      content: '';
      position: absolute;
      background-color: currentColor;
    }
    .crosshair.cross::before {
      top: 50%;
      left: 0;
      width: 100%;
      height: 2px;
      transform: translateY(-50%);
    }
    .crosshair.cross::after {
      top: 0;
      left: 50%;
      width: 2px;
      height: 100%;
      transform: translateX(-50%);
    }
    .crosshair.spike {
      width: 48px;
      height: 48px;
      background-image: url("https://sploop.io/img/entity/hard_spike.png");
      background-size: cover;
      background-repeat: no-repeat;
      background-position: center;
    }
    .crosshair.square {
      width: 14px;
      height: 14px;
      border: 2px solid;
    }
    .crosshair.triangle {
      width: 0;
      height: 0;
      border-left: 10px solid transparent;
      border-right: 10px solid transparent;
      border-bottom: 18px solid currentColor;
    }
    .crosshair.xcross {
      width: 20px;
      height: 20px;
    }
    .crosshair.xcross::before, .crosshair.xcross::after {
      content: '';
      position: absolute;
      width: 2px;
      height: 100%;
      background-color: currentColor;
      left: 50%;
      top: 0;
      transform-origin: center;
    }
    .crosshair.xcross::before {
      transform: rotate(45deg);
    }
    .crosshair.xcross::after {
      transform: rotate(-45deg);
    }
    body.hide-cursor * {
      cursor: none !important;
    }
    #watermark {
      position: fixed;
      bottom: 100px;
      right: 10px;
      color: rgba(255, 255, 255, 0.2);
      font-size: 14px;
      font-family: 'Segoe UI', sans-serif;
      pointer-events: none;
      z-index: 100001;
      user-select: none;
    }
  `;
  document.head.appendChild(style);
  const ui = document.createElement('div');
  ui.id = 'hookx-ui';
  ui.innerHTML = `
    <label><input type="checkbox" id="toggleCrosshair"> Enable Crosshair</label>
    <select id="crosshairStyle" class="hookx-select">
      <option value="dot">Dot</option>
      <option value="cross">Cross</option>
      <option value="spike">Spike</option>
      <option value="square">Square</option>
      <option value="triangle">Triangle</option>
      <option value="xcross">X Cross</option>
    </select>
    <input type="color" id="crosshairColor" class="hookx-color" value="#ff0000">
  `;
  document.body.appendChild(ui);
  const watermark = document.createElement('div');
  watermark.id = 'watermark';
  watermark.textContent = 'hooder';
  document.body.appendChild(watermark);
  let crosshair = document.createElement('div');
  crosshair.className = 'crosshair dot';
  document.body.appendChild(crosshair);
  const toggle = document.getElementById('toggleCrosshair');
  const styleSelect = document.getElementById('crosshairStyle');
  const colorPicker = document.getElementById('crosshairColor');
  function throttle(fn, wait) {
    let lastCall = 0;
    return function (...args) {
      const now = performance.now();
      if (now - lastCall >= wait) {
        lastCall = now;
        fn(...args);
      }
    };
  }
  function debounce(fn, wait) {
    let timeout;
    return function (...args) {
      clearTimeout(timeout);
      timeout = setTimeout(() => fn(...args), wait);
    };
  }
  function setCrosshairStyle(styleName) {
    const newCrosshair = document.createElement('div');
    newCrosshair.className = 'crosshair ' + styleName;
    document.body.replaceChild(newCrosshair, crosshair);
    crosshair = newCrosshair;
    updateColor();
    if (toggle.checked) {
      crosshair.style.display = 'block';
    }
  }
  toggle.addEventListener('change', () => {
    if (toggle.checked) {
      crosshair.style.display = 'block';
      document.body.classList.add('hide-cursor');
      document.addEventListener('mousemove', throttledMoveCrosshair);
    } else {
      crosshair.style.display = 'none';
      document.body.classList.remove('hide-cursor');
      document.removeEventListener('mousemove', throttledMoveCrosshair);
    }
  });
  styleSelect.addEventListener('change', () => {
    setCrosshairStyle(styleSelect.value);
  });
  const debouncedUpdateColor = debounce(updateColor, 100);
  colorPicker.addEventListener('input', debouncedUpdateColor);
  function updateColor() {
    const color = colorPicker.value;
    crosshair.style.backgroundColor = '';
    crosshair.style.borderColor = '';
    crosshair.style.color = color;
    if (crosshair.classList.contains('dot')) {
      crosshair.style.backgroundColor = color;
    } else if (crosshair.classList.contains('cross') || crosshair.classList.contains('xcross')) {
      crosshair.style.background = 'transparent';
    } else if (crosshair.classList.contains('square')) {
      crosshair.style.borderColor = color;
      crosshair.style.background = 'transparent';
    }
  }
  const throttledMoveCrosshair = throttle((e) => {
    crosshair.style.transform = `translate(${e.clientX}px, ${e.clientY}px) translate(-50%, -50%)`;
  }, 16);
  let isDragging = false;
  let currentX;
  let currentY;
  let initialX;
  let initialY;
  ui.addEventListener('mousedown', (e) => {
    initialX = e.clientX - currentX;
    initialY = e.clientY - currentY;
    isDragging = true;
  });
  document.addEventListener('mousemove', (e) => {
    if (isDragging) {
      e.preventDefault();
      currentX = e.clientX - initialX;
      currentY = e.clientY - initialY;
      ui.style.left = `${currentX}px`;
      ui.style.top = `${currentY}px`;
    }
  });
  document.addEventListener('mouseup', () => {
    isDragging = false;
  });
  currentX = 50;
  currentY = 50;
})();