Stimulation Splitter

LiveSplit Auto-Split support for Stimulation Clicker!

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==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();
});

})();