您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds "Save current rig layout" & "Empty rig layout" quick actions to a Cracking crime.
当前为
// ==UserScript== // @name Torn Rigs Layout Switcher // @namespace https://github.com/SOLiNARY // @version 0.2.1 // @description Adds "Save current rig layout" & "Empty rig layout" quick actions to a Cracking crime. // @author Ramin Quluzade, Silmaril [2665762] // @license MIT License // @match https://www.torn.com/loader.php?sid=crimes* // @match https://www.torn.com/loader.php?sid=crimes#/cracking // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @grant none // @run-at document-start // ==/UserScript== (async function() { 'use strict'; const rfcvArg = "rfcv="; const isTampermonkeyEnabled = typeof unsafeWindow !== 'undefined'; let rfcv = localStorage.getItem("silmaril-rigs-layout-switcher-rfcv") ?? null; const crimesReadUrl = '/loader.php?sid=crimesData&step=crimesList&typeID=10&'; const crimesWriteUrl = '/loader.php?sid=crimesData&step=prepare&typeID=10&crimeID=204&value1='; const emptyLayout = `[ {"x": 0, "y": 0, "item": null}, {"x": 1, "y": 0, "item": null}, {"x": 2, "y": 0, "item": null}, {"x": 3, "y": 0, "item": null}, {"x": 4, "y": 0, "item": null}, {"x": 0, "y": 1, "item": null}, {"x": 1, "y": 1, "item": null}, {"x": 2, "y": 1, "item": null}, {"x": 3, "y": 1, "item": null}, {"x": 4, "y": 1, "item": null}, {"x": 0, "y": 2, "item": null}, {"x": 1, "y": 2, "item": null}, {"x": 2, "y": 2, "item": null}, {"x": 3, "y": 2, "item": null}, {"x": 4, "y": 2, "item": null}, {"x": 0, "y": 3, "item": null}, {"x": 1, "y": 3, "item": null}, {"x": 2, "y": 3, "item": null}, {"x": 3, "y": 3, "item": null}, {"x": 4, "y": 3, "item": null}, {"x": 0, "y": 4, "item": null}, {"x": 1, "y": 4, "item": null}, {"x": 2, "y": 4, "item": null}, {"x": 3, "y": 4, "item": null}, {"x": 4, "y": 4, "item": null}]`; let rfcvUpdatedThisSession = false; let mutationFound = false; let panelAdded = false; let rigLayouts = localStorage.getItem("silmaril-rigs-layout-switcher-layouts") ?? ""; let rigLayoutsArray = rigLayouts.split(','); // console.log('rigLayouts', rigLayouts, rigLayoutsArray); let currentRig = 0; let rigsInfo = {}; let requestsQueue = []; const { fetch: originalFetch } = isTampermonkeyEnabled ? unsafeWindow : window; const customFetch = async (...args) => { let [resource, config] = args; let response = await originalFetch(resource, config); let fetchUrl = response.url; if (fetchUrl.indexOf(crimesReadUrl) >= 0 || fetchUrl.indexOf(crimesWriteUrl) >= 0) { try { const jsonData = await response.clone().json(); const rig = fetchUrl.indexOf(crimesReadUrl) >= 0 ? jsonData.DB.crimesByType.rig : jsonData.DB.additionalInfo.prepareInfo.rig; rig.chassis.forEach((rigData, rigId) => { // console.log('rigData', rigData); // console.log('rigId', rigId); let items = []; rigData.components.forEach((componentData) => { if (componentData.ID == 0) { return; } let componentInfo = new ComponentInfo(componentData.coords[0].x, componentData.coords[0].y, componentData.ID); if (componentData.coords[1] != null) { componentInfo.x2 = componentData.coords[1].x; componentInfo.y2 = componentData.coords[1].y; } items.push(componentInfo); }); rigsInfo[rigId] = items; }); // console.log('rigsInfo', rigsInfo); } catch (error) { console.log('[TornRigsLayoutSwitcher] No targets, skipping the script init', error); } } if (rfcvUpdatedThisSession) { return response; } if (!rfcvUpdatedThisSession){ let rfcvIdx = fetchUrl.indexOf(rfcvArg); if (rfcvIdx >= 0){ rfcv = fetchUrl.substr(rfcvIdx + rfcvArg.length); localStorage.setItem("silmaril-loadout-switcher-rfcv", rfcv); document.querySelectorAll("div.silmaril-torn-rigs-layout-switcher-container button").forEach((button) => button.classList.remove("disabled")); rfcvUpdatedThisSession = true; } } return response; }; if (isTampermonkeyEnabled){ unsafeWindow.fetch = customFetch; } else { window.fetch = customFetch; } const styles = ` div.silmaril-torn-rigs-layout-switcher-container { display: inline-flex; align-items: center; margin-left: 5px; } .wave-animation { position: relative; overflow: hidden; } .wave { pointer-events: none; position: absolute; width: 100%; height: 33px; background-color: transparent; opacity: 0; transform: translateX(-100%); animation: waveAnimation 3s cubic-bezier(0, 0, 0, 1); } @keyframes waveAnimation { 0% { opacity: 1; transform: translateX(-100%); } 100% { opacity: 0; transform: translateX(100%); } } `; if (isTampermonkeyEnabled){ GM_addStyle(styles); } else { let style = document.createElement("style"); style.type = "text/css"; style.innerHTML = styles; while (document.head == null){ await sleep(50); } document.head.appendChild(style); } const setLayoutUrl = "/loader.php?sid=crimesData&step=prepare&typeID=10&crimeID=204&value1={layout}&rfcv={rfcv}"; const layoutTemplate = `{"step":"{step}","chassisID":{rig},"ID":{component},"coords":[{shortComponent}{longComponent}]}`; const coordinatesTemplate = `{"x":{xCoordinate},"y":{yCoordinate}}`; const add = 'add'; const remove = 'remove'; const componentIds = { "eCPU": 1, "CPU": 2, "HPCPU": 3, "Fan": 4, "Water Block": 5, "Heat Sink": 6, "PSU": 7, "None": 0 }; const observerTarget = document.querySelector("html"); const observerConfig = { attributes: false, childList: true, characterData: false, subtree: true }; const observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutationItem) { if (mutationFound || panelAdded){ observer.disconnect(); return; } let mutation = mutationItem.target; if (mutation.classList == "crime-root cracking-root") { // console.log('MATCHED RIG', mutation.querySelector('div.rig___aY5rF')); const rigDiv = mutation.querySelector('div.rig___aY5rF'); if (rigDiv == null) { // console.log('no rig found'); return; } // console.log('Rig Found!'); mutationFound = true; observer.disconnect(); const buttonContainer = document.createElement('div'); buttonContainer.className = 'silmaril-rigs-layout-switcher-templates'; const waveDiv = document.createElement('div'); waveDiv.className = 'wave'; buttonContainer.appendChild(waveDiv); addLayoutsAndSettingButtons(buttonContainer); const spamButton = document.createElement('button'); spamButton.id = 'silmaril-rigs-layout-switcher-spam-button'; spamButton.type = 'button'; spamButton.className = 'torn-btn non-deletable disabled'; spamButton.textContent = 'No layout chosen'; spamButton.addEventListener('click', async event => { if (event.target.classList.contains('disabled')){ return; } const urlToProcess = requestsQueue.shift(); if (requestsQueue.length == 0) { event.target.classList.add('disabled'); event.target.textContent = 'All requests completed!'; } else { event.target.textContent = `Spam click ${requestsQueue.length} more times!`; } await sendSetLayoutRequest(urlToProcess); }); if (!panelAdded){ mutation.querySelector('div.currentCrime___KNKYQ').append(getRigChoices(), getLayoutActions(mutation), buttonContainer, spamButton); panelAdded = true; } } }); }); observer.observe(observerTarget, observerConfig); function addLayoutsAndSettingButtons(root){ const empty = document.createElement('button'); empty.type = 'button'; empty.className = 'torn-btn non-deletable'; empty.textContent = 'Empty'; empty.setAttribute('data-action', 'remove'); empty.addEventListener('click', () => {handleLoadoutClick(root)}); root.appendChild(empty); addLayoutButtons(root); } function getRigChoices(){ const rigChoiceWrapper = document.createElement('ul'); rigChoiceWrapper.role = 'tablist'; rigChoiceWrapper.className = 'torn-tabs tabs-dark ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all'; const rigChoice1 = document.createElement('li'); rigChoice1.id = 'silmaril-rigs-layout-switcher-choice-1'; rigChoice1.role = 'tab'; rigChoice1.ariaSelected = 'true'; rigChoice1.setAttribute('data-disable', '0'); rigChoice1.className = 'ui-state-default ui-corner-top ui-tabs-active ui-state-active'; rigChoice1.addEventListener('click', event => { // console.log('event', event); currentRig = 0; event.target.parentNode.classList.add('ui-tabs-active'); event.target.parentNode.classList.add('ui-state-active'); let rig2 = document.getElementById('silmaril-rigs-layout-switcher-choice-2'); rig2.classList.remove('ui-tabs-active'); rig2.classList.remove('ui-state-active'); let rig3 = document.getElementById('silmaril-rigs-layout-switcher-choice-3'); rig3.classList.remove('ui-tabs-active'); rig3.classList.remove('ui-state-active'); // console.log('currentRig', currentRig); }); const rigChoiceLink1 = document.createElement('a'); rigChoiceLink1.innerText = 'Chassis #1'; rigChoice1.append(rigChoiceLink1); const rigChoice2 = document.createElement('li'); rigChoice2.id = 'silmaril-rigs-layout-switcher-choice-2'; rigChoice2.role = 'tab'; rigChoice2.ariaSelected = 'false'; rigChoice2.setAttribute('data-disable', '0'); rigChoice2.className = 'ui-state-default ui-corner-top'; rigChoice2.addEventListener('click', event => { // console.log('event', event); currentRig = 1; event.target.parentNode.classList.add('ui-tabs-active'); event.target.parentNode.classList.add('ui-state-active'); let rig1 = document.getElementById('silmaril-rigs-layout-switcher-choice-1'); rig1.classList.remove('ui-tabs-active'); rig1.classList.remove('ui-state-active'); let rig3 = document.getElementById('silmaril-rigs-layout-switcher-choice-3'); rig3.classList.remove('ui-tabs-active'); rig3.classList.remove('ui-state-active'); // console.log('currentRig', currentRig); }); const rigChoiceLink2 = document.createElement('a'); rigChoiceLink2.innerText = 'Chassis #2'; rigChoice2.append(rigChoiceLink2); const rigChoice3 = document.createElement('li'); rigChoice3.id = 'silmaril-rigs-layout-switcher-choice-3'; rigChoice3.role = 'tab'; rigChoice3.ariaSelected = 'false'; rigChoice3.setAttribute('data-disable', '0'); rigChoice3.className = 'ui-state-default ui-corner-top'; rigChoice3.addEventListener('click', event => { // console.log('event', event); currentRig = 2; event.target.parentNode.classList.add('ui-tabs-active'); event.target.parentNode.classList.add('ui-state-active'); let rig1 = document.getElementById('silmaril-rigs-layout-switcher-choice-1'); rig1.classList.remove('ui-tabs-active'); rig1.classList.remove('ui-state-active'); let rig2 = document.getElementById('silmaril-rigs-layout-switcher-choice-2'); rig2.classList.remove('ui-tabs-active'); rig2.classList.remove('ui-state-active'); // console.log('currentRig', currentRig); }); const rigChoiceLink3 = document.createElement('a'); rigChoiceLink3.innerText = 'Chassis #3'; rigChoice3.append(rigChoiceLink3); rigChoiceWrapper.append(rigChoice1, rigChoice2, rigChoice3); // console.log('rigChoiceWrapper', rigChoiceWrapper); return rigChoiceWrapper; } function getLayoutActions(root){ const actionsContainer = document.createElement('div'); actionsContainer.className = 'silmaril-torn-rigs-layout-switcher-container'; const saveCurrentLayout = document.createElement('button'); saveCurrentLayout.type = 'button'; saveCurrentLayout.className = 'torn-btn'; saveCurrentLayout.textContent = 'Save current layout'; saveCurrentLayout.addEventListener('click', () => { // console.log('save current layout'); let userInput = prompt("Please enter a unique name for a layout (DO NOT USE COMMAS ',')" ?? '').toLowerCase(); if (userInput !== null && userInput.length > 0) { rigLayouts = localStorage.getItem("silmaril-rigs-layout-switcher-layouts") ?? ""; rigLayoutsArray = rigLayouts.split(','); if (!rigLayoutsArray.includes(userInput)) { rigLayoutsArray.push(userInput); rigLayouts = rigLayoutsArray.join(','); localStorage.setItem("silmaril-rigs-layout-switcher-layouts", rigLayouts); } localStorage.setItem(`silmaril-rigs-layout-switcher-layout-${userInput}`, JSON.stringify(rigsInfo[currentRig])); root.querySelectorAll("div.silmaril-rigs-layout-switcher-templates > button:not(.non-deletable)").forEach((item) => item.remove()); addLayoutButtons(root.querySelector('div.currentCrime___KNKYQ > div.silmaril-rigs-layout-switcher-templates')); } else { console.error("[TornBazaarFiller] User cancelled the layout naming input."); } }); const deleteLayout = document.createElement('button'); deleteLayout.type = 'button'; deleteLayout.className = 'torn-btn'; deleteLayout.textContent = 'Delete layout'; deleteLayout.addEventListener('click', () => { // console.log('delete layout'); let userInput = prompt("Please enter a name of a layout to delete" ?? '').toLowerCase(); if (userInput !== null && userInput.length > 0) { rigLayouts = localStorage.getItem("silmaril-rigs-layout-switcher-layouts") ?? ""; rigLayoutsArray = rigLayouts.split(','); rigLayoutsArray = rigLayoutsArray.filter(item => item !== userInput); rigLayouts = rigLayoutsArray.join(','); localStorage.setItem("silmaril-rigs-layout-switcher-layouts", rigLayouts); localStorage.removeItem(`silmaril-rigs-layout-switcher-layout-${userInput}`); root.querySelectorAll("div.silmaril-rigs-layout-switcher-templates > button:not(.non-deletable)").forEach((item) => item.remove()); addLayoutButtons(root.querySelector('div.currentCrime___KNKYQ > div.silmaril-rigs-layout-switcher-templates')); } else { console.error("[TornBazaarFiller] User cancelled the layout naming input."); } }); actionsContainer.append(saveCurrentLayout, deleteLayout); return actionsContainer; } async function addLayoutButtons(root){ rigLayoutsArray.forEach((layout) => { if (layout == ''){ return; } const button = document.createElement('button'); button.type = 'button'; button.className = rfcv === null ? 'torn-btn disabled' : 'torn-btn'; button.textContent = layout; button.setAttribute('data-action', 'add'); button.addEventListener('click', () => {handleLoadoutClick(root, layout)}); root.appendChild(button); }) } async function handleLoadoutClick(root, layoutName = null){ let layout = layoutName == null ? emptyLayout : localStorage.getItem(`silmaril-rigs-layout-switcher-layout-${layoutName}`); let action = event.target.getAttribute('data-action'); if (event.target.classList.contains('disabled')){ return; } const layoutItems = JSON.parse(layout); // console.log('layout', layoutItems); await saveSetLayoutRequestsToQueue(action, currentRig, layoutItems, root); } function saveSetLayoutRequestsToQueue(action, rig, items, root){ // console.log('items', items); requestsQueue = []; const urlWithRfcv = setLayoutUrl.replace("{rfcv}", rfcv); items.forEach((item) => { const coordinates = coordinatesTemplate.replace("{xCoordinate}", item.x).replace("{yCoordinate}", item.y); let layoutUrl2 = layoutTemplate.replace("{step}", action).replace("{rig}", rig).replace("{component}", item.item).replace("{shortComponent}", coordinates); let layoutUrl; if (item.x2 != null) { const coordinatesLong = coordinatesTemplate.replace("{xCoordinate}", item.x2).replace("{yCoordinate}", item.y2); layoutUrl = layoutUrl2.replace("{longComponent}", `,${coordinatesLong}`); } else { layoutUrl = layoutUrl2.replace("{longComponent}", ""); } const url = urlWithRfcv.replace("{layout}", layoutUrl); requestsQueue.push(url); }); let spamButton = document.getElementById('silmaril-rigs-layout-switcher-spam-button'); spamButton.textContent = `Spam click ${requestsQueue.length} more times!`; spamButton.classList.remove('disabled'); } async function sendSetLayoutRequest(url){ await fetch(url, { method: 'GET', }) .then(response => { if (response.ok) { console.log('component change request OK'); } else { console.error("[TornRigsLayoutSwitcher] Set Layout request failed:", response); } }) .catch(error => { console.error("[TornRigsLayoutSwitcher] Error setting Layout:", error); }); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } class ComponentInfo { constructor(x, y, item, x2 = null, y2 = null) { this.x = x; this.y = y; this.item = item; this.x2 = x2; this.y2 = y2; } } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址