您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
LiveSplit Auto-Split support for Stimulation Clicker!
// ==UserScript== // @name Stimulation Splitter // @namespace mikarific.com // @description LiveSplit Auto-Split support for Stimulation Clicker! // @icon https://raw.githubusercontent.com/Mikarific/StimulationSplitter/main/assets/userscript/icon.png // @icon64 https://raw.githubusercontent.com/Mikarific/StimulationSplitter/main/assets/userscript/icon64.png // @version 0.1.1 // @author Mikarific // @match https://neal.fun/* // @run-at document-start // @noframes // @inject-into browser // @sandbox raw // @connect github.com // @supportURL https://discord.gg/Ka4ww68xnY // @homepageURL https://discord.gg/Ka4ww68xnY // @license MIT // @grant GM.getValue // @grant GM.registerMenuCommand // @grant GM.setValue // ==/UserScript== (function () { 'use strict'; if (window.location.hostname === 'neal.fun') { let finishedLoading = false; window.history.pushState = new Proxy(history.pushState, { apply: (target, thisArg, argsList) => { if (argsList[2] !== undefined && finishedLoading) { const newURL = new URL(argsList[2], document.baseURI); const isStimulationClicker = newURL.hostname === 'neal.fun' && newURL.pathname === '/stimulation-clicker/'; if (isStimulationClicker) location.replace(newURL); } return Reflect.apply(target, thisArg, argsList); } }); window.history.replaceState = new Proxy(history.replaceState, { apply: (target, thisArg, argsList) => { if (argsList[2] !== undefined && finishedLoading) { const newURL = new URL(argsList[2], document.baseURI); const isStimulationClicker = newURL.hostname === 'neal.fun' && newURL.pathname === '/stimulation-clicker/'; if (isStimulationClicker) location.replace(newURL); } return Reflect.apply(target, thisArg, argsList); } }); window.addEventListener('popstate', () => { if (finishedLoading) { const newURL = new URL(window.location.href); const isStimulationClicker = newURL.hostname === 'neal.fun' && newURL.pathname === '/stimulation-clicker/'; if (isStimulationClicker) location.replace(newURL); } }); if (document.readyState === 'interactive') { finishedLoading = true; } else { window.addEventListener('DOMContentLoaded', () => { finishedLoading = true; }, { once: true }); } } function domPromise(elem) { return new Promise(resolve => { if (window.location.hostname === 'neal.fun' && window.location.pathname === '/stimulation-clicker/') { if (document.readyState === 'interactive') { resolve(elem()); } else { window.addEventListener('DOMContentLoaded', () => { resolve(elem()); }, { once: true }); } } else { resolve(null); } }); } const container = domPromise(() => document.querySelector('.container')); const dom = { container }; function getVueState(container, resolve) { if (container.__vue__ !== undefined) { const vueState = container.__vue__.stimulation === undefined ? container.__vue__.$children.find(child => child.stimulation !== undefined) : container.__vue__; resolve(vueState); } else { Object.defineProperty(container, '__vue__', { set(vueState) { if (vueState !== null) { resolve(vueState); Object.defineProperty(container, '__vue__', { value: vueState, writable: true, configurable: true, enumerable: true }); } }, configurable: true, enumerable: true }); } } const state = new Promise(resolve => { if (window.location.hostname === 'neal.fun' && window.location.pathname === '/stimulation-clicker/') { if (document.readyState === 'complete') { dom.container.then(async container => getVueState(await container, resolve)); } else { window.addEventListener('load', () => { dom.container.then(async container => getVueState(await container, resolve)); }, { once: true }); } } else { resolve(null); } }); async function startLiveSplit() { const livesplitServer = new WebSocket('ws://127.0.0.1:16834/livesplit'); livesplitServer.onopen = () => { livesplitServer.send('reset'); }; function getSplitIndex() { return new Promise(resolve => { livesplitServer.onmessage = event => { livesplitServer.onmessage = null; resolve(parseInt(event.data)); }; livesplitServer.send('getsplitindex'); }); } async function split(splitTo) { let splitIndex = await getSplitIndex(); for (let i = Math.max(splitIndex, 0); i < splitTo; i++) { livesplitServer.send('skipsplit'); splitIndex++; } if (splitIndex === splitTo) livesplitServer.send('split'); } const vueState = await state; // When stimulation is first shown, start the timer const { set: showStimulationSetter } = Object.getOwnPropertyDescriptor(vueState, 'showStimulation'); Object.defineProperty(vueState, 'showStimulation', { set(showStimulation) { if (!vueState.showStimulation && showStimulation) livesplitServer.send('starttimer'); return showStimulationSetter.call(this, showStimulation); }, configurable: true, enumerable: true }); // When Hydraulic Press is purchased, split to index 0 const { set: showHydraulicPressSetter } = Object.getOwnPropertyDescriptor(vueState, 'showHydraulicPress'); Object.defineProperty(vueState, 'showHydraulicPress', { set(showHydraulicPress) { if (!vueState.showHydraulicPress && showHydraulicPress) split(0); return showHydraulicPressSetter.call(this, showHydraulicPress); }, configurable: true, enumerable: true }); // When Levels is purchased, split to index 1 const { set: showLevelsSetter } = Object.getOwnPropertyDescriptor(vueState, 'showLevels'); Object.defineProperty(vueState, 'showLevels', { set(showLevels) { if (!vueState.showLevels && showLevels) split(1); return showLevelsSetter.call(this, showLevels); }, configurable: true, enumerable: true }); // When Stock Market is purchased, split to index 2 const { set: showStockMarketSetter } = Object.getOwnPropertyDescriptor(vueState, 'showStockMarket'); Object.defineProperty(vueState, 'showStockMarket', { set(showStockMarket) { if (!vueState.showStockMarket && showStockMarket) split(2); return showStockMarketSetter.call(this, showStockMarket); }, configurable: true, enumerable: true }); // When Email is purchased, split to index 3 const { set: inboxUnlockedSetter } = Object.getOwnPropertyDescriptor(vueState, 'inboxUnlocked'); Object.defineProperty(vueState, 'inboxUnlocked', { set(inboxUnlocked) { if (!vueState.inboxUnlocked && inboxUnlocked) split(3); return inboxUnlockedSetter.call(this, inboxUnlocked); }, configurable: true, enumerable: true }); // When Crypto is purchased, split to index 4 const { set: cryptoUnlockedSetter } = Object.getOwnPropertyDescriptor(vueState, 'cryptoUnlocked'); Object.defineProperty(vueState, 'cryptoUnlocked', { set(cryptoUnlocked) { if (!vueState.cryptoUnlocked && cryptoUnlocked) split(4); return cryptoUnlockedSetter.call(this, cryptoUnlocked); }, configurable: true, enumerable: true }); // When Leverage is purchased, split to index 5 const { set: stockLeverageSetter } = Object.getOwnPropertyDescriptor(vueState, 'stockLeverage'); Object.defineProperty(vueState, 'stockLeverage', { set(stockLeverage) { if (vueState.stockLeverage === 1 && stockLeverage === 2) split(5); return stockLeverageSetter.call(this, stockLeverage); }, configurable: true, enumerable: true }); // When Subway Surfers Wormhole is purchased, split to index 6 vueState.startWormhole = new Proxy(vueState.startWormhole, { apply: (target, thisArg, argsList) => { split(6); return Reflect.apply(target, thisArg, argsList); } }); // When Go to the Ocean is purchased, split to index 7 vueState.endGame = new Proxy(vueState.endGame, { apply: (target, thisArg, argsList) => { split(7); return Reflect.apply(target, thisArg, argsList); } }); } if (window.location.hostname === 'neal.fun' && window.location.pathname === '/stimulation-clicker/') { if (document.readyState === 'complete') { startLiveSplit(); } else { window.addEventListener('load', startLiveSplit, { once: true }); } } async function patchDVDs() { if (await GM.getValue('dvdStandardization', true)) { const vueState = await state; const bgState = vueState.$refs.bg; const renderer = bgState.$refs.renderer; renderer.style.width = '1920px'; renderer.style.height = '1080px'; renderer.style.position = 'fixed'; renderer.style.top = '50%'; renderer.style.left = '50%'; renderer.style.transform = 'translate(-50%, -50%)'; // We don't have direct access to updateDVDs as it's not part of vue data, // but we can set the size of the window to 1920x1080 before the dvd hits // calculation by setting them before the start of bgAnimationLoop, and // resetting them after the bgAnimationLoop function has executed. vueState.bgAnimationLoop = new Proxy(vueState.bgAnimationLoop, { apply: (target, thisArg, argsList) => { const realWidth = window.innerWidth; const realHeight = window.innerHeight; window.innerWidth = 1920; window.innerHeight = 1080; const returnValue = Reflect.apply(target, thisArg, argsList); window.innerWidth = realWidth; window.innerHeight = realHeight; return returnValue; } }); // This is purely for the visuals, actually renders the DVDs at the size of the canvas (1920x1080) bgState.resize(); } } if (window.location.hostname === 'neal.fun' && window.location.pathname === '/stimulation-clicker/') { if (document.readyState === 'complete') { patchDVDs(); } else { window.addEventListener('load', patchDVDs, { once: true }); } } GM.registerMenuCommand('Toggle DVD Standardization', async () => { const dvdStandardization = await GM.getValue('dvdStandardization', true); GM.setValue('dvdStandardization', !dvdStandardization); if (dvdStandardization) alert('DVD Standardization has been turned OFF'); if (!dvdStandardization) alert('DVD Standardization has been turned ON'); location.reload(); }); })();