登录

我给烧饼 3D 水果机做了个自动开始的工具

532
  • 楼主

    最近看到好几个饼友都出了水果机满徽章,感觉爆率还挺高的,于是我也忍不住去试了试。

    玩了两个小时,成功出了两个!不过徽章是出了,手也快点废了…… 整整两个小时都在不停地点 “开始”,属实给我点麻了。😂

    为了造福后面想刷水果机满徽章的饼友们,我就顺手写了一个油猴脚本,可以自动点击 “开始”,彻底解放双手,再也不用一直手动狂点了!

    废话不多说,直接上脚本!

    最后,祝各位饼友都能出,早日拿下水果机的满勋章!🎰

    
    // ==UserScript==
    // @name         烧饼水果机自动开奖助手
    // @namespace    sb-fruit-auto-spin
    // @version      2.1.0
    // @description  等待游戏加载,沿用下注,无下注时尝试苹果10,自动续开
    // @match        https://sb.sb/games/fruit*
    // @grant        none
    // @run-at       document-idle
    // ==/UserScript==
    
    (() => {
      'use strict';
    
      if (window.__SB_FRUIT_AUTO_210__) return;
      window.__SB_FRUIT_AUTO_210__ = true;
    
      const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
      const $ = (selector, root = document) =>
        root.querySelector(selector);
    
      let running = false;
      let paused = false;
      let rounds = 0;
      let loopToken = 0;
    
      const PANEL_ID = 'sb-fruit-auto-panel';
    
      function getSpin() {
        return $('#spin');
      }
    
      function getLabel() {
        return $('.spin-label')?.textContent.trim() || '';
      }
    
      function getBets() {
        return [...document.querySelectorAll(
          '#bet-grid output[data-amount]'
        )].map(el => Number(el.textContent.trim()) || 0);
      }
    
      function getTotalBet() {
        return getBets().reduce((sum, value) => sum + value, 0);
      }
    
      function gameReady() {
        return !!(
          getSpin() &&
          $('#bet-grid') &&
          document.querySelector('canvas')
        );
      }
    
      function setStatus(message) {
        const el = $('#sb-fa-status');
        if (el) el.textContent = message;
      }
    
      function addLog(message) {
        const el = $('#sb-fa-log');
        const time = new Date().toLocaleTimeString();
    
        if (el) {
          el.textContent = `${time} ${message}`;
        }
    
        console.log('[水果机助手]', message);
      }
    
      function updateButtons() {
        const start = $('#sb-fa-start');
        const pause = $('#sb-fa-pause');
    
        if (!start || !pause) return;
    
        start.disabled = running;
        pause.disabled = !running;
        pause.textContent = paused ? '继续' : '暂停';
      }
    
      // 查找显示为10的下注档位
      function findTenUnit() {
        const candidates = [
          ...document.querySelectorAll(
            'button, [role="button"]'
          )
        ];
    
        return candidates.find(el => {
          if (el.closest('#bet-grid')) return false;
    
          const text = (el.innerText || el.textContent || '')
            .replace(/\s+/g, ' ')
            .trim();
    
          return /^10(?:\s*游戏币)?$/.test(text);
        });
      }
    
      // 无下注时才尝试设置苹果10
      async function ensureBet() {
        const current = getBets();
    
        if (current.some(value => value > 0)) {
          addLog('检测到已有下注,沿用原下注');
          return true;
        }
    
        setStatus('没有下注,正在查找10游戏币档位…');
    
        const unit = findTenUnit();
        const apple = $('#bet-grid [data-add="3"]');
    
        if (!unit || !apple) {
          setStatus('找不到下注档位,请手动设置苹果10');
          addLog('没有找到可靠的10游戏币按钮,未自动下注');
          return false;
        }
    
        unit.click();
        await sleep(500);
    
        apple.click();
        await sleep(700);
    
        const after = getBets();
    
        if (after[3] === 10 && after.reduce((a, b) => a + b, 0) === 10) {
          addLog('已成功设置苹果10');
          return true;
        }
    
        setStatus('下注验证不通过,请手动检查');
        addLog('实际下注:' + JSON.stringify(after));
        return false;
      }
    
      async function waitForGame(token) {
        setStatus('等待你进入水果机…');
    
        while (running && token === loopToken) {
          if (gameReady()) {
            setStatus('游戏已加载,可以开始');
            return true;
          }
    
          await sleep(500);
        }
    
        return false;
      }
    
      async function waitForIdle(token) {
        let busyDetected = false;
        let idleSince = 0;
        const started = Date.now();
    
        while (running && token === loopToken) {
          if (paused) {
            await sleep(300);
            continue;
          }
    
          const spin = getSpin();
    
          if (!spin) {
            setStatus('游戏按钮消失,等待恢复');
            await sleep(500);
            continue;
          }
    
          const busy = spin.disabled || getLabel() !== '开始开奖';
    
          if (busy) {
            busyDetected = true;
            idleSince = 0;
          } else if (busyDetected) {
            if (!idleSince) idleSince = Date.now();
    
            // 按钮恢复空闲并稳定一段时间
            if (Date.now() - idleSince >= 1800) {
              return true;
            }
          }
    
          if (Date.now() - started > 120000) {
            setStatus('等待开奖超时,停止自动续开');
            return false;
          }
    
          await sleep(300);
        }
    
        return false;
      }
    
      async function run(token) {
        if (!await waitForGame(token)) return;
    
        const maxRounds = Math.min(
          999,
          Math.max(1, Number($('#sb-fa-limit').value) || 10)
        );
    
        const stopBalance = Math.max(
          0,
          Number($('#sb-fa-balance').value) || 0
        );
    
        while (running && token === loopToken) {
          if (paused) {
            await sleep(300);
            continue;
          }
    
          if (document.hidden) {
            setStatus('页面在后台,等待切回');
            await sleep(1000);
            continue;
          }
    
          if (!gameReady()) {
            setStatus('等待游戏重新加载');
            await sleep(1000);
            continue;
          }
    
          if (rounds >= maxRounds) {
            setStatus('达到最大局数,停止');
            break;
          }
    
          const spin = getSpin();
    
          if (spin.disabled || getLabel() !== '开始开奖') {
            setStatus('等待游戏进入空闲状态');
            await sleep(500);
            continue;
          }
    
          if (!await ensureBet()) break;
    
          if (stopBalance > 0) {
            const balanceElement =
              $('#balance') ||
              $('[data-balance]');
    
            if (!balanceElement) {
              setStatus('无法读取余额,停止运行');
              break;
            }
    
            const currentBalance = Number(
              balanceElement.textContent.replace(/[^\d.-]/g, '')
            );
    
            if (!Number.isFinite(currentBalance) ||
              currentBalance <= stopBalance) {
              setStatus('余额达到停止线');
              break;
            }
          }
    
          if (!getSpin() ||
            getSpin().disabled ||
            getLabel() !== '开始开奖') {
            await sleep(500);
            continue;
          }
    
          setStatus('正在启动下一局…');
    
          getSpin().click();
    
          // 必须检测到游戏真正进入运行状态
          let started = false;
    
          for (let i = 0; i < 40; i++) {
            await sleep(250);
    
            if (
              !getSpin() ||
              getSpin().disabled ||
              getLabel() !== '开始开奖'
            ) {
              started = true;
              break;
            }
          }
    
          if (!started) {
            setStatus('没有检测到开始状态,已停止');
            addLog('开始状态检测失败');
            break;
          }
    
          rounds++;
          setStatus(`第 ${rounds} 局正在进行`);
          addLog(`第 ${rounds} 局开始`);
    
          const completed = await waitForIdle(token);
    
          if (!completed) break;
    
          addLog(`第 ${rounds} 局完成`);
          setStatus(`第 ${rounds} 局结束,准备下一局`);
    
          await sleep(1500);
        }
    
        if (token === loopToken) {
          running = false;
          paused = false;
          updateButtons();
          setStatus(`自动运行结束,启动 ${rounds} 局`);
        }
      }
    
      function start() {
        if (running) return;
    
        running = true;
        paused = false;
        rounds = 0;
    
        const token = ++loopToken;
    
        updateButtons();
        addLog('自动运行启动');
    
        run(token).catch(error => {
          console.error('[水果机助手]', error);
          running = false;
          paused = false;
          updateButtons();
          setStatus('脚本错误:' + error.message);
        });
      }
    
      function togglePause() {
        if (!running) return;
    
        paused = !paused;
        updateButtons();
    
        setStatus(paused ? '已暂停' : '继续运行');
        addLog(paused ? '暂停' : '恢复');
      }
    
      function stop() {
        running = false;
        paused = false;
        loopToken++;
    
        updateButtons();
        setStatus('已停止,不再自动开始下一局');
        addLog('手动停止');
      }
    
      function createPanel() {
        if ($('#' + PANEL_ID)) return;
    
        const panel = document.createElement('div');
        panel.id = PANEL_ID;
    
        panel.innerHTML = `
          <div style="font-size:16px;font-weight:bold;margin-bottom:12px">
            🍎 水果机自动开奖 2.1
          </div>
    
          <div id="sb-fa-status" style="
            font-size:13px;
            color:#a7e9b6;
            margin-bottom:12px;
          ">等待进入游戏…</div>
    
          <label style="font-size:12px">
            最大连续局数
            <input id="sb-fa-limit" type="number"
              min="1" max="999" value="10">
          </label>
    
          <label style="font-size:12px">
            余额停止线(0为关闭)
            <input id="sb-fa-balance" type="number"
              min="0" value="0">
          </label>
    
          <div style="display:flex;gap:6px;margin-top:12px">
            <button id="sb-fa-start">开始</button>
            <button id="sb-fa-pause" disabled>暂停</button>
            <button id="sb-fa-stop">停止</button>
          </div>
    
          <div id="sb-fa-log" style="
            font-size:11px;
            color:#ccc;
            margin-top:12px;
            word-break:break-word;
          ">等待操作</div>
        `;
    
        Object.assign(panel.style, {
          position: 'fixed',
          right: '16px',
          top: '100px',
          width: '250px',
          padding: '15px',
          boxSizing: 'border-box',
          background: 'rgba(25,30,35,.97)',
          color: '#fff',
          zIndex: '2147483647',
          border: '1px solid #777',
          borderRadius: '10px',
          boxShadow: '0 4px 20px #0008',
          fontFamily: 'Arial,sans-serif'
        });
    
        const style = document.createElement('style');
    
        style.textContent = `
          #${PANEL_ID} input {
            display:block;
            box-sizing:border-box;
            width:100%;
            padding:7px;
            margin:5px 0 10px;
            border-radius:5px;
            border:1px solid #888;
          }
    
          #${PANEL_ID} button {
            flex:1;
            padding:9px 4px;
            border:0;
            border-radius:5px;
            background:#268b48;
            color:white;
            cursor:pointer;
          }
    
          #${PANEL_ID} button:disabled {
            opacity:.4;
            cursor:not-allowed;
          }
    
          #sb-fa-pause {
            background:#b17b20;
          }
    
          #sb-fa-stop {
            background:#bd3333;
          }
        `;
    
        document.head.appendChild(style);
        document.body.appendChild(panel);
    
        $('#sb-fa-start').addEventListener('click', start);
        $('#sb-fa-pause').addEventListener('click', togglePause);
        $('#sb-fa-stop').addEventListener('click', stop);
    
        updateButtons();
      }
    
    
      function init() {
        if (!document.body) {
          setTimeout(init, 300);
          return;
        }
    
        // 不在游戏启动页创建面板
        if (gameReady()) {
          createPanel();
          setStatus('游戏已加载,可以开始');
          addLog('游戏初始化完成');
          return;
        }
    
        setTimeout(() => {
          const observer = new MutationObserver(() => {
            if (gameReady()) {
              observer.disconnect();
    
              // 只有真正进入游戏后才创建面板
              createPanel();
              setStatus('游戏已加载,可以开始');
              addLog('已检测到游戏场景');
            }
          });
    
          observer.observe(document.body, {
            childList: true,
            subtree: true
          });
    
          // 防止游戏在监听器建立前刚好加载完成
          if (gameReady()) {
            observer.disconnect();
            createPanel();
            setStatus('游戏已加载,可以开始');
          }
        }, 500);
      }
    
      init();
    
    
    })();
    
    
    收藏支持 1反对打赏 1
    个性签名

    学习的能力和方法比知识本身更重要

1 人打赏,共 1 饼 crazypeace 1

2 条回复

发表回复