Automate Arcanum

Automates the game arcanum by allowing autoclicking and autocasting

目前为 2023-09-12 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Automate Arcanum
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Automates the game arcanum by allowing autoclicking and autocasting
  6. // @author You
  7. // @match https://mathiashjelm.gitlab.io/arcanum/
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=galaxy.click
  9. // @grant GM_addStyle
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15. const customStyles = `
  16. .timeInput {
  17. width: 6rem;
  18. background-color: #181818;
  19. border-color: #646464;
  20. color: #D7DADC;
  21. text-align: center;
  22. padding: var(--sm-gap);
  23. margin: var(--sm-gap);
  24. }
  25. .checkbox {
  26. position: relative;
  27. display: inline-block;
  28. width: 1.25em;
  29. height: 1.25em;
  30. background-color: transparent;
  31. border: 1px solid #000;
  32. }
  33. .autoCheck {
  34. margin-left: -1.75em;
  35. margin-top: 1.35em;
  36. }
  37. .quickslotTimer {
  38. width: 3rem;
  39. background-color: #181818;
  40. border-color: #646464;
  41. color: #D7DADC;
  42. text-align: center;
  43. padding: var(--sm-gap);
  44. margin: var(--sm-gap);
  45. height: 2rem;
  46. }
  47. .quickbar {
  48. flex-direction: column;
  49. flex-wrap: wrap;
  50. justify-content: center;
  51. align-items: center;
  52. }
  53. `;
  54. const loadClass = "load-message"
  55. let hasLoaded = false;
  56. let actionInterval = 0;
  57. let autocastInterval = 0;
  58. let offMain = false;
  59. let intervalId;
  60. let autoKey;
  61. let quickslotTimers = [];
  62.  
  63. function beginAutoAction() {
  64. clearInterval(intervalId);
  65. intervalId = setInterval(function() {
  66. const button = document.querySelector(`[data-key="${autoKey}"]`);
  67. if (button) {
  68. button.click();
  69. console.log(`Timer is currently ${actionInterval} and it is clicking ${autoKey}`);
  70. }
  71. }, actionInterval);
  72. }
  73.  
  74. function stopAutoAction() {
  75. clearInterval(intervalId);
  76. }
  77.  
  78. function beginAutoQuickslot(timer, index) {
  79. clearInterval(quickslotTimers[index]);
  80. quickslotTimers[index] = setInterval(function() {
  81. const quickslot = document.querySelector(`#slot${index}`);
  82. if (quickslot && quickslot.checked) {
  83. const quickslot = document.getElementsByClassName("quickslot")[index];
  84. quickslot.children[0].click();
  85. console.log(`Clicking the quickslot number ${parseInt(index+1)}`)
  86. }
  87. }, timer * 1000);
  88. }
  89.  
  90. function stopAutoQuickSlot(index) {
  91. clearInterval(quickslotTimers[index]);
  92. }
  93.  
  94. function checkOnlyOne(id, n){
  95. const checkboxes = document.getElementsByName(n);
  96. Array.prototype.forEach.call(checkboxes,function(e){
  97. if (e != id) {
  98. e.checked = false;
  99. }
  100. });
  101. }
  102.  
  103. function pageUpdate() {
  104.  
  105. const taskList = document.querySelector(".task-list")
  106. if (hasLoaded) {
  107. console.log("Page updated, checking if changes need to be made..");
  108. }
  109.  
  110. let buttons = taskList.querySelectorAll(".task-btn");
  111. let filteredBtns = Array.from(buttons).filter(button => !button.classList.contains("locked"));
  112. let lockedBtns = Array.from(buttons).filter(button => button.classList.contains("locked"));
  113. let i = 0;
  114. let quickslots = document.querySelector(".quickbar").querySelectorAll(".quickslot").forEach(quickslot => {
  115. if (document.getElementById("slot"+i)) {
  116. return
  117. }
  118.  
  119. const checkbox = document.createElement("input");
  120. checkbox.setAttribute("type", "checkbox");
  121. checkbox.setAttribute("name", "quickslot");
  122. checkbox.classList.add("quickCheck");
  123. checkbox.classList.add("checkbox");
  124. checkbox.id = "slot"+i;
  125. checkbox.addEventListener("change", function () {
  126. const checkboxIndex = parseInt(this.id.replace("slot", ""));
  127. if (this.checked) {
  128. beginAutoQuickslot(document.getElementById("timer"+checkboxIndex).value, checkboxIndex);
  129. } else {
  130. stopAutoQuickSlot(checkboxIndex, checkboxIndex);
  131. }
  132. });
  133.  
  134. const timer = document.createElement("input");
  135. timer.setAttribute("placeholder", "(s)");
  136. timer.addEventListener("input", function(e) {
  137. const inputValue = e.target.value;
  138. const cleanedValue = inputValue
  139. .replace(/[^0-9]+/g, '') // Remove non-numeric and non-dot characters
  140. .replace(/(\..*?)\..*/g, '$1') // Remove multiple dots
  141. .replace(/^0[^.]/, '0'); // Remove leading zero before a non-dot character
  142.  
  143. if (inputValue !== cleanedValue) {
  144. // If the value was changed, update the input field
  145. e.target.value = cleanedValue;
  146. }
  147.  
  148. if (e.target.id == "actionTimer") {
  149. actionInterval = e.target.value;
  150. beginAutoAction();
  151. }
  152.  
  153. if (document.querySelector(`#slot${e.target.id.replace("timer", "")}`).checked) {
  154. console.log(`The current timer value is ${e.target.value}`);
  155. beginAutoQuickslot(e.target.value, e.target.id.replace("timer", ""));
  156. }
  157. });
  158. timer.classList.add("quickslotTimer")
  159. timer.setAttribute("type", "text");
  160. timer.id = `timer${i}`;
  161.  
  162. quickslot.insertAdjacentElement("afterend", timer);
  163. quickslot.insertAdjacentElement('beforeend', checkbox);
  164. i++;
  165. });
  166.  
  167. filteredBtns.forEach(button => {
  168. const dataKey = button.getAttribute("data-key")
  169. if (document.getElementById(dataKey)) {
  170. return
  171. }
  172. const checkbox = document.createElement("input");
  173. checkbox.setAttribute("type", "checkbox");
  174. checkbox.setAttribute("name", "task");
  175. checkbox.classList.add("autoCheck");
  176. checkbox.classList.add("checkbox");
  177. checkbox.addEventListener("change", function() {
  178. if (this.checked && actionInterval != 0) {
  179. autoKey = dataKey;
  180. beginAutoAction();
  181. } else {
  182. stopAutoAction();
  183. }
  184. });
  185. checkbox.id = dataKey
  186. button.insertAdjacentElement('afterend', checkbox);
  187. });
  188.  
  189. lockedBtns.forEach(button => {
  190. const dataKey = button.getAttribute("data-key");
  191. const checkbox = document.getElementById(dataKey);
  192. if (checkbox) {
  193. checkbox.parentNode.removeChild(checkbox);
  194. }
  195. });
  196. }
  197.  
  198. function handleDivAppeared(mutationsList) {
  199. for (const mutation of mutationsList) {
  200. if(mutation.type === "childList") {
  201. const addedNodes = Array.from(mutation.addedNodes);
  202. for (const addedNode of addedNodes) {
  203. if (addedNode.classList && addedNode.classList.contains("main-tasks")) {
  204. if (document.querySelector(".main-tasks")) {
  205. offMain = false;
  206. pageUpdate();
  207. }
  208. }
  209. }
  210.  
  211. }
  212. }
  213.  
  214. }
  215.  
  216. function handleDivDisappeared(mutationsList) {
  217. for (const mutation of mutationsList) {
  218. if (mutation.type === 'childList') {
  219. const removedNodes = Array.from(mutation.removedNodes);
  220. for (const removedNode of removedNodes) {
  221. if (removedNode.classList && removedNode.classList.contains("main-task")){
  222. offMain = true;
  223. }
  224. if (removedNode.classList && removedNode.classList.contains(loadClass)) {
  225. // Save loaded, run script
  226. console.log(`Page loaded, beginning automation.`);
  227. // Inject the custom CSS styles into the page
  228. GM_addStyle(customStyles);
  229. hasLoaded = true;
  230. const timer = document.createElement("input");
  231. timer.classList.add("timeInput");
  232. timer.setAttribute("type", "text");
  233. timer.id = "actionTimer";
  234. const label = document.createElement("label");
  235. label.setAttribute("for", "actionTimer")
  236. label.innerHTML="Action interval (ms):"
  237. document.querySelector(".load-opts").appendChild(timer);
  238. document.querySelector(".load-opts").insertBefore(label, document.getElementById("actionTimer"));
  239. timer.addEventListener("input", function(e) {
  240. const inputValue = e.target.value;
  241. const cleanedValue = inputValue
  242. .replace(/[^0-9]+/g, '') // Remove non-numeric and non-dot characters
  243. .replace(/(\..*?)\..*/g, '$1') // Remove multiple dots
  244. .replace(/^0[^.]/, '0'); // Remove leading zero before a non-dot character
  245.  
  246. if (inputValue !== cleanedValue) {
  247. // If the value was changed, update the input field
  248. e.target.value = cleanedValue;
  249. }
  250.  
  251. if (e.target.id == "actionTimer") {
  252. actionInterval = e.target.value;
  253. beginAutoAction();
  254. }
  255. });
  256.  
  257. pageUpdate();
  258.  
  259. startClassObserver();
  260. }
  261. }
  262. }
  263. }
  264. }
  265.  
  266. function startClassObserver() {
  267. const classObserver = new MutationObserver((mutationsList, classObserver) => {
  268. // Handle class attribute changes here
  269. pageUpdate();
  270. });
  271.  
  272. const classConfig = { attributes: true, attributeFilter: ['class'] };
  273.  
  274. // Start observing class attribute changes
  275. classObserver.observe(document, classConfig);
  276. }
  277.  
  278. // Create a MutationObserver that watches for changes in the DOM
  279. const observer = new MutationObserver(handleDivDisappeared);
  280. const observer2 = new MutationObserver(handleDivAppeared);
  281. const config = { childList: true, subtree: true };
  282.  
  283. // Start observing the DOM
  284. observer.observe(document, config);
  285. observer2.observe(document, config);
  286.  
  287. document.addEventListener("click", function(e) {
  288. if(e.target.classList.contains("autoCheck")){
  289. checkOnlyOne(e.target, e.target.getAttribute("name"));
  290. }
  291. });
  292.  
  293. })();