MiniClient - Syringe

Enhance your MiniBlox experience

  1. // ==UserScript==
  2. // @name MiniClient - Syringe
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1
  5. // @description Enhance your MiniBlox experience
  6. // @author Syringe (Drapinqs)
  7. // @match https://miniblox.io/*
  8. // @icon https://cdn.discordapp.com/attachments/741464074986192918/1266539478781136958/miniclient75.png?ex=66a5844e&is=66a432ce&hm=8b9f2b3837225050260a044829102506f44cc6fd6b8ffa706052de573e626feb&
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @grant GM_addStyle
  12. // @run-at document-start
  13. // ==/UserScript==
  14.  
  15. (function() {
  16. 'use strict';
  17.  
  18. // Initial settings
  19. if (!GM_getValue("settings")) {
  20. GM_setValue("settings", JSON.stringify({
  21. inverted: false,
  22. showFps: true, // Set default to true to show FPS by default
  23. showCrosshair: false,
  24. crosshairUrl: '',
  25. imageUrl: ''
  26. }));
  27. }
  28.  
  29. // Add styles
  30. GM_addStyle(`
  31. .mbclient-settings {
  32. display: flex;
  33. padding: 10px;
  34. flex-direction: column;
  35. width: 95%;
  36. color: white;
  37. text-shadow: 1px 1px 0 black;
  38. background-color: rgba(31, 31, 31, 0.9);
  39. border: 3px solid rgb(174, 0, 255);
  40. border-radius: 10px;
  41. cursor: move;
  42. }
  43.  
  44. .mbclient-setting {
  45. display: flex;
  46. justify-content: space-between;
  47. align-items: center;
  48. width: full;
  49. padding: 0.25rem;
  50. }
  51.  
  52. .checkbox-round {
  53. width: 1.3em;
  54. height: 1.3em;
  55. background-color: rgb(255, 87, 87);
  56. border-radius: 20%;
  57. vertical-align: middle;
  58. border: 1px solid #ddd;
  59. appearance: none;
  60. -webkit-appearance: none;
  61. outline: none;
  62. cursor: pointer;
  63. transition: all ease 0.3s;
  64. }
  65.  
  66. .checkbox-round:checked {
  67. background-color: rgb(134, 255, 78);
  68. }
  69.  
  70. #fps-container {
  71. background-color: rgba(0, 0, 0, 0.7);
  72. color: white;
  73. padding: 5px;
  74. border: 3px solid rgb(174, 0, 255);
  75. border-radius: 5px;
  76. font-size: 14px;
  77. position: fixed;
  78. top: 4px;
  79. left: 918px;
  80. z-index: 9999;
  81. cursor: move;
  82. display: block; /* Set to block by default */
  83. }
  84.  
  85. /* Inverted colors */
  86. .inverted {
  87. filter: invert(1) hue-rotate(180deg);
  88. }
  89.  
  90. /* Crosshair */
  91. #crosshair {
  92. position: fixed;
  93. top: 50%;
  94. left: 50%;
  95. transform: translate(-50%, -50%);
  96. pointer-events: none;
  97. z-index: 9999;
  98. display: none;
  99. }
  100. `);
  101.  
  102. // Function to create settings panel
  103. function createSettingsPanel() {
  104. const settingsHTML = `
  105. <div id="settings" class="mbclient-settings">
  106. <p style="font-weight: bold; text-align: center; line-height: 95%; font-size: 140%;">MiniClient<br>by Syringe</p>
  107. <p style="text-align: center; font-size: 100%; color: rgb(219, 60, 48);"><i>use F8 to show/hide</i></p>
  108. <div class="mbclient-setting">
  109. <label for="image-url">Custom Background</label>
  110. <input style="width: 40%; border: 2px solid rgb(174, 0, 255); border-radius: 10px;" id="image-url" type="text" placeholder=" Enter image URL" />
  111. </div>
  112. <div class="mbclient-setting">
  113. <label for="crosshair-url">Custom Crosshair</label>
  114. <input style="width: 40%; border: 2px solid rgb(174, 0, 255); border-radius: 10px;" id="crosshair-url" type="text" placeholder=" Enter image URL" />
  115. </div>
  116. <div class="mbclient-setting">
  117. <label for="inverted">Inverted Colors</label>
  118. <input id="inverted" data-setting="inverted" type="checkbox" class="checkbox-round" value="false" />
  119. </div>
  120. <div class="mbclient-setting">
  121. <label for="show-fps">Show FPS</label>
  122. <input id="show-fps" data-setting="showFps" type="checkbox" class="checkbox-round" />
  123. </div>
  124. </div>
  125. `;
  126. const settingsElement = document.createElement('div');
  127. settingsElement.innerHTML = settingsHTML;
  128. settingsElement.style.position = "fixed";
  129. settingsElement.style.zIndex = "9999";
  130. settingsElement.style.top = "1rem";
  131. settingsElement.style.left = "1rem";
  132. document.body.appendChild(settingsElement);
  133.  
  134. // Make sure settingsElement exists before trying to make it draggable
  135. if (settingsElement) {
  136. makeDraggable(settingsElement, "settingsTop", "settingsLeft");
  137. }
  138.  
  139. // Add event listener for toggling the settings panel
  140. document.addEventListener('keydown', (event) => {
  141. if (event.key === "F8") {
  142. settingsElement.style.display = settingsElement.style.display === "none" ? "block" : "none";
  143. }
  144. });
  145.  
  146. // Handle image URL input
  147. handleImageUrlInput();
  148. // Handle crosshair URL input
  149. handleCrosshairUrlInput();
  150. // Handle inverted colors
  151. handleInvertedColors();
  152. // Handle Show FPS
  153. handleShowFps();
  154. }
  155.  
  156. // Function to make an element draggable
  157. function makeDraggable(element, localStorageKeyTop, localStorageKeyLeft) {
  158. let isDragging = false;
  159. let offsetX, offsetY;
  160.  
  161. element.addEventListener('mousedown', (e) => {
  162. isDragging = true;
  163. offsetX = e.clientX - element.getBoundingClientRect().left;
  164. offsetY = e.clientY - element.getBoundingClientRect().top;
  165. element.style.cursor = 'grabbing';
  166. });
  167.  
  168. document.addEventListener('mousemove', (e) => {
  169. if (isDragging) {
  170. const newX = e.clientX - offsetX;
  171. const newY = e.clientY - offsetY;
  172. const rect = element.getBoundingClientRect();
  173. const parentRect = document.documentElement.getBoundingClientRect();
  174.  
  175. // Prevent the element from being dragged out of the viewport
  176. const maxX = parentRect.width - rect.width;
  177. const maxY = parentRect.height - rect.height;
  178.  
  179. const clampedX = Math.max(0, Math.min(newX, maxX));
  180. const clampedY = Math.max(0, Math.min(newY, maxY));
  181.  
  182. element.style.left = `${clampedX}px`;
  183. element.style.top = `${clampedY}px`;
  184.  
  185. // Save the position in GM storage
  186. GM_setValue(localStorageKeyTop, `${clampedY}px`);
  187. GM_setValue(localStorageKeyLeft, `${clampedX}px`);
  188. }
  189. });
  190.  
  191. document.addEventListener('mouseup', () => {
  192. isDragging = false;
  193. element.style.cursor = 'move';
  194. });
  195.  
  196. // Restore position from GM storage
  197. const savedTop = GM_getValue(localStorageKeyTop, '4px');
  198. const savedLeft = GM_getValue(localStorageKeyLeft, '918px');
  199. element.style.top = savedTop;
  200. element.style.left = savedLeft;
  201. }
  202.  
  203. // Function to handle image URL input
  204. function handleImageUrlInput() {
  205. const urlInput = document.getElementById('image-url');
  206. const defaultImage = 'https://cdn.discordapp.com/attachments/741464074986192918/1266326290475122791/default-92b37f60.png';
  207.  
  208. if (urlInput) {
  209. const settings = JSON.parse(GM_getValue('settings'));
  210. urlInput.value = settings.imageUrl || '';
  211.  
  212. urlInput.addEventListener('input', () => {
  213. const newUrl = urlInput.value.trim();
  214. settings.imageUrl = newUrl || defaultImage;
  215. GM_setValue('settings', JSON.stringify(settings));
  216. replaceImageSource(settings.imageUrl);
  217. });
  218.  
  219. if (settings.imageUrl && settings.imageUrl !== defaultImage) {
  220. replaceImageSource(settings.imageUrl);
  221. }
  222. }
  223. }
  224.  
  225. // Function to replace image source
  226. function replaceImageSource(newUrl) {
  227. const imgElements = document.querySelectorAll('img.chakra-image');
  228. imgElements.forEach(img => {
  229. if (img.src.includes('/assets/default-92b37f60.png')) {
  230. img.src = newUrl;
  231. }
  232. });
  233. }
  234.  
  235. // Function to handle crosshair URL input
  236. function handleCrosshairUrlInput() {
  237. const crosshairInput = document.getElementById('crosshair-url');
  238. const settings = JSON.parse(GM_getValue('settings'));
  239.  
  240. if (crosshairInput) {
  241. crosshairInput.value = settings.crosshairUrl || '';
  242.  
  243. crosshairInput.addEventListener('input', () => {
  244. const newUrl = crosshairInput.value.trim();
  245. settings.crosshairUrl = newUrl;
  246. GM_setValue('settings', JSON.stringify(settings));
  247. updateCrosshair(newUrl);
  248. });
  249.  
  250. if (settings.crosshairUrl) {
  251. updateCrosshair(settings.crosshairUrl);
  252. }
  253. }
  254. }
  255.  
  256. // Function to update crosshair
  257. function updateCrosshair(url) {
  258. let crosshair = document.getElementById('crosshair');
  259.  
  260. if (!crosshair) {
  261. crosshair = document.createElement('img');
  262. crosshair.id = 'crosshair';
  263. document.body.appendChild(crosshair);
  264. }
  265.  
  266. crosshair.src = url;
  267. crosshair.style.display = url ? 'block' : 'none';
  268. }
  269.  
  270. // Function to handle color inversion
  271. function handleInvertedColors() {
  272. const invertedCheckbox = document.getElementById('inverted');
  273. const settings = JSON.parse(GM_getValue('settings'));
  274.  
  275. if (invertedCheckbox) {
  276. invertedCheckbox.checked = settings.inverted;
  277.  
  278. invertedCheckbox.addEventListener('change', () => {
  279. settings.inverted = invertedCheckbox.checked;
  280. GM_setValue('settings', JSON.stringify(settings));
  281. document.body.classList.toggle('inverted', settings.inverted);
  282. });
  283.  
  284. // Apply initial state
  285. if (settings.inverted) {
  286. document.body.classList.add('inverted');
  287. }
  288. }
  289. }
  290.  
  291. // Function to handle Show FPS checkbox
  292. function handleShowFps() {
  293. const showFpsCheckbox = document.getElementById('show-fps');
  294. const settings = JSON.parse(GM_getValue('settings'));
  295.  
  296. if (showFpsCheckbox) {
  297. showFpsCheckbox.checked = settings.showFps;
  298.  
  299. showFpsCheckbox.addEventListener('change', () => {
  300. settings.showFps = showFpsCheckbox.checked;
  301. GM_setValue('settings', JSON.stringify(settings));
  302. toggleFpsDisplay(settings.showFps);
  303. });
  304.  
  305. // Apply initial state
  306. if (settings.showFps) {
  307. toggleFpsDisplay(true);
  308. }
  309. }
  310. }
  311.  
  312. // Function to show or hide FPS display
  313. function toggleFpsDisplay(show) {
  314. let fpsContainer = document.getElementById('fps-container');
  315. if (show) {
  316. if (!fpsContainer) {
  317. fpsContainer = document.createElement('div');
  318. fpsContainer.id = 'fps-container';
  319. fpsContainer.style.position = 'fixed';
  320. fpsContainer.style.top = '4px';
  321. fpsContainer.style.left = '918px';
  322. fpsContainer.style.zIndex = '9999';
  323. fpsContainer.style.cursor = 'move';
  324. fpsContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
  325. fpsContainer.style.color = 'white';
  326. fpsContainer.style.padding = '5px';
  327. fpsContainer.style.border = '3px solid rgb(174, 0, 255)';
  328. fpsContainer.style.borderRadius = '5px';
  329. fpsContainer.style.fontSize = '14px';
  330. document.body.appendChild(fpsContainer);
  331.  
  332. // Make the fpsContainer draggable
  333. makeDraggable(fpsContainer, 'fpsTop', 'fpsLeft');
  334. }
  335. fpsContainer.style.display = 'block';
  336. showFps(); // Start FPS monitoring
  337. } else {
  338. if (fpsContainer) {
  339. fpsContainer.style.display = 'none';
  340. }
  341. }
  342. }
  343.  
  344. // Function to show FPS
  345. function showFps() {
  346. const fpsContainer = document.getElementById('fps-container');
  347. if (!fpsContainer) return;
  348.  
  349. let lastTime = performance.now();
  350. let frameCount = 0;
  351. let fps = 0;
  352.  
  353. // Update FPS display
  354. const updateFpsDisplay = () => {
  355. fpsContainer.innerHTML = `<span id="fps">${fps}</span> FPS`;
  356. };
  357.  
  358. // Calculate FPS based on frame count and time elapsed
  359. const calculateFps = () => {
  360. const now = performance.now();
  361. const deltaTime = now - lastTime;
  362.  
  363. if (deltaTime > 0) {
  364. fps = Math.round((frameCount * 1000) / deltaTime);
  365. lastTime = now;
  366. frameCount = 0;
  367. updateFpsDisplay();
  368. }
  369. };
  370.  
  371. // Request animation frame callback
  372. const frameCallback = () => {
  373. frameCount++;
  374. requestAnimationFrame(frameCallback);
  375. };
  376.  
  377. // Start counting frames
  378. requestAnimationFrame(frameCallback);
  379.  
  380. // Update FPS every 500 milliseconds
  381. setInterval(calculateFps, 500);
  382. }
  383.  
  384. // Check settings and initialize
  385. function init() {
  386. document.addEventListener('DOMContentLoaded', () => {
  387. setTimeout(() => {
  388. const settings = JSON.parse(GM_getValue('settings'));
  389. console.log('Loaded settings:', settings); // Debug
  390.  
  391. // Initialize settings panel
  392. createSettingsPanel();
  393.  
  394. // Apply initial settings
  395. handleImageUrlInput();
  396. handleCrosshairUrlInput();
  397. handleInvertedColors();
  398. handleShowFps();
  399. }, 1000); // Apply settings after 1 second
  400. });
  401. }
  402.  
  403. init();
  404. })();