WhatBeatsRock Save Loader

Load saves for whatbeatsrock.com with pause functionality, adjustable wait time, file-based save selection, improved UI with padding, and edge-only dragging with scrollable middle

  1. // ==UserScript==
  2. // @name WhatBeatsRock Save Loader
  3. // @namespace http://violentmonkey.net/
  4. // @version 3.0
  5. // @description Load saves for whatbeatsrock.com with pause functionality, adjustable wait time, file-based save selection, improved UI with padding, and edge-only dragging with scrollable middle
  6. // @author JoTheStupid
  7. // @match https://www.whatbeatsrock.com/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. let paused = false;
  16. let saveEntries = [];
  17. let index = 0;
  18. let isDragging = false;
  19. let dragStartX, dragStartY, startX, startY;
  20.  
  21. // UI Styles
  22. const buttonStyle = `
  23. padding: 8px 16px;
  24. margin-top: 10px;
  25. background-color: #4CAF50;
  26. border: 2px solid #333;
  27. color: white;
  28. cursor: pointer;
  29. font-size: 14px;
  30. border-radius: 5px;
  31. text-align: center;
  32. `;
  33. const pauseButtonStyle = `
  34. padding: 8px 16px;
  35. margin-top: 10px;
  36. background-color: #f44336;
  37. border: 2px solid #333;
  38. color: white;
  39. cursor: pointer;
  40. font-size: 14px;
  41. border-radius: 5px;
  42. text-align: center;
  43. `;
  44. const containerStyle = `
  45. padding: 10px;
  46. background-color: white;
  47. border: 2px solid black;
  48. border-radius: 10px;
  49. position: fixed;
  50. top: 10px;
  51. right: 10px;
  52. width: 320px;
  53. height: 500px;
  54. z-index: 1000;
  55. resize: both;
  56. overflow: auto;
  57. min-width: 200px;
  58. min-height: 200px;
  59. `;
  60. const edgeThreshold = 10; // The distance from the edge in pixels to allow dragging
  61.  
  62. // Create a container for input elements
  63. let container = document.createElement('div');
  64. container.style.cssText = containerStyle;
  65. document.body.appendChild(container);
  66.  
  67. // Handle dragging the container (only from edges)
  68. container.addEventListener('mousedown', function (e) {
  69. const rect = container.getBoundingClientRect();
  70. const isEdge = e.clientX - rect.left < edgeThreshold ||
  71. rect.right - e.clientX < edgeThreshold ||
  72. e.clientY - rect.top < edgeThreshold ||
  73. rect.bottom - e.clientY < edgeThreshold;
  74.  
  75. if (isEdge) {
  76. isDragging = true;
  77. dragStartX = e.clientX;
  78. dragStartY = e.clientY;
  79. startX = container.offsetLeft;
  80. startY = container.offsetTop;
  81. e.preventDefault();
  82. }
  83. });
  84.  
  85. document.addEventListener('mousemove', function (e) {
  86. if (isDragging) {
  87. let offsetX = e.clientX - dragStartX;
  88. let offsetY = e.clientY - dragStartY;
  89. container.style.left = startX + offsetX + 'px';
  90. container.style.top = startY + offsetY + 'px';
  91. }
  92. });
  93.  
  94. document.addEventListener('mouseup', function () {
  95. isDragging = false;
  96. });
  97.  
  98. // Create a file input for save file upload
  99. let fileInput = document.createElement('input');
  100. fileInput.type = 'file';
  101. fileInput.accept = '.txt';
  102. fileInput.style.width = '100%';
  103. fileInput.style.marginBottom = '10px';
  104. container.appendChild(fileInput);
  105.  
  106. // Create a textbox for input
  107. let inputBox = document.createElement('textarea');
  108. inputBox.style.width = '100%';
  109. inputBox.style.height = '100px';
  110. inputBox.style.padding = '5px';
  111. inputBox.style.border = '1px solid #ccc';
  112. inputBox.style.borderRadius = '5px';
  113. inputBox.style.marginBottom = '10px';
  114. container.appendChild(inputBox);
  115.  
  116. // Create a button to apply the save
  117. let enterSaveButton = document.createElement('button');
  118. enterSaveButton.innerHTML = 'Enter Save';
  119. enterSaveButton.style.cssText += buttonStyle;
  120. container.appendChild(enterSaveButton);
  121.  
  122. // Create a pause/resume button
  123. let pauseButton = document.createElement('button');
  124. pauseButton.innerHTML = 'Pause';
  125. pauseButton.style.cssText += pauseButtonStyle;
  126. container.appendChild(pauseButton);
  127.  
  128. pauseButton.addEventListener('click', () => {
  129. paused = !paused;
  130. pauseButton.innerHTML = paused ? 'Resume' : 'Pause';
  131. pauseButton.style.backgroundColor = paused ? '#f44336' : '#4CAF50';
  132. if (!paused) {
  133. processNextEntry();
  134. }
  135. });
  136.  
  137. // Create a label for the slider
  138. let sliderLabel = document.createElement('label');
  139. sliderLabel.innerHTML = 'Wait Time (ms):';
  140. container.appendChild(sliderLabel);
  141.  
  142. // Create a slider for wait time
  143. let waitTimeSlider = document.createElement('input');
  144. waitTimeSlider.type = 'range';
  145. waitTimeSlider.min = '100';
  146. waitTimeSlider.max = '3000';
  147. waitTimeSlider.value = '1000';
  148. waitTimeSlider.style.width = '100%';
  149. waitTimeSlider.style.marginBottom = '10px';
  150. container.appendChild(waitTimeSlider);
  151.  
  152. // Display the current wait time
  153. let waitTimeDisplay = document.createElement('div');
  154. waitTimeDisplay.innerHTML = `Current wait time: ${waitTimeSlider.value} ms`;
  155. waitTimeDisplay.style.marginBottom = '10px';
  156. container.appendChild(waitTimeDisplay);
  157.  
  158. // Create a div to show color-coded save options
  159. let saveListContainer = document.createElement('div');
  160. saveListContainer.style.width = '100%';
  161. saveListContainer.style.height = '150px'; // Set height and make it scrollable
  162. saveListContainer.style.overflowY = 'auto'; // Enable vertical scrolling for the save list
  163. saveListContainer.style.border = '1px solid #ccc';
  164. saveListContainer.style.borderRadius = '5px';
  165. container.appendChild(saveListContainer);
  166.  
  167. // Color cycle for the saves
  168. const colors = ['red', 'orange', 'yellow', 'blue', 'purple'];
  169.  
  170. // Handle file reading and populate the saveList with color-coded saves
  171. fileInput.addEventListener('change', (event) => {
  172. const file = event.target.files[0];
  173. if (file) {
  174. const reader = new FileReader();
  175. reader.onload = function(e) {
  176. const contents = e.target.result;
  177. const saves = contents.split('\n').map(save => save.trim()).filter(save => save !== '');
  178.  
  179. // Clear previous saves
  180. saveListContainer.innerHTML = '';
  181.  
  182. // Populate the save list with color-coded clickable divs
  183. saves.forEach((save, idx) => {
  184. let saveItem = document.createElement('div');
  185. saveItem.textContent = save;
  186. saveItem.style.cursor = 'pointer';
  187. saveItem.style.padding = '5px';
  188. saveItem.style.border = '1px solid black';
  189. saveItem.style.marginBottom = '5px';
  190. saveItem.style.backgroundColor = colors[idx % colors.length]; // Alternate colors
  191.  
  192. // Click to insert the save into the input box
  193. saveItem.addEventListener('click', () => {
  194. inputBox.value = save;
  195. });
  196.  
  197. saveListContainer.appendChild(saveItem);
  198. });
  199.  
  200. console.log('Saves loaded:', saves);
  201. };
  202. reader.readAsText(file);
  203. }
  204. });
  205.  
  206. // Update the wait time display as the slider is moved
  207. waitTimeSlider.addEventListener('input', () => {
  208. waitTimeDisplay.innerHTML = `Current wait time: ${waitTimeSlider.value} ms`;
  209. });
  210.  
  211. // Function to find the Next button
  212. function findNextButton() {
  213. return document.querySelector('button.py-4.px-8.border.border-1-black.text-lg');
  214. }
  215.  
  216. // Function to simulate typing and clicking
  217. function simulateInput(input, callback) {
  218. if (paused) return;
  219.  
  220. console.log(`Simulating input: ${input}`);
  221. let inputField = document.querySelector('.pl-4.py-4.text-lg.border.border-1-black');
  222. let submitButton = document.querySelector('.p-4.border.border-1-black.text-lg.bg-green-200, .p-4.border.border-1-black.text-lg.text-gray-400');
  223.  
  224. if (inputField && submitButton) {
  225. inputField.focus();
  226. Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set.call(inputField, input);
  227.  
  228. let inputEvent = new Event('input', { bubbles: true });
  229. let changeEvent = new Event('change', { bubbles: true });
  230. inputField.dispatchEvent(inputEvent);
  231. inputField.dispatchEvent(changeEvent);
  232.  
  233. setTimeout(() => {
  234. submitButton.click();
  235. setTimeout(() => {
  236. let nextButton = findNextButton();
  237. if (nextButton) {
  238. nextButton.click(); // Click the next button
  239. setTimeout(callback, parseInt(waitTimeSlider.value));
  240. } else {
  241. console.log('Next button not found, retrying...');
  242. setTimeout(() => simulateInput(input, callback), parseInt(waitTimeSlider.value)); // Retry if the next button is not found
  243. }
  244. }, parseInt(waitTimeSlider.value));
  245. }, parseInt(waitTimeSlider.value));
  246. } else {
  247. console.log('Input field or submit button not found, retrying...');
  248. setTimeout(() => simulateInput(input, callback), parseInt(waitTimeSlider.value));
  249. }
  250. }
  251.  
  252. // Function to enter the save from the input box and start processing it
  253. function enterSave() {
  254. let saveText = inputBox.value.trim();
  255. if (!saveText) {
  256. alert('Please enter a save');
  257. console.log('No save text entered.');
  258. return;
  259. }
  260.  
  261. saveEntries = saveText.split('🤜').map(entry => entry.trim()).reverse();
  262. index = 0;
  263. processNextEntry();
  264. }
  265.  
  266. function processNextEntry() {
  267. if (paused) return;
  268.  
  269. if (index < saveEntries.length) {
  270. let currentInput = checkTextBoxForCurrentInput();
  271. let currentIndex = saveEntries.findIndex(entry => entry.toLowerCase() === currentInput.toLowerCase());
  272.  
  273. if (currentIndex !== -1 && currentIndex + 1 < saveEntries.length) {
  274. console.log(`Current input is "${currentInput}". Inputting next entry: ${saveEntries[currentIndex + 1]}`);
  275. simulateInput(saveEntries[currentIndex + 1], () => {
  276. index = currentIndex + 2;
  277. processNextEntry();
  278. });
  279. } else {
  280. console.log(`Current input "${currentInput}" does not match any expected input. Retrying...`);
  281. setTimeout(processNextEntry, parseInt(waitTimeSlider.value));
  282. }
  283. } else {
  284. console.log('All entries processed.');
  285. }
  286. }
  287.  
  288. function checkTextBoxForCurrentInput() {
  289. let currentInputElement = document.querySelector('.text-2xl.text-center');
  290. if (currentInputElement) {
  291. let currentInput = currentInputElement.textContent.trim();
  292. if (currentInput.endsWith('?')) {
  293. currentInput = currentInput.slice(0, -1);
  294. }
  295. return currentInput;
  296. }
  297. return null;
  298. }
  299.  
  300. // Add the Enter Save button click event
  301. enterSaveButton.addEventListener('click', () => {
  302. console.log('Enter Save button clicked.');
  303. enterSave();
  304. });
  305. })();