Lucibear

Untitled

Jul 21st, 2020
288
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         aardvark arcanum auto - Harrygiel's fork, mod for 0.8.1
  3. // @version      1716
  4. // @author       aardvark, Linspatz, Harrygiel, Lucibear
  5. // @description  Automates casting buffs, buying gems making types gems, making lore. Adds sell junk/dupe item buttons. Must open the main tab and the spells tab once to work.
  6. // @downloadURL  https://github.com/harrygiel/arcanum-automation/raw/master/automate.user.js
  7. // @match        http://www.lerpinglemur.com/arcanum/*
  8. // @match        https://www.lerpinglemur.com/arcanum/*
  9. // @match        http://game312933.konggames.com/gamez/0031/2933/*
  10. // @match        https://game312933.konggames.com/gamez/0031/2933/*
  11. // @match        http://www.kongregate.com/games/lerpinglemur/theory-of-magic
  12. // @match        https://shokkuno.gitlab.io/arcanum/*
  13. // @match        https://www.kongregate.com/games/lerpinglemur/theory-of-magic*
  14. // @run-at       document-idle
  15. // ==/UserScript==
  16. //debugger;
  17. var tc_arcanum_ver = 10000;     // used to allow backwards compatibility
  18.  
  19. var tc_debug = false;   // set to true to see debug messages
  20.  
  21. var tc_suspend = false;     // set this to true in console to suspend all auto functions
  22.  
  23. // Setting to false will stop individual actions
  24. var tc_auto_misc = true;
  25. var tc_use_sublimate = true;
  26. var tc_auto_gather = true;
  27. var tc_auto_grind = true;
  28. var tc_auto_cast = true;
  29. var tc_skipcast = true;
  30. var tc_auto_focus = true;
  31. var tc_auto_earn_gold = false;
  32. var tc_auto_heal = true;
  33. var tc_auto_adv = true;
  34. var tc_auto_focus_aggressive = false;
  35.  
  36. // Set to a adventure name to continously run that adventure, leave blank to disable
  37. var tc_auto_adventure = "";
  38. var tc_adventure_wait = 30;//How many ticks to wait to rerun an adventure
  39. var tc_adventure_wait_cd = 30;//Counts down
  40. /* The following can be increased by encounters in the adventure listed.
  41. (Stat) - ("dungeon name") (increased amount) (chance to get the encounter needed)
  42. Skills:
  43. Anatomy - "ruined crypt" 0.01 2/7
  44. Spirit Lore - "hestia's cottage" 0.001 1/6, "explore treffil wood" 0.001 1/6
  45. Charms - "hestia's cottage" 0.01 1/6
  46. Enchanting - "hestia's cottage" 0.01 1/6
  47. Potions - "hestia's cottage" 0.01 1/6
  48. Scrying - "hestia's cottage" 0.001 1/6
  49. History - "hestia's cottage" 0.001 1/6, "genezereth" 0.001 1/7
  50. Crafting - "fazbit's workshop" 0.001 1/7
  51. Pyromancy - "fazbit's workshop" 0.001 1/7
  52. Alchemy - "fazbit's workshop" 0.001 2/7
  53. Minerology - "genezereth" 0.001 1/7
  54. Air Lore - "genezereth" 0.001 1/7
  55. Stats:
  56. Arcana - "pidwig's cove" 0.05 1/6
  57. */
  58.  
  59. var tc_auto_speed = 1000; // Speed in ms, going too low will cause performance issues.
  60. var tc_auto_speed_spells = 950; // interval in ms for spell casting. should be 1000, but lag can cause spells to run out
  61.  
  62. var tc_spells = new Map();
  63. var tc_resources = new Map();
  64. var tc_actions = new Map();
  65. var tc_bars = new Map();
  66. var tc_adventures = new Map();
  67. var tc_running = new Map();
  68. var tc_focus;
  69. var tc_rest;
  70. var tc_checked_spells = 0;  // have a look at the spell tab on startup
  71. var tc_time_offset = 0; // used for casting spells - this will incr. every second
  72. var tc_runners = 1;     // how many runners are unlocked (based on what's seen, so will be a minimum)
  73.  
  74. // List of Gems that needs to be updated manually if changed.
  75. var tc_gems = {
  76.     "arcane gem" : "imbue gem (arcane)",
  77.     "fire gem" : "imbue gem (fire)",
  78.     "water gem" : "imbue gem (water)",
  79.     "nature gem" : "imbue gem (nature)", //updated from lifegem to gem, v1086 change
  80.     "earth gem" : "imbue stone (earth)",
  81.     "air gem" : "imbue gem (air)",
  82.     "shadow gem" : "imbue gem (shadow)",
  83.     "light gem" : "imbue gem (light)",
  84.     "spirit gem" : "imbue gem (spirit)",
  85.     "blood gem" : "coagulate gem (blood)",
  86. };
  87.  
  88. // List of spells to autocast when needed without using quickbar (and interval to cast at)
  89. var tc_autospells = {
  90.     "minor mana" : 30,
  91.     "lesser mana" : 60,
  92.     "mana" : 120,
  93.     "minor fount" : 30,
  94.     "fount" : 60,
  95.     "greater fount" : 90,
  96.     "wild growth" : 45,
  97.     "abundance" : 60,
  98.     "unearth" : 120,
  99.     "unseen servant" : 45,
  100.     "guided strike" : 45,
  101.     "true strike" : 45,
  102.     "perfect strike" : 45,
  103.     "whisper" : 50,
  104.     "insight" : 60,
  105.     "wind sense" : 60,
  106.     "water sense" : 60,
  107.     "fire sense" : 60,
  108.     "whirling step III" : 80,
  109.     "dust devil II" : 45,
  110.     "adamant shell" : 180,
  111.     "pulsing light" : 45,
  112.     "pulsing light II" : 60,
  113.     "pulsing light III" : 120,
  114.     "copper skin" : 30,
  115.     "stone skin" : 50,
  116.     "iron skin" : 60,
  117. };
  118.  
  119. // One-off actions that earn gold ordered from best to worst (gold/stamina), and stamina cost/click.
  120. // Some also have side effects: ignore actions with negative side effects including using mana (conflict with autofocus).
  121. var tc_actions_gold = {
  122.     "advise notables" : 0.3,    // 4.5  (0.35 + .1 + .1 + .3 + .5) / .3  - after 1500 turns
  123.     "do chores" : 0.17,         // 2.94 (0.3 + .1 + .1) / 0.17 - after 250 turns
  124.     "clean stables" : 0.08,     // 2.5  (0.2 / 0.08)
  125.     "gather herbs" : 0.3,       // 1.33  (2 / 0.3*5) - assumes we are auto-selling surplus herbs
  126. };
  127.  
  128. // Call this every second - will automatically pick up new spells
  129. function tc_populate_spells()
  130. {
  131.     // It can be confusing that autocast doesn't do anything until the spells tab is visited,
  132.     // so switch to it on startup and grab anything there.
  133.     if (tc_checked_spells == 0) {
  134.         if (!tc_settab("spells"))   // this might fail if spells not available yet
  135.             tc_checked_spells++;
  136.         tc_checked_spells++;
  137.         // wait for tab to be displayed
  138.         return;
  139.     }
  140.  
  141.     if (tc_gettab() !== "spells") return;
  142.  
  143.     for (let qs of document.querySelectorAll(".spells .bottom .spellbook table tr")) {
  144.         if (qs.childElementCount == 3) {
  145.             var spell = qs.children[1].innerHTML.toLowerCase();
  146.             if (!tc_spells.get(spell) && !qs.children[2].firstChild.disabled) {
  147.                 // Don't save spells we haven't learnt yet.
  148.                 if (qs.children[2].firstChild.innerText.toLowerCase() == "cast") {
  149.                     tc_spells.set(spell, qs.children[2].firstChild);
  150.                     if (tc_debug) console.log("Saved spell: " + spell);
  151.                 }
  152.             }
  153.         }
  154.     }
  155.  
  156.     if (tc_checked_spells == 1) {
  157.         // switch tab succeeded and we've grabbed the spells, so switch back to main
  158.         tc_settab("main");
  159.         tc_checked_spells++;
  160.     }
  161. }
  162.  
  163. // Call this every second to update resource values
  164. function tc_populate_resources()
  165. {
  166.     var resources = document.querySelectorAll("div.game-main div.res-list div.res-group div.rsrc.separate.hidable");
  167.     if (resources.length == 0)
  168.     {
  169.         resources = document.querySelectorAll("div.game-main div.res-list div");
  170.     }
  171.     for (let n of resources) {
  172.         var name = n.querySelector("span.item-name").innerText.toLowerCase();
  173.         var vals = n.querySelectorAll("span.num-align")[0].innerText.split("/");
  174.         var val0 = parseInt(vals[0]);
  175.         var val1 = parseInt(vals[1]);
  176.         tc_resources.set(name, [ val0, val1 ]);
  177.     }
  178. }
  179.  
  180. // Call every second to update mana bars
  181. function tc_populate_bars()
  182. {
  183.     for (let n of document.querySelectorAll("div.game-main div.vitals div.statbars div.hidable.statbar")) {
  184.         var name = n.querySelector("span.name").innerText.toLowerCase();
  185.         var vals = n.querySelectorAll("span.barspan")[0].innerText.split("/");
  186.         var val0 = parseFloat(vals[0]);
  187.         var val1 = parseFloat(vals[1]);
  188.         tc_bars.set(name, [ val0, val1 ]);
  189.     }
  190. }
  191.  
  192. // Call every second to look for new buttons and ones that are now active.
  193. function tc_populate_actions()
  194. {
  195.     if (tc_gettab() !== "main") return;
  196.  
  197.     for (let qs of document.querySelectorAll(".main-tasks .task-list .task-btn.hidable:not(.locked):not(.disabled)")) {
  198.         var key = qs.innerHTML.toLowerCase();
  199.         if (!tc_actions.get(key)) {
  200.             tc_actions.set(key, qs);
  201.             if (tc_debug) console.log("Action stored: " + qs.innerHTML);
  202.         }
  203.     }
  204. }
  205.  
  206. // Call every second to look for new adventures and adventures that are now active.
  207. function tc_populate_adventures()
  208. {
  209.     if (tc_gettab() !== "adventure") return;
  210.  
  211.     //Map is set up as: name, [progress, needed, button]
  212.     for (let qs of document.querySelectorAll("div.game-main div.locales div.locale")){
  213.         if(!qs.children[0].children[0].children[1].disabled){
  214.             var name = qs.children[0].children[0].children[0].innerText // name of dungeon
  215.             var vals = qs.children[1].innerText.split("/")
  216.             tc_adventures.set(name,[vals[0],vals[1],qs.children[0].children[0].children[1]]);
  217.         }
  218.     }
  219. }
  220.  
  221. // Call every second to check what you are doing.
  222. function tc_populate_running()
  223. {
  224.     tc_running.clear();
  225.     for (let qs of document.querySelectorAll("div.running div")){
  226.         var key = qs.lastElementChild.innerHTML.toLowerCase();
  227.         tc_running.set(key, qs.firstChild);
  228.     }
  229. }
  230.  
  231. // Check if a resource is above a percentage. example: tc_check_resource("gold",.5);    // that's not a % lol
  232. function tc_check_resource(resource,percent) {
  233.     return !tc_resources.get(resource) || tc_resources.get(resource)[0] >= tc_resources.get(resource)[1] * percent;
  234. }
  235.  
  236. // Check if a bar(mana etc) is above a percentage.
  237. function tc_check_bars(bars,percent) {
  238.     return !tc_bars.get(bars) || tc_bars.get(bars)[0] >= tc_bars.get(bars)[1] * percent;
  239. }
  240.  
  241. // Check if you are in an adventure
  242. function tc_check_running_adv(){
  243.     for (let qs of tc_running.keys()) {
  244.         if (qs.split(/?|??|??|??/).length==2) return true;
  245.     }
  246.     return false;
  247. }
  248.  
  249. // Return name of current tab
  250. function tc_gettab()
  251. {
  252.     for (let tab of document.querySelectorAll("div.menu-items div.menu-item span")) {
  253.         var s = tab.innerHTML;
  254.         if (! /<\/u>/.test(s))
  255.             return s.slice(1, -1);  // strip off leading and trailing space
  256.     }
  257. }
  258.  
  259. // Set current tab to "name", return false if no such tab available.
  260. function tc_settab(newtab)
  261. {
  262.     for (let tab of document.querySelectorAll("div.menu-items div.menu-item span")) {
  263.         if (tab.innerHTML.indexOf(newtab) != -1) {
  264.             tab.click();
  265.             return true;
  266.         }
  267.     }
  268.     return false;   // name not recognised, maybe not unlocked yet
  269. }
  270.  
  271. // Clicks the selected adventure button
  272. function tc_click_adv(adventure)
  273. {
  274.     var lcl = tc_adventures.get(adventure);
  275.     if (!lcl) return;
  276.     if (tc_suspend) return;
  277.  
  278.     if (lcl.disabled) {
  279.         if (tc_debug) console.log("Adventure '" + adventure + "' was disabled - deleting it");
  280.         tc_adventures.delete(adventure);
  281.         return;
  282.     }
  283.  
  284.     if (tc_debug) console.log("Clicking: " + adventure);
  285.     lcl[2].click();
  286.     return;
  287. }
  288.  
  289. // Clicks the action button
  290. function tc_click_action(action)
  291. {
  292.     var act = tc_actions.get(action);
  293.     if (!act) return false;
  294.  
  295.     if (act.disabled) { // not sure how this happens, but seems to prevent action ever being called again
  296.         if (tc_debug) console.log("Action '" + action + "' was disabled - deleting it");
  297.         tc_actions.delete(action);
  298.         return false;
  299.     }
  300.  
  301.     if (tc_debug) console.log("Clicking: " + action);
  302.     act.click();
  303.     return true;    // click might still have failed
  304. }
  305.  
  306. // Clicks the spell button
  307. function tc_cast_spell(spell)
  308. {
  309.     var spl = tc_spells.get(spell);
  310.     if (!spl) return false;
  311.  
  312.     if (spl.disabled) { // not sure how this happens, but seems to prevent action ever being called again
  313.         if (tc_debug) console.log("Spell '" + spell + "' was disabled - deleting it");
  314.         tc_spells.delete(spell);
  315.         return false;
  316.     }
  317.  
  318.     if (tc_debug) console.log("Casting: " + spell);
  319.     spl.click();
  320.     return true;
  321. }
  322.  
  323. // Adds an input field to each button on the quickbar to allow casting at regular intervals. Author: iko
  324. function iko_autocast()
  325. {
  326.     // Stuff for quickslot bar
  327.     for (let qs of document.querySelectorAll(".quickslot")) {
  328.         // If it doesn't have the text entry box yet then add it.
  329.         if (!qs.lastElementChild.classList.contains("timeset")) {
  330.             var box = document.createElement("input");
  331.             box.setAttribute("type", "text");
  332.             box.setAttribute("class", "timeset");
  333.             box.setAttribute("style", "position:absolute;bottom:0px;left:0px;width:100%;font-weight:bold;opacity:0.75;text-align:center;");
  334.             qs.appendChild(box);
  335.         }
  336.  
  337.         var val = parseInt(qs.lastElementChild.value);
  338.         if (val > 0 && tc_time_offset % val == 0 && qs.firstElementChild.firstElementChild !== null && qs.lastElementChild !== document.activeElement) {
  339.             qs.firstElementChild.firstElementChild.click()
  340.         }
  341.     }
  342. }
  343.  
  344. //Function to check spell exclusion rules
  345. function tc_skip_cast(spell)
  346. {
  347.     //Add sanity checks to some autocasting spells
  348.     //No heals at full health
  349.     if (spell == "pulsing light" && tc_bars.get("hp")[1]-tc_bars.get("hp")[0] <= 5) return true;
  350.     if (spell == "pulsing light II" && tc_bars.get("hp")[1]-tc_bars.get("hp")[0] <= 25) return true;
  351.     if (spell == "pulsing light III" && tc_bars.get("hp")[1]-tc_bars.get("hp")[0] <= 50) return true;
  352.  
  353.     //No herb generating spells if herbs are full
  354.     if (spell == "wild growth" && tc_check_resource("herbs",1)) return true;
  355.     if (spell == "abundance" && tc_check_resource("herbs",1)) return true;
  356.  
  357.     //No gem generating spells if gems are full
  358.     if (spell == "unearth" && tc_check_resource("gems",1)) return true;
  359.  
  360.     //No need for unseen servant if gold is full
  361.     if (spell == "unseen servant" && tc_check_resource("gold",1)) return true;
  362.  
  363.     //No exclusion triggered, return false
  364.     return false;
  365. }
  366.  
  367. // For AUTOING. Casts spells listed under autospells
  368. function tc_autocast()
  369. {
  370.     if (tc_suspend) return;
  371.     if (!tc_auto_cast) return;
  372.  
  373.     for (var spell in tc_autospells) {
  374.         var rpt = tc_autospells[spell];
  375.         if (tc_time_offset % rpt == 0 && !(tc_skipcast && tc_skip_cast(spell))) {
  376.             if (tc_debug) console.log("try casting " + spell);
  377.             tc_cast_spell(spell);
  378.         }
  379.     }
  380. }
  381.  
  382. // For AUTOING. Does several actions
  383. function tc_automate()
  384. {
  385.     if (tc_suspend) return;
  386.     if (!tc_auto_misc) return;
  387.  
  388.     tc_populate_resources();
  389.  
  390.     if (tc_check_resource("herbs",1) && !tc_check_resource("gold",1)){
  391.         var missing = (tc_resources.get("gold")[1]-tc_resources.get("gold")[0]);
  392.         var sellamount = Math.min(missing/2,tc_resources.get("herbs")[1] / 2);
  393.         for (let i=0; i < sellamount; ++i)
  394.             tc_click_action("sell herbs");
  395.     }
  396.     if (tc_check_resource("research",1) && !tc_check_resource("scrolls",1) && tc_check_bars("mana",.75))
  397.         tc_click_action("scribe scroll");
  398.     if (!tc_check_resource("codices",1) && tc_check_resource("scrolls",1) && tc_check_bars("mana",.5))
  399.         tc_click_action("bind codex");
  400.     // Selling scrolls can be useful late game when we're automatically generating them,
  401.     // and buying scrolls is useful at the start,
  402.     // but there was a problem here when scrolls = max-1 and we bought a scroll, then scrolls were maxed but money wasn't
  403.     // so next tick we'd sell the scroll and so we'd never be able to max either.
  404.     if (tc_resources.get("gold")[0] < tc_resources.get("gold")[1] - 20 && tc_check_resource("scrolls",1))
  405.         tc_click_action("sell scroll");
  406.     else if (tc_check_resource("gold",1) && !tc_check_resource("scrolls",1))
  407.         tc_click_action("buy scroll");  // could fail if scribe above maxed them
  408.  
  409.     // If money maxed, buy gem
  410.     if (tc_check_resource("gold",1) && !tc_check_resource("gems",1))
  411.         tc_click_action("purchase gem");
  412.  
  413.     // If gems maxed, try making some different ones
  414.     if (tc_check_resource("gems",1)) {
  415.         var bought_gem = false;
  416.         for (var gem in tc_gems) {  // try to make one of each
  417.             if (!tc_check_resource(gem,1)) {
  418.                 if (tc_debug) console.log("not maxed " + gem + " calling " + tc_gems[gem]);
  419.                 if (tc_click_action(tc_gems[gem]))
  420.                     bought_gem = true;
  421.             }
  422.         }
  423.         // or buy the gem box
  424.         if (!bought_gem)
  425.             tc_click_action("gem box");
  426.     }
  427.  
  428.     // Sublimate lore
  429.     if (tc_use_sublimate && tc_check_resource("codices",1)) {
  430.         if (tc_click_action("sublimate lore"))
  431.             for (let qs of document.querySelectorAll(".popup"))
  432.                 // will get some errors in console here as popup matches config screen
  433.                 if (qs.children[0].children[0] && qs.children[0].children[0].innerHTML == "sublimate lore")
  434.                     qs.children[1].children[0].click();
  435.     }
  436.  
  437.     //Gather herbs if stamina full and herbs are not
  438.     if (tc_auto_gather && tc_check_bars("stamina",1) && !tc_check_resource("herbs",1))
  439.         for (let i=0; i < 10; ++i)
  440.             tc_click_action("gather herbs");
  441.  
  442.     //Grind out some max reserach if max mana
  443.     if (tc_auto_grind && tc_check_bars("mana",1))
  444.         for (let i=0; i < 20; ++i)
  445.             tc_click_action("grind");
  446. }
  447.  
  448. function tc_autoadv()
  449. {
  450.     if (tc_suspend) return;
  451.     if (!tc_auto_adv) return;
  452.  
  453. /* Only works on Adventure tab but I am not ready to get rid of it yet. ~linspatz
  454.     if (tc_gettab()=="adventure"){
  455.         var lcl = tc_adventures.get(tc_auto_adventure);
  456.         if (!lcl) return;
  457.  
  458.         var advper = lcl[0]/lcl[1];
  459.         if (advper == 0 || advper == 1) tc_click_adv(tc_auto_adventure); // this might just need to be advper==1
  460.     }
  461. */
  462.     if (tc_check_running_adv()==false){
  463.         if (tc_adventure_wait_cd <= 0){
  464.             tc_click_adv(tc_auto_adventure);
  465.             tc_adventure_wait_cd = tc_adventure_wait; // resets countdown
  466.         } else {
  467.             tc_adventure_wait_cd--;
  468.         }
  469.  
  470.     }
  471. }
  472.  
  473.  
  474. // Sells all items that are considered junk
  475. function tc_selljunk()
  476. {
  477.     var sell_exact = [ "amulet", "axe", "band", "battleaxe", "belt", "boots", "broomstick", "cane", "cap", "cape", "cincture", "cloak", "club", "collar", "conical helm", "dagger", "girdle", "gloves", "greaves", "hat", "helm", "jerkin", "knife", "longsword", "loop", "mace", "necklace", "pendant", "ring", "robe", "sash", "shortsword", "spear", "staff" ];
  478.     var sell_match = [ "silk ", "cotton ", "stone ", "leather ", "^wood ", "bone ", "bronze ", "iron ", "^steel " ];    // aggressive
  479.  
  480.     // "silk ", "cotton ", "stone ", "leather ", "^wood ", "bone ", "bronze ", "iron ", "^steel ", "quicksteel ", "mithril ", "ebonwood ", "ethereal ", "adamant "
  481.  
  482.     function checkmatch(m) { for (let i of sell_match) if (RegExp(i).test(m)) return true; return false; }
  483.  
  484.     var itemlocation = document.querySelectorAll(".adventure .raid-bottom .inv .item-table tr")
  485.     if (itemlocation.length == 0)
  486.         itemlocation = document.querySelectorAll(".adventure .raid-bottom .inv table tr")
  487.  
  488.     for (let row of itemlocation) {
  489.         // table has 4 columns: name + 3 buttons: Equip, Take, Sell
  490.         if (row.children[3].children[0].innerText == "Sell") {
  491.             var item = row.children[0].innerText;
  492.             if (sell_exact.indexOf(item) != -1 || checkmatch(item)) {
  493.                 if (tc_debug) console.log("Selling: " + item);
  494.                 row.children[3].children[0].click();
  495.             }
  496.         }
  497.     }
  498. }
  499.  
  500. // Sells any item that you have more than one of
  501. function tc_selldups()
  502. {
  503.     var items = new Map(); // test
  504.  
  505.     // Build a map of item -> qty
  506.     var itemlocation = document.querySelectorAll(".adventure .raid-bottom .inv .item-table tr")
  507.     if (itemlocation.length == 0)
  508.         itemlocation = document.querySelectorAll(".adventure .raid-bottom .inv table tr")
  509.  
  510.     for (let row of itemlocation) {
  511.         // table has 4 columns: name + 3 buttons: Equip, Take, Sell
  512.         if (row.children[3].children[0].innerText == "Sell") {
  513.             var item = row.children[0].innerText;
  514.             var qty = items.get(item);
  515.             items.set(item, qty ? qty+1 : 1);
  516.         }
  517.     }
  518.  
  519.     // Now iterate over rows, selling items where qty > 1
  520.     for (let row of itemlocation) {
  521.         // table has 4 columns: name + 3 buttons: Equip, Take, Sell
  522.         if (row.children[3].children[0].innerText == "Sell") {
  523.             var item = row.children[0].innerText;
  524.             var qty = items.get(item);
  525.             var maxqty = 1;
  526.             var itemtype = "";
  527.             switch(item.split(" ").pop()){
  528.                 case "pendant": case "collar": case "amulet": case "necklace":
  529.                     maxqty = 3;
  530.                     break;
  531.                 case "band": case "loop": case "ring":
  532.                     maxqty = 4;
  533.                     break;
  534.                 case "shortsword": case "club": case "cane": case "knife": case "broomstick": case "dagger": case "axe": case "mace":
  535.                     maxqty = 2;
  536.                     break;
  537.                 default:
  538.                     maxqty = 1
  539.                     break;
  540.             }
  541.         }
  542.         if (qty > maxqty) {
  543.             if (tc_debug) console.log("Selling: " + item);
  544.             row.children[3].children[0].click();
  545.             items.set(item, qty-1);
  546.         }
  547.     }
  548. }
  549.  
  550. // Adds a filter input for loot gained from adventures.
  551. function tc_lootfilter()
  552. {
  553.     var input = document.getElementById("lootfilter");
  554.     if (!input) return;
  555.     var filter = input.value;
  556.     if (tc_debug) console.log("filter: " + filter);
  557.  
  558.     if (filter.length == 0) {
  559.         // Clear all hidden
  560.         for (let row of document.querySelectorAll(".adventure .raid-bottom .inv table tr"))
  561.             row.style.display = "";
  562.     }
  563.     else {
  564.         for (let row of document.querySelectorAll(".adventure .raid-bottom .inv table tr"))
  565.             if (row.children[0].innerText.indexOf(filter) != -1)
  566.                 row.style.display = "";
  567.             else
  568.                 row.style.display = "none";
  569.     }
  570. }
  571.  
  572. // Create junk and dupe sell buttons and a loot filter if not already present
  573. function tc_sellsetup()
  574. {
  575.     if (tc_gettab() != "adventure") return;
  576.     if (document.getElementById("selldups")) return;
  577.  
  578.     var sellall = document.querySelectorAll(".adventure .raid-bottom .inv span.top span button");
  579.     if (sellall.length = 0)
  580.         sellall = document.querySelectorAll(".adventure .raid-bottom .inv div.flex-row button");
  581.     if (sellall.length == 0) return;    // nothing to sell on tab yet
  582.     sellall = sellall[0];
  583.  
  584.     var selljunk = document.createElement("button");
  585.     var t1 = document.createTextNode("Sell Junk");
  586.     selljunk.appendChild(t1);
  587.     selljunk.addEventListener("click", tc_selljunk);
  588.  
  589.     var selldups = document.createElement("button");
  590.     var t2 = document.createTextNode("Sell Dupes");
  591.     selldups.appendChild(t2);
  592.     selldups.addEventListener("click", tc_selldups);
  593.     selldups.id = "selldups";
  594.  
  595.     sellall.parentNode.insertBefore(selljunk, null);
  596.     sellall.parentNode.insertBefore(selldups, null);
  597.  
  598.  
  599.     // Newest versions of the game come with its own loot filter. This code will no longer be needed when both sites update.
  600.     if (tc_arcanum_ver < 400){
  601.         var t3 = document.createTextNode("Filter");
  602.         var filter = document.createElement("Input");
  603.         filter.addEventListener("keyup", tc_lootfilter);
  604.         filter.id = "lootfilter";
  605.         filter.width = "50";
  606.  
  607.         sellall.parentNode.insertBefore(t3, null);
  608.         sellall.parentNode.insertBefore(filter, null);
  609.     }
  610.  
  611.     if (tc_debug) console.log("Sell buttons added");
  612. }
  613.  
  614. // Puts a button to set which dungeon to auto and pressing flee will cancel. Code based off of code by Bz
  615. function tc_advsetup()
  616. {
  617.     if (tc_gettab() !== "adventure") return;
  618.     if (tc_suspend) return;
  619.     if (!tc_auto_adv) return;
  620.     // makes clicking flee disable the auto adventure
  621.     if (document.querySelector("div.game-main div.adventure div.explore .raid-btn"))
  622.     {
  623.         document.querySelector("div.game-main div.adventure div.explore .raid-btn").addEventListener("click", function(){tc_auto_adventure = "";})
  624.     }
  625.  
  626.     // Creates an auto button for every adventure.
  627.     for (let qs of document.querySelectorAll("div.game-mid div.adventure div.locales div.locale span.separate:first-child")){
  628.         if (qs.lastElementChild.innerText !== "Auto"){
  629.             var seldungeon = document.createElement("button");
  630.             seldungeon.appendChild(document.createTextNode("Auto"));
  631.             seldungeon.addEventListener("click", function(){
  632.                 tc_auto_adventure = qs.firstElementChild.firstElementChild.innerText;
  633.                 tc_click_adv(tc_auto_adventure)});
  634.             if (tc_auto_adventure == qs.children[0].children[0].innerText)
  635.                 seldungeon.setAttribute("style", "color:#1B5E20");
  636.             qs.appendChild(seldungeon);
  637.         }
  638.     }
  639. }
  640.  
  641. // Quick and dirty. Needs to be worked on to allow you to set which action to press for gold
  642. function tc_autoearngold()
  643. {
  644.     if (tc_suspend) return;
  645.     if (!tc_auto_earn_gold) return;
  646.  
  647.     // Find which action we can do
  648.     var action = undefined;
  649.     for (let act in tc_actions_gold) {
  650.         var a = tc_actions.get(act);
  651.         if (a && !a.disabled) {
  652.             action = act;
  653.             break;
  654.         }
  655.     }
  656.     if (!action) return;    // no money-making actions available to us
  657.     var stam = tc_actions_gold[action];
  658.  
  659.     var amt = tc_bars.get("stamina")[0];
  660.     var max = tc_bars.get("stamina")[1];
  661.  
  662.     // This is quite efficient while resting, but then need to make sure we don't hit max stamina or resting will stop.
  663.     // However if adventuring, this will mean we delay re-entering dungeon as resting never stops until we hit max gold.
  664.     var min = max < 11 ? max-2 : max-5;
  665.     if (amt >= min) {
  666.         for (let i = (amt-min)/stam; i > 0; i--) {
  667.             if (tc_check_resource("gold", 1)) return;   // not sure if this will get updated every click
  668.             tc_click_action(action);
  669.         }
  670.     }
  671. }
  672.  
  673. var tc_skill_saved = "";
  674. var tc_skill_last = "";
  675.  
  676. /* This function is called when tc_auto_focus_aggressive is enabled and we have more than one runner.
  677.  One runner should always be resting and the other learning the skill. (If more than 2 runners could try multi-rest).
  678.  If someone chooses a skill then stick with that until maxed, otherwise rotate amongst them choosing lowest level.
  679.  How to tell if someone has specifically chosen a skill? Save current skill and next timeslice see if it's changed.
  680.  This is different from single runner case where we always finish with rest, so there will be no skill chosen unless the user selected one.
  681.  If there are 3 runners available, we need to switch off the old skill when a new one becomes lowest.
  682. */
  683. function tc_autofocus_multi(amt)
  684. {
  685.     var lowest_lvl = 1000;
  686.     var lowest_skill = "";
  687.     var lowest_btn;
  688.     var skill_btn;
  689.     var skill_to_learn = "";
  690.     var prev_btn;
  691.     var prev_skill = "";
  692.     for (let qs of document.querySelectorAll(".skills .skill")) {
  693.         var skill = qs.firstElementChild.firstElementChild.innerHTML;
  694.         var btn = qs.querySelectorAll("button")[0];
  695.         var text = btn.innerHTML.trim();    // Can be Unlock, Train, Stop
  696.         if (text == "Unlock" || btn.disabled) continue;
  697.  
  698.         // qs.firstElementChild.children[1].childNodes[0].data  - to get "Lvl: 3/4"
  699.         var lvl = parseInt(qs.firstElementChild.children[1].childNodes[0].data.substr(5).split('/')[0]);
  700.         if (lvl < lowest_lvl) {
  701.             lowest_lvl = lvl;
  702.             lowest_skill = skill;
  703.             lowest_btn = btn;
  704.         }
  705.  
  706.         if (text == "Stop") {   // it means we're training this skill
  707.             if (skill != tc_skill_last || skill == tc_skill_saved) {
  708.                 // the user changed to this skill or originally chose this one so learn it.
  709.                 if (tc_debug) console.log("Learning " + skill + ", last = " + tc_skill_last + ", saved = " + tc_skill_saved);
  710.                 tc_skill_saved = skill_to_learn = skill;
  711.                 skill_btn = btn;
  712.             }
  713.             else if (skill == tc_skill_last) {
  714.                 // If we don't end up learning this skill, we'll stop training it.
  715.                 prev_btn = btn;
  716.                 prev_skill = skill;
  717.             }
  718.         }
  719.     }
  720.  
  721.     if (skill_to_learn == "") {
  722.         if (lowest_skill == "") // nothing available to learn
  723.             return;
  724.  
  725.         skill_to_learn = lowest_skill;
  726.         skill_btn = lowest_btn;
  727.         if (tc_debug) console.log("Learn lowest skill: " + skill_to_learn);
  728.     }
  729.  
  730.     if (skill_btn.innerHTML.trim() == "Train")
  731.         skill_btn.click();
  732.     tc_skill_last = skill_to_learn;
  733.     if (prev_skill != "" && prev_skill != skill_to_learn) {
  734.         prev_btn.click();
  735.         if (tc_debug) console.log("Switch off " + prev_skill);
  736.     }
  737.  
  738.     // We're not going to be doing anything else while focussing (apart from autocast spells),
  739.     // so just keep enough mana for the 3 mana spells.
  740.     if (amt > 2.0) {
  741.         tc_focus.disabled = false;  // in case button hasn't been updated yet
  742.         for (let i = 10 * (amt-2); i > 0; i--)  // 0.1 mana per focus
  743.             tc_click_action("focus");
  744.     }
  745.  
  746.     // if tc_runners > 2 could try clicking other sorts of rest as well
  747.     tc_rest.click();    // Has no effect if already resting.
  748. }
  749.  
  750. // Uses focus until you have only 14 mana left.
  751. function tc_autofocus()
  752. {
  753.     if (tc_suspend) return;
  754.     if (!tc_auto_focus) return;
  755.  
  756.     if (!tc_focus || !tc_rest)
  757.         for (let qs of document.querySelectorAll(".vitals div.separate button.btn-sm")) {
  758.             if (!tc_focus && qs.innerHTML === "Focus")
  759.                 tc_focus = qs;
  760.             if (!tc_rest && qs.innerHTML.trim() === "rest")
  761.                 tc_rest = qs;
  762.         }
  763.     if (!tc_bars.get("mana")) return;
  764.  
  765.     var amt = tc_bars.get("mana")[0];
  766.     var max = tc_bars.get("mana")[1];
  767.  
  768.     if (tc_gettab() != "skills" || !tc_auto_focus_aggressive) {
  769.         tc_skill_saved = tc_skill_last = "";
  770.  
  771.         // 10 mana required for compile tome, 3 for Bind Codex, 1 for Scribe Scroll
  772.         var min = max < 15 ? max-1 : 14;
  773.         if (amt >= min) {
  774.             for (let i = 10 * (amt-min); i > 0; i--){
  775.                 tc_click_action("focus");
  776.             }
  777.         }
  778.         return;
  779.     }
  780.  
  781.     // We're on the skills tab - try to: repeat {use up all mana with focusing, then rest to restore mana }
  782.     // Note that if we switch tabs while resting, we won't restart learning skill when finished resting.
  783.     // As click() actions don't get processed immediately, we need to cheat by un-disabling some buttons.
  784.  
  785.     // Try to find which skill we're currently learning - the user might have switched since we last looked.
  786.     // Otherwise if we have a saved skill which isn't maxed, choose that,
  787.     // Otherwise learn cheapest skill (only checks level, not learning rate)
  788.     // Most useful at start of game, probably won't work well when multiple runners are unlocked.
  789.  
  790.     // If there are multiple runners available, the optimum strategy will be to have (at least) one resting continously,
  791.     // while the other one learns.
  792.     var r = document.querySelectorAll(".running div button").length;
  793.     if (r > tc_runners) tc_runners = r;
  794.     if (tc_runners > 1) {   // don't want to mess up the working single runner code
  795.         tc_autofocus_multi(amt) // available mana
  796.         return;
  797.     }
  798. 1
  799.     var lowest_lvl = 1000;
  800.     var lowest_skill = "";
  801.     var lowest_btn;
  802.     var skill_btn;
  803.     var skill_to_learn = "";
  804.     for (let qs of document.querySelectorAll(".skills .skill")) {
  805.         var skill = qs.firstElementChild.firstElementChild.innerHTML;
  806.         var btn = qs.querySelectorAll("button")[0];
  807.         var text = btn.innerHTML.trim();    // Can be Unlock, Train, Stop
  808.         if (text == "s" || btn.disabled) continue;
  809.  
  810.         if (text == "Stop") {   // it means we're training this skill
  811.             skill_to_learn = skill;
  812.             tc_skill_saved = skill;
  813.             skill_btn = btn;
  814.             if (tc_debug) console.log("Learning " + skill);
  815.             break;  // this takes precedence over anything else
  816.         }
  817.  
  818.         if (skill == tc_skill_saved) {
  819.             // skill still available to be learnt - we'll end up learning this unless another is active
  820.             skill_to_learn = skill;
  821.             skill_btn = btn;
  822.         }
  823.  
  824.         // qs.firstElementChild.children[1].childNodes[0].data  - to get "Lvl: 3/4"
  825.         var lvl = parseInt(qs.firstElementChild.children[1].childNodes[0].data.substr(5).split('/')[0]);
  826.         if (lvl < lowest_lvl) {
  827.             lowest_lvl = lvl;
  828.             lowest_skill = skill;
  829.             lowest_btn = btn;
  830.         }
  831.     }
  832.  
  833.     if (skill_to_learn == "") {
  834.         if (lowest_skill == "") // nothing available to learn
  835.             return;
  836.  
  837.         skill_to_learn = lowest_skill;
  838.         skill_btn = lowest_btn;
  839.         if (tc_debug) console.log("Learn lowest skill: " + skill_to_learn);
  840.     }
  841.  
  842.     if (skill_btn.innerHTML.trim() == "Train")
  843.         skill_btn.click();
  844.  
  845.     // We're not going to be doing anything else while focussing (apart from autocast spells),
  846.     // so just keep enough mana for the 3 mana spells.
  847.     if (amt > 2.0) {
  848.         tc_focus.disabled = false;  // button probably hasn't been updated yet, but we can cheat
  849.         for (let i = 10 * (amt-2); i > 0; i--)  // 0.1 mana per focus
  850.             tc_click_action("focus");
  851.     }
  852.  
  853.     tc_rest.disabled = false;
  854.     tc_rest.click();
  855. }
  856.  
  857. // Autoheal based on avalibility of spells and need.
  858. function tc_autoheal()
  859. {
  860.     if (tc_suspend) return;
  861.     if (!tc_auto_heal) return;
  862.  
  863.     if (tc_spells.has("sealing light iii")) {
  864.         if (tc_bars.get("hp")[1]-tc_bars.get("hp")[0] >= 100 && tc_bars.get("light")[1] >= 7)
  865.             tc_cast_spell("sealing light iii")
  866.         return;
  867.     }
  868.  
  869.     if (tc_spells.has("sealing light ii")){
  870.         if (tc_bars.get("hp")[1]-tc_bars.get("hp")[0] >= 50 && tc_bars.get("light")[1] >= 5)
  871.             tc_cast_spell("sealing light ii");
  872.     } else if (tc_spells.has("sealing light")){
  873.         if (tc_bars.get("hp")[1]-tc_bars.get("hp")[0] >= 15 && tc_bars.get("light")[1] >= 5)
  874.             tc_cast_spell("sealing light");
  875.     }
  876. }
  877.  
  878.  
  879. //coloring potions in red :D
  880. function tc_colorpot(){
  881.     if (tc_gettab() !== "equip") return;
  882.     for (let item of document.querySelectorAll(".item-table .separate")){
  883.         if (item.children[1].children[0].innerHTML == "Use"){
  884.             item.children[0].style["background-color"] = "red";
  885.         }
  886.     }
  887. }
  888.  
  889.  
  890. var tc_menu_inv_state = 0;
  891.  
  892. /* We need to switch tabs to get lists of equipment, but need to allow time for tab to populate,
  893.  so create a state machine, and do one action each tick.
  894.  */
  895. function tc_menu_inv()
  896. {
  897.  
  898.     var materials = [ "", "silk ", "cotton ", "stone ", "leather ", "wood ", "bone ", "bronze ", "iron ", "steel ", "quicksteel ",  "ethereal ", "ebonwood ", "idrasil ", "mithril ", "adamant "];  // worst to best
  899.     var armors = {  // items ordered worst to best within groups
  900.         "back"   : [ "cape", "robe", "cloak", "wings" ],
  901.         "chest"  : [ "jerkin", "padded armor", "chainmail", "plate mail" ],
  902.         "feet"   : [ "boots" ],
  903.         "fingers": [ "loop", "band", "ring" ],
  904.         "hands"  : [ "gloves" ],
  905.         "head"   : [ "hat", "cap", "helm", "conical helm" ],
  906.         "neck"   : [ "collar", "necklace", "pendant", "amulet" ],
  907.         "shins"  : [ "greaves" ],
  908.         "waist"  : [ "sash", "belt", "girdle", "cincture" ]
  909.     };
  910.     var weapons = [ "club", "knife", "broomstick", "cane", "dagger", "staff", "axe", "mace", "shortsword", "spear", "battleaxe", "longsword", "warhammer" ];    // roughly worst to best
  911.  
  912.     switch (tc_menu_inv_state) {
  913.     case 0: return;
  914.     case 1:     // Switch to Equip
  915.         tc_settab("equip");
  916.         tc_menu_inv_state++;
  917.         break;
  918.     case 2:     // Grab lists of equipment, inventory and switch to adventure
  919.         tc_menu_inv.equip = document.querySelectorAll(".inv-equip .equip .equip-slot"); // store in static var
  920.         tc_menu_inv.inv = document.querySelectorAll(".item-table tr");
  921.  
  922.         // Build a map of item -> qty
  923. /*          for (let row of document.querySelectorAll(".adventure .raid-bottom .inv .item-table tr")) {
  924.             // table has 4 columns: name + 3 buttons: Equip, Take, Sell
  925.             if (row.children[3].children[0].innerText == "Sell") {
  926.                 var item = row.children[0].innerText;
  927.                 var qty = items.get(item);
  928.                 items.set(item, qty ? qty+1 : 1);
  929.             }
  930.         } */
  931.         tc_settab("adventure");
  932.         tc_menu_inv_state++;
  933.         break;
  934.     case 3:     // Grab list of loot and populate inventory screen
  935.         var loots = document.querySelectorAll(".inv .item-table tr");
  936.         var equips = tc_menu_inv.equip;
  937.         var invs = tc_menu_inv.inv;
  938.  
  939.         // equip is always going to be 11 (as it's body slots, not items), so not actually much use
  940.         if (tc_debug) console.log("Loot items = " + loots.length + ", equip = " + equips.length + ", inv = " + invs.length);
  941.  
  942.         // Can use document.querySelectorAll(".menu-content")[0].clientWidth and clientHeight to get size of area to use for display.
  943.         // Can also use getBoundingClientRect(). x y width height top left etc.  Not sure if this is useful.
  944.         var size = document.querySelectorAll(".menu.game-mid")[0].getBoundingClientRect();
  945.         var divstyle = document.getElementById("tc_inv_main").style;
  946.  
  947.         divstyle.background = window.getComputedStyle(document.body, null).getPropertyValue('background-color');
  948.         divstyle.width = size.width + "px"
  949.         divstyle.height = size.height + "px"
  950.         divstyle.top = size.top + "px"
  951.         divstyle.left = size.left + "px"
  952.         divstyle.display = "block";
  953.  
  954.         var html;
  955.  
  956.         // Loot
  957.         html="";
  958.         var loot = new Map();
  959.         for (let row of loots) {
  960.             // table has 4 columns: name + 3 buttons: Equip, Take, Sell, except for e.g. Nymie's Charm and Pumpkin Cider
  961.             var item = row.children[0].innerText;
  962.             if (row.children.length == 4 && row.children[1].children[0].innerText == "Equip") {
  963.                 var rows = loot.get(item);
  964.                 if (!rows)
  965.                     loot.set(item, [ row ] );
  966.                 else {
  967.                     rows.push(row);
  968.                     loot.set(item, rows);
  969.                 }
  970.             }
  971.             else {
  972.                 // Some sort of unusual item, add it to a list at the bottom
  973.                 html += item + "<br>";
  974.             }
  975.         }
  976.         if (html != "")
  977.             document.getElementById("tc_inv_loot").innerHTML = "<b>Miscellaneous loot:</b><br>" + html;
  978.         if (tc_debug) console.log("Loot map items: " + loot.size);
  979. //      tc_loot = loot; // remove
  980.  
  981.         // Equip
  982.         // First generate empty tables for our loot.
  983.         html="";
  984.         for (let row of equips) {
  985.             // <tr data-v-4284ca61="" class="equip-slot"><td data-v-4284ca61="" class="slot-name">neck:</td> <td data-v-4284ca61="" class="slot-item"><div data-v-4284ca61=""><span data-v-4284ca61="" class="item-name">amulet</span> <button data-v-4284ca61="">Unequip</button></div><div data-v-4284ca61=""><span data-v-4284ca61="" class="item-name">bone necklace</span> <button data-v-4284ca61="">Unequip</button></div></td></tr>
  986.             var bodypart = row.children[0].innerText;
  987.             bodypart = bodypart.slice(0, -1);   // remove trailing ':'
  988.             html += "<span id='tc_inv_equip_" + bodypart + "'><b>" + bodypart + " : </b></span>";
  989.             html += "<br>";
  990.             if (bodypart == "left")
  991.                 continue;   // don't separate left and right
  992.             else
  993.                 html += "<table style='border:none' id='tc_inv_" + bodypart + "'></table>";
  994.         }
  995.         document.getElementById("tc_inv_equip").innerHTML = html;
  996.  
  997.         // then populate loot into above tables
  998.         for (let row of equips) {
  999.             // <tr data-v-4284ca61="" class="equip-slot"><td data-v-4284ca61="" class="slot-name">neck:</td> <td data-v-4284ca61="" class="slot-item"><div data-v-4284ca61=""><span data-v-4284ca61="" class="item-name">amulet</span> <button data-v-4284ca61="">Unequip</button></div><div data-v-4284ca61=""><span data-v-4284ca61="" class="item-name">bone necklace</span> <button data-v-4284ca61="">Unequip</button></div></td></tr>
  1000.             var bodypart = row.children[0].innerText;
  1001.             bodypart = bodypart.slice(0, -1);   // remove trailing ':'
  1002.             if (row.children[1].children.length > 0) {
  1003.                 var e = document.getElementById("tc_inv_equip_" + bodypart);
  1004.                 while (row.children[1].children.length > 0) {
  1005. //                  html += ", " + row.children[1].children[i].children[0].innerText;
  1006.                     e.insertBefore(row.children[1].children[0], null);  // this actually moves (rather than copies) the child out of row
  1007.                 }
  1008.             }
  1009.             if (bodypart == "left")
  1010.                 continue;   // don't separate left and right
  1011.             else if (bodypart == "right") {
  1012.                 // do all weapons
  1013.                 var table = document.getElementById("tc_inv_" + bodypart);
  1014.                 for (let i = materials.length-1; i >= 0; i--) {
  1015.                     for (let j = weapons.length-1; j >= 0; j--) {
  1016.                         var name = materials[i] + weapons[j];
  1017.                         var item = loot.get(name);
  1018.                         if (item) {
  1019. //                          html += name + " x " + item.length + "<br>";
  1020.                             for (let k of item)
  1021.                                 table.insertBefore(k, null);
  1022.                         }
  1023.                     }
  1024.                 }
  1025.             }
  1026.             else {
  1027.                 // armor
  1028.                 var table = document.getElementById("tc_inv_" + bodypart);
  1029.                 for (let i = materials.length-1; i >= 0; i--) {
  1030.                     var armor = armors[bodypart];
  1031.                     for (let j = armor.length-1; j >= 0; j--) {
  1032.                         var name = materials[i] + armor[j];
  1033.                         var item = loot.get(name);
  1034.                         if (item) {
  1035. //                          html += name + " x " + item.length + "<br>";
  1036.                             for (let k of item)
  1037.                                 table.insertBefore(k, null);
  1038.                         }
  1039.                     }
  1040.                 }
  1041.             }
  1042.         }
  1043.  
  1044.         // Inv - don't really do anything with this
  1045.         html="";
  1046.         html = "<b>Inventory:</b><br>";
  1047.         for (let row of invs) {
  1048.             html += row.children[0].innerText + "<br>";
  1049.         }
  1050.         document.getElementById("tc_inv_inv").innerHTML = html;
  1051.  
  1052.         tc_menu_inv_state++;
  1053.         break;
  1054.     case 4:
  1055.         // Sit back and wait for something to happen
  1056.         break;
  1057.     }
  1058. }
  1059.  
  1060. function tc_menu_inv_init()
  1061. {
  1062.     if (tc_debug) console.log("menu inventory entered");
  1063.     tc_menu_inv_state = 1;
  1064.  
  1065. //  tc_settab("adventure");
  1066. }
  1067.  
  1068. function tc_menu_inv_close()
  1069. {
  1070.     document.getElementById("tc_inv_main").style.display = "none";
  1071.     if (tc_debug) console.log("menu inventory closed");
  1072.     tc_menu_inv_state = 0;
  1073. }
  1074.  
  1075. // Dealing with inventory
  1076. function tc_inv_setup()
  1077. {
  1078.     // Create a menu item (tab) "Inventory".
  1079.     var dummy = document.createElement('div');  // this div won't be included, only the HTML below
  1080.     var html = `<div class="menu-item"><span id="tc_menu_inv"><u> inventory </u></span></div>`;
  1081.     dummy.innerHTML = html;
  1082.     document.querySelectorAll(".menu.game-mid .menu-items")[0].insertBefore(dummy, null);
  1083. //  document.body.firstElementChild.appendChild(dummy);
  1084.     document.getElementById("tc_menu_inv").addEventListener("click", tc_menu_inv_init);
  1085.  
  1086.     // Create a placeholder to store inventory.
  1087.     dummy = document.createElement('div');  // this div won't be included, only the HTML below
  1088.     html = `
  1089. <div id="tc_inv_main" class="menu-content" style="display:none">
  1090. <span title="This page does not update automatically. To switch to another tab, press the Close button first. On exit, it looks like loot has disappeared, switch tabs to refresh the loot list. Suggest using Sell dupes before entering this screen."><b>Equipped items and Loot</b></span>
  1091. <button type="button" id="tc_inv_close" style="float:right; position:sticky; top:5px">Close</button><br>
  1092. <hr>
  1093. <div id="tc_inv_equip"></div>
  1094. <hr>
  1095. <div id="tc_inv_inv"></div>
  1096. <hr>
  1097. <div id="tc_inv_loot"></div>
  1098. </div> `;
  1099.     dummy.innerHTML = html;
  1100.     document.querySelectorAll(".menu.game-mid")[0].insertBefore(dummy, null);
  1101.     document.getElementById("tc_inv_close").addEventListener("click", tc_menu_inv_close);
  1102.  
  1103.     if (tc_debug) console.log("Inventory tab added");
  1104. }
  1105.  
  1106.  
  1107. // Functions to load and save settings from local storage and display configuration dialog.
  1108.  
  1109. function tc_load_settings()
  1110. {
  1111.     // All data stored in localStorage is of type string - need to convert it back.
  1112.     // Also allow default values for first time running script.
  1113.     function get_val(name, default_val, type) {
  1114.         var val = localStorage.getItem(name);
  1115.         if (val === null) return default_val;
  1116.         if (type === "bool") return val === "true";
  1117.         if (type === "int") return parseInt(val);
  1118.         return val;
  1119.     }
  1120.  
  1121.     // Set default values here to be "noob-friendly"
  1122.     tc_suspend = get_val("tc_suspend", false, "bool");
  1123.     tc_auto_cast = get_val("tc_auto_cast", true, "bool");
  1124.     tc_skipcast = get_val("tc_skipcast", true, "bool");
  1125.     tc_auto_focus = get_val("tc_auto_focus", true, "bool");
  1126.     tc_auto_heal = get_val("tc_auto_heal", true, "bool");
  1127.     tc_auto_misc = get_val("tc_auto_misc", true, "bool");
  1128.     tc_use_sublimate = get_val("tc_use_sublimate", true, "bool");
  1129.     tc_auto_gather = get_val("tc_auto_gather", true, "bool");
  1130.     tc_auto_grind = get_val("tc_auto_grind", true, "bool");
  1131.     tc_auto_speed = get_val("tc_auto_speed", 1000, "int");
  1132.     tc_auto_speed_spells = get_val("tc_auto_speed_spells", 950, "int");
  1133.     tc_auto_earn_gold = get_val("tc_auto_earn_gold", false, "bool");
  1134.     tc_auto_adv = get_val("tc_auto_adv", true, "bool");
  1135.     tc_adventure_wait = get_val("tc_adventure_wait", 30, "int");    // needs to be below tc_auto_speed
  1136.     tc_adventure_wait_cd = tc_adventure_wait;   //sets current cooldown to same time as wait period.
  1137.     tc_auto_focus_aggressive = get_val("tc_auto_focus_aggressive", false, "bool");
  1138.     tc_debug = get_val("tc_debug", false, "bool");
  1139.  
  1140.     document.getElementById("tc_suspend").checked = !tc_suspend;    // this one's backwards
  1141.     document.getElementById("tc_auto_cast").checked = tc_auto_cast;
  1142.     document.getElementById("tc_skipcast").checked = tc_skipcast;
  1143.     document.getElementById("tc_auto_focus").checked = tc_auto_focus;
  1144.     document.getElementById("tc_auto_heal").checked = tc_auto_heal;
  1145.     document.getElementById("tc_auto_misc").checked = tc_auto_misc;
  1146.     document.getElementById("tc_use_sublimate").checked = tc_use_sublimate;
  1147.     document.getElementById("tc_auto_gather").checked = tc_auto_gather;
  1148.     document.getElementById("tc_auto_grind").checked = tc_auto_grind;
  1149.     document.getElementById("tc_auto_speed").value = tc_auto_speed;
  1150.     document.getElementById("tc_auto_speed_spells").value = tc_auto_speed_spells;
  1151.     document.getElementById("tc_auto_earn_gold").checked = tc_auto_earn_gold;
  1152.     document.getElementById("tc_auto_adv").checked = tc_auto_adv;
  1153.     document.getElementById("tc_adventure_wait").value = (tc_adventure_wait / 1000 * tc_auto_speed);
  1154.     document.getElementById("tc_auto_focus_aggressive").checked = tc_auto_focus_aggressive;
  1155.     document.getElementById("tc_debug").checked = tc_debug;
  1156. }
  1157.  
  1158. function tc_save_settings()
  1159. {
  1160.     tc_suspend = !document.getElementById("tc_suspend").checked;    // this one's backwards
  1161.     tc_auto_cast = document.getElementById("tc_auto_cast").checked;
  1162.     tc_skipcast = document.getElementById("tc_skipcast").checked;
  1163.     tc_auto_focus = document.getElementById("tc_auto_focus").checked;
  1164.     tc_auto_heal = document.getElementById("tc_auto_heal").checked;
  1165.     tc_auto_misc = document.getElementById("tc_auto_misc").checked;
  1166.     tc_use_sublimate = document.getElementById("tc_use_sublimate").checked;
  1167.     tc_auto_gather = document.getElementById("tc_auto_gather").checked;
  1168.     tc_auto_grind = document.getElementById("tc_auto_grind").checked;
  1169.     tc_auto_speed = parseInt(document.getElementById("tc_auto_speed").value);
  1170.     tc_auto_speed_spells = parseInt(document.getElementById("tc_auto_speed_spells").value);
  1171.     tc_auto_earn_gold = document.getElementById("tc_auto_earn_gold").checked;
  1172.     tc_auto_adv = document.getElementById("tc_auto_adv").checked;
  1173.     tc_adventure_wait = (parseInt(document.getElementById("tc_adventure_wait").value) * 1000 / tc_auto_speed );
  1174.     tc_adventure_wait_cd = tc_adventure_wait;   //sets current cooldown to same time as wait period.
  1175.     tc_auto_focus_aggressive = document.getElementById("tc_auto_focus_aggressive").checked;
  1176.     tc_debug = document.getElementById("tc_debug").checked;
  1177.  
  1178.     localStorage.setItem("tc_suspend", tc_suspend);
  1179.     localStorage.setItem("tc_auto_cast", tc_auto_cast);
  1180.     localStorage.setItem("tc_skipcast", tc_skipcast);
  1181.     localStorage.setItem("tc_auto_focus", tc_auto_focus);
  1182.     localStorage.setItem("tc_auto_heal", tc_auto_heal);
  1183.     localStorage.setItem("tc_auto_misc", tc_auto_misc);
  1184.     localStorage.setItem("tc_use_sublimate", tc_use_sublimate);
  1185.     localStorage.setItem("tc_auto_gather", tc_auto_gather);
  1186.     localStorage.setItem("tc_auto_grind", tc_auto_grind);
  1187.     localStorage.setItem("tc_auto_speed", tc_auto_speed);
  1188.     localStorage.setItem("tc_auto_speed_spells", tc_auto_speed_spells);
  1189.     localStorage.setItem("tc_auto_earn_gold", tc_auto_earn_gold);
  1190.     localStorage.setItem("tc_auto_adv", tc_auto_adv);
  1191.     localStorage.setItem("tc_adventure_wait", tc_adventure_wait);
  1192.     localStorage.setItem("tc_auto_focus_aggressive", tc_auto_focus_aggressive);
  1193.     localStorage.setItem("tc_debug", tc_debug);
  1194.  
  1195.     // Now need to restart timers with new values
  1196.     tc_start_timers();
  1197. }
  1198.  
  1199. function tc_close_config_cancel()
  1200. {
  1201.     var config = document.getElementById("config_options");
  1202.     if (!config) return;
  1203.  
  1204.     config.style.display = "none";
  1205.     if (tc_debug) console.log("config close (cancel) clicked");
  1206. }
  1207.  
  1208. function tc_close_config_save()
  1209. {
  1210.     var config = document.getElementById("config_options");
  1211.     if (!config) return;
  1212.  
  1213.     tc_save_settings();
  1214.     config.style.display = "none";
  1215.     if (tc_debug) console.log("config close (save) clicked");
  1216. }
  1217.  
  1218. function tc_show_config()
  1219. {
  1220.     var config = document.getElementById("config_options");
  1221.     if (!config) return;
  1222.  
  1223.     // Set the background color as the user might have changed modes.
  1224.     // Make it slightly lighter/darker than normal background so it's more obvious.
  1225.     config.style.backgroundColor = document.querySelector("body").classList.contains("darkmode") ? "#333" : "#eee";
  1226.  
  1227.     tc_load_settings();
  1228.     config.style.display = "block";
  1229.     if (tc_debug) console.log("config clicked");
  1230. }
  1231.  
  1232. function tc_config_help()
  1233. {
  1234.     var help = document.getElementById("config_help");
  1235.     if (!help) return;
  1236.  
  1237.     // Set the background color as the user might have changed modes.
  1238.     // Make it slightly lighter/darker than normal background so it's more obvious.
  1239.     help.style.backgroundColor = document.querySelector("body").classList.contains("darkmode") ? "#333" : "#eee";
  1240.  
  1241.     tc_load_settings();
  1242.     help.style.display = "block";
  1243.     if (tc_debug) console.log("config help clicked");
  1244. }
  1245.  
  1246. function tc_help_close()
  1247. {
  1248.     var help = document.getElementById("config_help");
  1249.     if (!help) return;
  1250.  
  1251.     help.style.display = "none";
  1252.     if (tc_debug) console.log("config help closed");
  1253. }
  1254.  
  1255. function tc_config_setup()
  1256. {
  1257.     if (document.getElementById("automate_config")) return;
  1258.  
  1259.     // Try to add the config button to the quickslot bar, but if the user hasn't created any shortcuts yet fall back to quickbar
  1260.     var config = document.querySelectorAll(".quickslot");
  1261.     if (config.length == 0) {
  1262.         config = document.querySelectorAll(".quickbar");
  1263.         if (config.length == 0) return; // nothing to add it to
  1264.     }
  1265.     config = config[0];
  1266.  
  1267.     var configbtn = document.createElement("button");
  1268.     var t1 = document.createTextNode("Configure Automation");
  1269.     configbtn.appendChild(t1);
  1270.     configbtn.id = "automate_config";
  1271.     configbtn.style = "margin-left: auto";  // align right in flexbox
  1272.     configbtn.addEventListener("click", tc_show_config);
  1273.  
  1274.     var dummy = document.createElement('div');  // this div won't be included, only the HTML below
  1275.     // Need to specify a background color or the dialog will be transparent.
  1276.     // Will get fixed to match dark/light mode when it's opened in tc_show_config()
  1277.     // Looks ugly, but will do for now.
  1278.     // Add auto adventuring?
  1279.     // Add equipment considered junk
  1280.     var html = `
  1281. <!-- Configuration Option -->
  1282. <div id="config_options" class="settings popup" style="display:none; background-color:#777; max-width:800px; position: absolute; bottom:15px; right: 15px; top: auto; left: auto;">
  1283. <input type="checkbox" name="tc_suspend" id="tc_suspend" title="If unchecked, all automation is suspended. If checked, items enabled below will be run."> enable automation of items below<br><br>
  1284. <input type="checkbox" name="tc_auto_misc" id="tc_auto_misc"> buy gems, sell herbs, scribe scrolls etc.<br>
  1285. <input type="checkbox" name="tc_use_sublimate" id="tc_use_sublimate"> activate sublimate lore when codices are full<br>
  1286. <input type="checkbox" name="tc_auto_gather" id="tc_auto_gather"> gather herbs when stamina is full and herbs aren't (recommend minimum 2000ms auto interval)<br>
  1287. <input type="checkbox" name="tc_auto_grind" id="tc_auto_grind"> use the grind action when mana is full (recommend minimum 2000ms auto interval)<br>
  1288. <input type="checkbox" name="tc_auto_earn_gold" id="tc_auto_earn_gold" title="Clicks one-off tasks like 'do chores' or 'advise notables' when you have enough stamina and you are in need of gold. "> click actions which make gold when gold is low<br>
  1289. <input type="checkbox" name="tc_auto_focus" id="tc_auto_focus"> click focus while learning skills<br>
  1290. <input type="checkbox" name="tc_auto_cast" id="tc_auto_cast" title="e.g. mana, fount"> cast common buff spells<br>
  1291. <input type="checkbox" name="tc_skipcast" id="tc_skipcast"> but don't cast buffs when not needed (eg. no wild growth when herbs are full)<br>
  1292. <input type="checkbox" name="tc_auto_heal" id="tc_auto_heal"> cast healing spells in combat<br><br>
  1293. <input type="checkbox" name="tc_auto_adv" id="tc_auto_adv"> automatically reenter dungeons <br>
  1294. <input type="text" name="tc_adventure_wait" id="tc_adventure_wait" width=10> number of seconds to wait before reentering an adventure<br>
  1295. <hr>
  1296. <input type="text" name="tc_auto_speed" id="tc_auto_speed" width=10> interval (ms) to run automation functions<br>
  1297. <input type="text" name="tc_auto_speed_spells" id="tc_auto_speed_spells" width=10 title="Should be 1000 but reduce it if lag is causing spell buffs to expire"> interval (ms) for spellcast functions<br>
  1298. <hr>
  1299. Advanced features:<br>
  1300. <input type="checkbox" name="tc_auto_focus_aggressive" id="tc_auto_focus_aggressive" title="Only works while in skills. Attempts to alternate between rests and focus to maximise learning speed. Will switch to lowest level skill when current one is maxed. May have odd behaviour at times."> try to learn faster when in skills tab<br>
  1301. <input type="checkbox" name="tc_debug" id="tc_debug"> send debug info to console<br>
  1302. <hr>
  1303. <button type="button" id="tc_config_help">Help</button>
  1304. <button type="button" id="tc_close_config_cancel" style="float:right">Cancel</button>
  1305. <button type="button" id="tc_close_config_save" style="float:right">Save</button>
  1306. </div>
  1307.  
  1308. <!-- Configuration Help -->
  1309. <div id="config_help" class="settings popup" style="display:none; background-color:#777; max-width:800px; position: absolute; bottom:15px; right: 15px; top: auto; left: auto;">
  1310. <p>
  1311. This automation script is designed to help automate and optimise some basic actions.
  1312. <p>
  1313. Some of the check boxes etc. have tooltips if hovered over. Some additional notes:
  1314. <p>
  1315. "Enable automation ..." if unchecked will stop all automation, except for the casting from the quickbar at the bottom of the screen. The quickbar has a white box on each button - type a time interval in seconds there, and the action will happen at that interval.
  1316. <p>
  1317. "Buy gems, sell herbs etc." should be safe to be enabled all the time. The only major downside currently is it will Sublimate Lore when it is able to do so - this can be a problem at the beginning when it takes longer to produce codices. Sublimate may be split off into its own config option at some point.
  1318. <p>
  1319. "Click actions which make gold" will use stamina. This means that resting may never stop until gold is maxed, which will make adventuring somewhat tedious, so disable it while adventuring.
  1320. <p>
  1321. "Click focus while learning skills" will use mana. The same comment about resting above applies.
  1322. <p>
  1323. The advanced feature "try to learn faster in skills tab" will alternate between using focus and rest to hopefully maximise the speed at which skills are learnt. If a skill is chosen then that skill will be maxed, then the automation will switch between the remaining unlocked skills, learning the lowest-levelled skill until everything is maxed. If no skill is being learnt when entering the skills tab, the automation will go straight to cycling through the skills. If multiple runners are unlocked, it should still work, but there is no advantage in trying to learn two skills at the same time, as only one will get focus. If the automation seems to get stuck on one skill, switch to another tab and stop learning the skill, then switch back to Skills.
  1324. <hr>
  1325. <button type="button" id="tc_help_close" style="float:right">Close</button>
  1326. </div> `;
  1327.  
  1328.     dummy.innerHTML = html;
  1329.     document.body.firstElementChild.appendChild(dummy);
  1330.  
  1331.     config.parentNode.insertBefore(configbtn, null);
  1332.  
  1333.     // Now need to add the onClick handlers for the cancel/save buttons.
  1334.     // Can't do this directly in the HTML above because the GreaseMonkey functions exist in a different namespace
  1335.     document.getElementById("tc_close_config_cancel").addEventListener("click", tc_close_config_cancel);
  1336.     document.getElementById("tc_close_config_save").addEventListener("click", tc_close_config_save);
  1337.     document.getElementById("tc_config_help").addEventListener("click", tc_config_help);
  1338.     document.getElementById("tc_help_close").addEventListener("click", tc_help_close);
  1339.     if (tc_debug) console.log("Config button added");
  1340. }
  1341. function updateResourceColor(){
  1342.     let r = document.querySelectorAll("div.game-main div.res-list div.res-group div.rsrc.separate.hidable");
  1343.     for(let q of r){
  1344.         var values = q.querySelectorAll("span.num-align")[0].innerText.split("/");
  1345.         var pct = values[0]/values[1];
  1346.         if(values[0] === values[1]) {
  1347.             q.style.color='#00b800';
  1348.         } else if(values[0] === 0) {
  1349.             q.style.color='#FF0000';
  1350.         } else if(pct >= 0.9) {
  1351.             q.style.color='#80b800';
  1352.         } else if(pct <= 0.1) {
  1353.             q.style.color='#ff8000';
  1354.         } else {
  1355.             q.style.color='#ffa500';
  1356.         }
  1357.     }
  1358. }
  1359.  
  1360. function tc_configure_for_version()
  1361. {
  1362.     tc_arcanum_ver = parseInt(document.querySelectorAll(".link-bar .vers")[0].innerHTML.split(' ')[1]);
  1363.     // 323 for current lemur site, 890+ on Kong
  1364.  
  1365.     // The default values should always be for the latest version, adjust them here if working with an old version.
  1366.     if (tc_arcanum_ver <= 323) {
  1367.         tc_gems["nature gem"] = "imbue lifegem";
  1368.         tc_gems["earth gem"] = "imbue stone";
  1369.         tc_gems["blood gem"] = "coagulate gem";
  1370.     }
  1371. }
  1372.  
  1373.  
  1374. /*
  1375.     Basic Automation Stuff
  1376. */
  1377.  
  1378. // Main timer for most functions
  1379. var tc_timer_ac;
  1380. // Timer for spells.
  1381. // Can't guarantee that timer will work exactly every second, so can reduce interval to compensate so spells don't run out
  1382. var tc_timer_autocast;
  1383.  
  1384. function tc_start_timers()  // can be restarted by save_settings()
  1385. {
  1386.     if (tc_timer_ac != undefined)
  1387.         window.clearInterval(tc_timer_ac);
  1388.     if (tc_timer_autocast != undefined)
  1389.         window.clearInterval(tc_timer_autocast);
  1390.  
  1391.         tc_config_setup();
  1392.         tc_populate_spells();
  1393.         tc_populate_resources();
  1394.         tc_populate_actions();
  1395.         tc_populate_bars();
  1396.         tc_populate_adventures();
  1397.         tc_populate_running();
  1398.  
  1399.     tc_timer_ac = window.setInterval(function() {
  1400.         tc_config_setup();
  1401.         tc_populate_spells();
  1402.         tc_populate_resources();
  1403.         tc_populate_actions();
  1404.         tc_populate_bars();
  1405.         tc_populate_adventures();
  1406.         tc_populate_running();
  1407.         tc_automate();
  1408.         tc_autofocus();
  1409.         tc_autoheal();
  1410.         tc_autoadv();
  1411.         tc_sellsetup();
  1412.         tc_advsetup();
  1413.         tc_autoearngold();
  1414.         tc_menu_inv();  // handle populating Inventory tab
  1415.         tc_colorpot() // potion coloring
  1416.     }, tc_auto_speed);
  1417.  
  1418.     tc_timer_autocast = window.setInterval(function() {
  1419.         updateResourceColor();
  1420.         iko_autocast();
  1421.         tc_autocast();
  1422.         tc_time_offset++;   // must be done here so it works even if tc_autocast disabled
  1423.     }, tc_auto_speed_spells);
  1424. }
  1425.  
  1426. // Need to make sure page has finished loading before we try to add buttons and set up timers,
  1427. // so check every 100ms for quickbar to become visible before doing anything.
  1428.  
  1429. var tc_load_count = 0;  // just for interest
  1430. var tc_load_timer = window.setInterval(function() {
  1431.     var config = document.querySelectorAll(".quickbar");
  1432.     tc_load_count++;
  1433.     if (config.length == 0) return; // document not loaded yet
  1434.  
  1435.     console.log("Document loaded after " + tc_load_count*100 + " ms");
  1436.  
  1437.     // Do this before we start any timers - loads timer values etc. from local storage.
  1438.  
  1439.     // get Arcanum version and config some vars accordingly.
  1440.     // Need to do this first in case it affects config setup.
  1441.     tc_configure_for_version();
  1442.     tc_config_setup();
  1443.     tc_load_settings();
  1444.     tc_inv_setup();
  1445.  
  1446.     tc_start_timers();
  1447.  
  1448.     window.clearInterval(tc_load_timer);
  1449. }, 100);
Add Comment
Please, Sign In to add comment