Greasy Fork

Greasy Fork is available in English.

榭洛科特的背包 Beta

榭洛科特的背包测试版,仅控制浏览器,不对游戏进行任何操作。Sierokarte's BackPack Beta Version. This script only effect browser, do nothing to game.

当前为 2025-07-31 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name                榭洛科特的背包 Beta
// @version             0.1.0
// @author              Alk
// @license             GPL-3.0
// @description         榭洛科特的背包测试版,仅控制浏览器,不对游戏进行任何操作。Sierokarte's BackPack Beta Version. This script only effect browser, do nothing to game.
// @match               *.granbluefantasy.jp
// @grant               unsafeWindow
// @grant               GM_registerMenuCommand
// @grant               GM_setValue
// @grant               GM_getValue
// @run-at              document-start
// @namespace           http://greasyfork.icu/users/1455240
// ==/UserScript==

(function() {
  'use strict';

  // 战斗结果 JSON 文件名集合
  const battleSet = new Set([
    'normal_attack_result.json',
    'ability_result.json',
    'fatal_chain_result.json',
    'summon_result.json'
  ]);

  // 配置默认值
  const defaults = {
    refresh_attack: true,
    hide_left_sb: false,
    hide_right_sb: false,
    refresh_koenig_dekret: true,
    refresh_coronal_ejection: false,
    refresh_secret_triad: false,
    hunter_mode: false,
    replace_refresh_with_back: false,
    allow_auto_redirect: false,
    auto_redirect_hash: ''
  };

  // 获取/切换 配置
  function getCfg(key) {
    return GM_getValue(key, defaults[key]);
  }
  function toggleCfg(key) {
    const val = !getCfg(key);
    GM_setValue(key, val);
    return val;
  }

  // 注册右键菜单
  const labels = {
    refresh_attack: '攻击后自动刷新',
    hide_left_sb: '关闭左侧边栏',
    hide_right_sb: '关闭右侧边栏',
    refresh_koenig_dekret: '帝王法令刷新',
    refresh_coronal_ejection: '日冕喷发刷新',
    refresh_secret_triad: '万事皆三刷新',
    hunter_mode: '猎金模式',
    replace_refresh_with_back: '替换刷新为后退',
    allow_auto_redirect: '自动跳转'
  };
  Object.entries(labels).forEach(([key, label]) => {
    GM_registerMenuCommand(
      `${label}:${getCfg(key) ? '已开启' : '已关闭'}`,
      () => toggleCfg(key)
    );
  });

  // 页面加载后隐藏侧栏
  window.addEventListener('load', () => {
    if (!/mobile/.test(navigator.userAgent.toLowerCase())) {
      if (getCfg('hide_left_sb')) {
        document.body.firstElementChild?.firstElementChild?.remove();
      }
    }
    if (getCfg('hide_right_sb')) {
      document.querySelector('#submenu')?.remove();
    }
  });

  // 自动跳转
  if (getCfg('allow_auto_redirect')) {
    const doRedirect = () => {
      if (location.hash.startsWith('#result_multi')) {
        const target = GM_getValue('auto_redirect_hash', defaults.auto_redirect_hash);
        if (target) location.href = `${location.origin}/${target}`;
      }
    };
    window.addEventListener('load', () => setTimeout(doRedirect, Math.random() * 1000 + 100));
    window.addEventListener('popstate', () => setTimeout(doRedirect, Math.random() * 1000 + 100));
  }

  // 跳转控制
  function reload() {
    if (getCfg('replace_refresh_with_back')) return goBack();
    setTimeout(() => location.reload(), Math.random() * 150 + 50);
  }
  function goBack() {
    setTimeout(() => history.go(-1), Math.random() * 150 + 50);
  }

  // 战斗处理逻辑
  function handleBattle(scenario, urlKey) {
    const winObj = scenario.find(o => o.cmd === 'win');
    const winStatus = winObj
      ? (winObj.is_last_raid === 1 ? 'raid' : 'battle')
      : 'continue';

    if (winStatus === 'raid') return goBack();
    if (winStatus === 'battle') return reload();
    if (urlKey === 'normal_attack_result.json' && getCfg('refresh_attack')) return reload();

    if (urlKey === 'ability_result.json') {
      const ability = scenario.find(o => o.cmd === 'ability');
      if (ability) {
        if ((getCfg('refresh_koenig_dekret') && ability.name === 'ケーニヒ・ベシュテレン') ||
            (getCfg('refresh_coronal_ejection') && ability.name.includes('攻撃行動') && ability.name.includes('2回')) ||
            (getCfg('refresh_secret_triad') && ability.name === 'シークレットトライアド')) {
          return reload();
        }
      }
    }

    if ((urlKey === 'fatal_chain_result.json' || urlKey === 'summon_result.json') && getCfg('refresh_attack')) {
      return reload();
    }
  }

  // 拦截 XHR
  const origSend = unsafeWindow.XMLHttpRequest.prototype.send;
  unsafeWindow.XMLHttpRequest.prototype.send = function(...args) {
    this.addEventListener('load', () => {
      try {
        const url = new URL(this.responseURL);
        const key = url.pathname.split('/')[3];
        if (!battleSet.has(key)) return;
        const resp = JSON.parse(this.responseText);
        const scenario = resp.scenario || resp;
        handleBattle(scenario, key);
      } catch (e) {
        console.warn('脚本出错:', e);
      }
    });
    origSend.apply(this, args);
  };
})();