您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
榭洛科特的背包测试版 For Via
// ==UserScript== // @name 榭洛科特的背包Via Beta // @version 0.0.3 // @author Alk // @license GPL-3.0 // @description 榭洛科特的背包测试版 For Via // @description 仅控制浏览器,不对游戏进行任何操作。 // @description Sierokarte's BackPack Beta Version. This script only affects the browser, does nothing to GBF game. // @match https://gbf.game.mbga.jp/* // @match https://game.granbluefantasy.jp/* // @run-at document-start // @namespace sierokarte-backpack-for-via // @icon https://raw.githubusercontent.com/enorsona/thirdparty/refs/heads/master/sherurotes_carrybag.ico // ==/UserScript== (function () { 'use strict'; // 配置默认值 const options = { current: { mode: 'normal', // accepts: normal / the obj_name in modes auto_redirect_hash: '', delay_ratio: 100, contribution: 1800000 }, modes: { normal: { delay_ratio: 100, }, gold_hunt: { enabled: false, selector: 'a.btn-targeting:nth-child(1) > div:nth-child(2) > div:nth-child(3)', delay_ratio: 300, // 0 => any enemies: [ 'Lv150 プロトバハムート', 'Lv200 アーカーシャ' ] } }, ui_rework: { enabled: true, hides: { side_bar: { left: true, right: true, }, assist: { enabled: true, hp_less_than: 50, member_more_than: 30, } }, sort: { enabled: true, ascend: false, } }, features: { refresh: { attack: true, ability: { enabled: true, keywords: { koenig_dekret: { enabled: true, keys: ['ケーニヒ・ベシュテレン'] }, coronal_ejection: { enabled: true, keys: ['攻撃行動', '2回'] }, secret_triad: { enabled: true, keys: ['シークレットトライアド'] }, } } }, reload_type: 'back', // accepts: back | reload auto_back_to_assist: true, auto_redirect: { enabled: false, }, battle_set: [ 'normal_attack_result.json', 'ability_result.json', 'fatal_chain_result.json', 'summon_result.json' ] }, delays: { enabled: true, factors: [100, 100, 100], }, enums: { hash: { result: '#result', assist: '#quest/assist', raid: '#raid', }, specified: { assist: { list: '.prt-search-list', card: '.btn-multi-raid.lis-raid.search', hp_bar: '.prt-raid-gauge-inner', members: '.prt-flees-in' }, contribution: { number: 'div.lis-user:nth-child(1) > div:nth-child(4)', } } } } const functions = { event: { onGameLoad: () => { if (options.ui_rework.enabled) { functions.ui.hide.side_bar.left(); functions.ui.hide.side_bar.right(); functions.ui.hide.assist.do(); functions.ui.sort.assist.do(); functions.redirect.do(); } }, onGameUnload: () => { functions.intervals.unload(); } }, ui: { hide: { side_bar: { left: () => { if (options.ui_rework.hides.side_bar.left) { const sbl = document.querySelector('body > div:first-child > div:first-child'); if (sbl) { if (sbl.classList[0].startsWith('_')) { sbl.remove(); } } } }, right: () => { document.querySelector('#submenu')?.remove(); } }, assist: { do: () => { const is_assist = functions.check.do(options.enums.hash.assist); if (is_assist === false) return; const config = options.ui_rework.hides.assist; if (config.enabled) { functions.intervals.append('assist_filter', 200, functions.ui.hide.assist.run); } }, run: () => { const config = options.ui_rework.hides.assist; const enums = options.enums.specified.assist; const li = document.querySelector(enums.list); if (!li) return; const targets = li.querySelectorAll(enums.card); if (targets) { if (targets.length === 0) return; targets.forEach((i) => { const percentage = parseInt(i.querySelector(enums.hp_bar).style.width); const members = parseInt(i.querySelector(enums.members).innerText); const less_than = config.hp_less_than; const more_than = config.hide_member_more_than; if (percentage < less_than || members > more_than) { i.remove(); } }) } } } }, sort: { assist: { do: () => { if (options.ui_rework.sort.enabled) { functions.intervals.append('assist_sorter', 200, functions.ui.sort.assist.run); } }, run: () => { const enums = options.enums.specified.assist; const li = document.querySelector(enums.list); if (!li) return; const children = Array.from(li.children); children.sort((a, b) => { const a_hp = parseInt(a.querySelector(enums.hp_bar).style.width); const b_hp = parseInt(b.querySelector(enums.hp_bar).style.width); const a_members = parseInt(a.querySelector(enums.members).innerText); const b_members = parseInt(b.querySelector(enums.members).innerText); const use_asc = options.ui_rework.sort.ascend; if (a_hp < b_hp) return use_asc ? -1 : 1; if (a_hp > b_hp) return use_asc ? 1 : -1; if (a_members < b_members) return use_asc ? 1 : -1; if (a_members > b_members) return use_asc ? -1 : 1; }) children.forEach(child => li.appendChild(child)); }, } } }, check: { do: (hash) => { return location.hash.includes(hash); } }, game: { handle: (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 functions.redirect.back(); if (winStatus === 'battle') return functions.redirect.reload(); if (urlKey === 'normal_attack_result.json' && options.features.refresh.attack) return functions.redirect.reload(); if (urlKey === 'ability_result.json' || urlKey === 'summon_result.json') { const ability = scenario.find(o => o['cmd'] === 'ability'); if (ability) { Object.keys(options.features.refresh.ability.keywords).forEach(keyword => { const self = options.features.refresh.ability.keywords[keyword]; if (self.enabled) { self.keys.some((key) => { if (ability.name.includes(key)) return functions.redirect.reload(); }) } }) } } } }, redirect: { do: () => { let enabled = false; if (options.current.mode === 'gold_hunt') { enabled = true; } else if (options.features.auto_redirect.enabled === true) { enabled = true; } if (options.current.auto_redirect_hash === '') { enabled = false; } if (enabled) { functions.redirect.goto(options.current.auto_redirect_hash); } }, goto: (hash) => { setTimeout(() => { window.open(`${location.origin}/${hash}`, '_self') }, functions.random.get()) }, reload: () => { const reloadType = options.features.reload_type; if (reloadType === 'back') { functions.redirect.back(); } else if (reloadType === 'reload') { setTimeout(() => { location.reload(); }, functions.random.get()) } }, back: () => { setInterval(() => {history.back()}, functions.random.get()) }, contribution: { do: () => { let enabled = options.features.auto_back_to_assist; if (options.current.mode === 'gold_hunt') enabled = true; if (functions.check.do(options.enums.hash.raid)) enabled = true; if (enabled === false) return; const enums = options.enums.specified.contribution; const contribution = document.querySelector(enums.number); if (contribution) { const contribution_number = parseInt(contribution.innerText); if (contribution_number > options.current.contribution) { functions.redirect.contribution.goto(); } } else { setTimeout(functions.redirect.contribution.do, 200); } }, goto: () => { functions.redirect.goto(options.enums.hash.assist); } } }, random: { ratio: (value, ratio) => { return Math.random() * (ratio / 100 * value); }, get: () => { let result = 0; options.delays.factors.forEach(factor => { result = result + functions.random.ratio(factor, options.current.ratio); }) return result; } }, intervals: { list: {}, append: (name, timeout, callback) => { functions.intervals.list[name] = setInterval(callback, timeout); }, remove: (name) => { clearInterval(functions.intervals.list[name]); functions.intervals.list[name] = undefined; }, unload: () => { Object.keys(functions.intervals.list).forEach((name) => { functions.intervals.remove(name); }) } } } // 页面加载后隐藏侧栏 window.addEventListener('load', functions.event.onGameLoad); window.addEventListener('DOMContentLoaded', functions.event.onGameLoad); window.addEventListener('popstate', functions.event.onGameLoad); window.addEventListener('unload', functions.event.onGameUnload); window.addEventListener('sierokarte-xhr', (e) => { const {key, scenario} = e.detail; functions.game.handle(scenario, key); }); // 拦截 XHR const originalOpen = XMLHttpRequest.prototype.open; XMLHttpRequest.prototype.open = function (method, url, ...rest) { this.addEventListener('load', () => { try { const targetUrl = new URL(this.responseURL); const key = targetUrl.pathname.split('/')[3]; if (!this.responseURL.includes('.json')) return; const resp = JSON.parse(this.responseText); window.dispatchEvent(new CustomEvent('sierokarte-xhr', { detail: {key, scenario: resp['scenario'] || resp} })); } catch (e) { console.log(e); } }); return originalOpen.call(this, method, url, ...rest); }; })();