forked from mettalogic/arcanum-automation
-
Notifications
You must be signed in to change notification settings - Fork 10
/
automate.user.js
1550 lines (1350 loc) · 62.6 KB
/
automate.user.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// ==UserScript==
// @name aardvark arcanum auto - Harrygiel's fork
// @version 2007
// @author aardvark, Linspatz, Harrygiel, Sing
// @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. Add some hack feature ^^
// @downloadURL https://github.com/Harrygiel/arcanum-automation/edit/master/automate.user.js
// @updateURL https://github.com/Harrygiel/arcanum-automation/edit/master/automate.user.js
// @match http://www.lerpinglemur.com/arcanum/*
// @match https://www.lerpinglemur.com/arcanum/*
// @match https://mathiashjelm.gitlab.io/arcanum/*
// @match https://arcanumtesting.gitlab.io/arcanum/*
// @match http://game312933.konggames.com/gamez/0031/2933/*
// @match https://game312933.konggames.com/gamez/0031/2933/*
// @match http://www.kongregate.com/games/lerpinglemur/theory-of-magic
// @match https://www.kongregate.com/games/lerpinglemur/theory-of-magic*
// @run-at document-idle
// ==/UserScript==
var tc_debug = false; // set to true to see debug messages
function log(message) {
if (tc_debug) return; console.log(message);
}
var tc_suspend = false; // set this to true in console to suspend all auto functions
// Setting to false will stop individual actions
var tc_auto_misc = false;
var tc_use_sublimate = true;
var tc_auto_gather = false;
var tc_auto_grind = true;
var tc_auto_cast = false;
var tc_skipcast = false;
var tc_auto_focus = false;
var tc_auto_earn_gold = false;
var tc_auto_heal = false;
var tc_auto_adv = false;
var tc_auto_focus_aggressive = false;
// Set to a adventure name to continously run that adventure, leave blank to disable
var tc_auto_adventure = "";
var tc_adventure_wait = 3;//How many ticks to wait to rerun an adventure
var tc_adventure_wait_cd = 30;//Counts down
/* The following can be increased by encounters in the adventure listed.
(Stat) - ("dungeon name") (increased amount) (chance to get the encounter needed)
Skills:
Anatomy - "ruined crypt" 0.01 2/7
Spirit Lore - "hestia's cottage" 0.001 1/6, "explore treffil wood" 0.001 1/6
Charms - "hestia's cottage" 0.01 1/6
Enchanting - "hestia's cottage" 0.01 1/6
Potions - "hestia's cottage" 0.01 1/6
Scrying - "hestia's cottage" 0.001 1/6
History - "hestia's cottage" 0.001 1/6, "genezereth" 0.001 1/7
Crafting - "fazbit's workshop" 0.001 1/7
Pyromancy - "fazbit's workshop" 0.001 1/7
Alchemy - "fazbit's workshop" 0.001 2/7
Minerology - "genezereth" 0.001 1/7
Air Lore - "genezereth" 0.001 1/7
Stats:
Arcana - "pidwig's cove" 0.05 1/6
*/
var tc_auto_speed = 1000; // Speed in ms, going too low will cause performance issues.
var tc_auto_speed_spells = 950; // interval in ms for spell casting. should be 1000, but lag can cause spells to run out
var tc_spells = new Map();
var tc_resources = new Map();
var tc_actions = new Map();
var tc_bars = new Map();
var tc_adventures = new Map();
var tc_running = new Map();
var tc_focus;
var tc_rest;
var tc_checked_spells = 0; // have a look at the spell tab on startup
var tc_time_offset = 0; // used for casting spells - this will incr. every second
var tc_runners = 1; // how many runners are unlocked (based on what's seen, so will be a minimum)
var resoure_list = ['gold','research','arcana','scrolls','starcharts','tapestries','runestones','firerune','waterrune','spiritrune','airrune','earthrune','timerune','bodies','bones','skulls','bonedust','souls','schematic','codices','tomes','gems','managem','firegem','watergem','naturegem','earthgem','airgem','shadowgem','lightgem','spiritgem','bloodgem','timegem','voidgem','ichor','dreams','herbs','mana','fire','air','earth','water','nature','shadow','light','spirit','tempus','chaos','void'];
var locale_list = ['loc_ageshall','mustylibrary','loc_spring','eryleyot','loc_treffil','pidwigcove','rithel','ruinedcrypt','hallofmirrors','fazbitshop','genezereth','loc_orrem','loc_ettinmoors','loc_menagerie','loc_tenwick'];
var encounter_list = ['enc_primer1','enc_workbook1','enc_bookworm','mysticwater','manatree','enc_heather','enc_tapestry','foggydale','enc_chest1','enc_chest2','enc_chest3','enc_chest4','enc_primer2','enc_thyffr','enc_delki','enc_gnome','enc_gibber','enc_mummy','eeriemoans','strangebones','enc_embalm1','sarcophagus','enc_rats','hauntedglade','hiddencache','murkywater','enc_blackcat','enc_hest_ward','enc_cauldron','enc_hettie','enc_hestia','enc_pidwig','pidwigtreasure','starrysky1','pidwigstars','sombersunset','brightvista','enc_tome','enc_history','enc_workbook2','enc_furnace','enc_alchemy','enc_statue2','enc_statue4','enc_battle1','enc_mtpass','enc_oldstone','enc_sindel','enc_tenwick','enc_wyrd','enc_gap','enc_mirror1','enc_mirror2','enc_futuremirror','enc_rageemirror','enc_mirrorhall','enc_voidmirror','enc_watermirror','enc_pastmirror','enc_farmirror','enc_sandstorm','enc_oasis','enc_mirage','enc_orremtrade','enc_madwinds','orrem_rains','enc_orrem_cave','enc_caravan','enc_aeonclock','e_bloodgrass','e_spidermass','e_snakemass','e_agolith','e_cockatrice','e_trumple','e_balmuth','e_moss_portal','e_big_scale','e_gryffon','e_wyvern','e_hydra','e_barghest','e_phoenix','e_pogler','e_flithy','e_bestiary1','e_bestiary2']
var task_with_length =['errands','prestidigitation','heist','spellbook','act_scry','act_concoct','act_mine','dreamweaver','spingold','bloodsiphon','graverob','murder','vileexperiment','dissect','grindbones','embalm','paidseance','trapsoul','indulge','chant','eatchildren','sabbat','a_oppress','geas','weavetapestry','craftrune','craftschematic','demonbag','mapstars','bestiary','bestiary2','compiletome','codexannih','markhulcodex','sylvansyllabary','dwarfbook','lemurlexicon','demondict','arazorannals','orremannals','malleus','terraform','remakehammer','maketitanhammer','fazbitfixate','coporisfabrica','unendingscroll','unendingcodex','unendingtome','almagest','craftgem','craftfirerune','craftearthrune','craftairrune','craftwaterrune','craftspiritrune','phylactory','up_lich','animalfriend','summonfamiliar']
var dungeon_list = ['sunnyfield','placidgrove','pestcontrol','stonyhills','ettinmarchcamp','fetidbarrow','treffilwoods','underden','veldranswreck','aragheights','hauntedmanor','sereditetemple','goblincamp','orccamp','aragogres','spidercave','aragwastes','mtgorborung','greatbog','catacrypts','elementrift','veldransstorehouse','desillagrotto','charredkeep','belowgorborung','temple of strativax','holyhall']
var function_list = ['un_stress', 'max_space', 'unlock_tasks_and_upgrades'];
// List of Gems that needs to be updated manually if changed.
var tc_gems = {
"arcane gem": "imbue gem (arcane)",
"fire gem": "imbue gem (fire)",
"water gem": "imbue gem (water)",
"nature gem": "imbue gem (nature)", //updated from lifegem to gem, v1086 change
"earth gem": "imbue stone (earth)",
"air gem": "imbue gem (air)",
"shadow gem": "imbue gem (shadow)",
"light gem": "imbue gem (light)",
"spirit gem": "imbue gem (spirit)",
"blood gem": "coagulate gem (blood)",
};
// List of spells to autocast when needed without using quickbar (and interval to cast at)
var tc_autospells = {
"calming murmurs": 45,
"soothing breeze": 45,
"minor mana" : 30,
"lesser mana" : 60,
"mana" : 120,
"minor fount" : 30,
"fount" : 60,
"greater fount" : 90,
"wild growth" : 45,
"abundance" : 60,
"unearth" : 120,
"unseen servant" : 45,
"guided strike" : 45,
"true strike" : 45,
"perfect strike" : 45,
"whisper" : 50,
"insight" : 60,
"wind sense" : 60,
"water sense" : 60,
"fire sense" : 60,
"whirling step" : 45,
"whirling step II" : 60,
"whirling step III" : 80,
"dust devil II" : 45,
"adamant shell" : 180,
"pulsing light" : 45,
"pulsing light II" : 60,
"pulsing light III" : 120,
"copper skin" : 30,
"stone skin" : 50,
"iron skin" : 60,
};
// One-off actions that earn gold ordered from best to worst (gold/stamina), and stamina cost/click.
// Some also have side effects: ignore actions with negative side effects including using mana (conflict with autofocus).
var tc_actions_gold = {
"treat ailments" : 0.2, // 5 (1) / .2
"advise notables" : 0.3, // 4.5 (0.35 + .1 + .1 + .3 + .5) / .3 - after 1500 turns
"do chores" : 0.17, // 2.94 (0.3 + .1 + .1) / 0.17 - after 250 turns
"clean stables" : 0.08, // 2.5 (0.2 / 0.08)
"gather herbs" : 0.3, // 1.33 (2 / 0.3*5) - assumes we are auto-selling surplus herbs
};
// Used to override tc_actions_gold if not empty
var tc_auto_earn_gold_override = '';
// Call this every second - will automatically pick up new spells
function tc_populate_spells() {
// It can be confusing that autocast doesn't do anything until the spells tab is visited,
// so switch to it on startup and grab anything there.
if (tc_checked_spells == 0) {
if (!tc_settab("spells")) // this might fail if spells not available yet
tc_checked_spells++;
tc_checked_spells++;
// wait for tab to be displayed
return;
}
if (tc_gettab() !== "spells") return;
for (let qs of document.querySelectorAll(".spells .bottom .spellbook table tr")) {
if (qs.childElementCount == 3) {
var spell = qs.children[1].innerHTML.toLowerCase();
if (!tc_spells.get(spell) && !qs.children[2].firstChild.disabled) {
// Don't save spells we haven't learnt yet.
if (qs.children[2].firstChild.innerText.toLowerCase() == "cast") {
tc_spells.set(spell, qs.children[2].firstChild);
log("Saved spell: " + spell);
}
}
}
}
if (tc_checked_spells == 1) {
// switch tab succeeded and we've grabbed the spells, so switch back to main
tc_settab("main");
tc_checked_spells++;
}
}
// Call this every second to update resource values
function tc_populate_resources() {
var resources = document.querySelectorAll("div.game-main div.resource-list tr.item-name:not(.locked)");
if (resources.length == 0)
resources = document.querySelectorAll("div.game-main div.res-list div");
for (let n of resources) {
var name = n.firstElementChild.innerHTML.toLowerCase();
var vals = n.lastElementChild.innerHTML.split("/");
var val0 = parseInt(vals[0]);
var val1 = parseInt(vals[1]);
tc_resources.set(name, [val0, val1]);
}
}
// Call every second to update mana bars
function tc_populate_bars() {
for (let n of document.querySelectorAll("div.game-main div.vitals .hidable.statbar")) {
var name = n.firstElementChild.innerHTML.toLowerCase();
var vals = n.querySelectorAll("span.bar-text")[0].innerText.split("/");
var val0 = parseFloat(vals[0]);
var val1 = parseFloat(vals[1]);
tc_bars.set(name, [val0, val1]);
}
}
// Call every second to look for new buttons and ones that are now active.
function tc_populate_actions() {
if (tc_gettab() !== "main") return;
for (let qs of document.querySelectorAll(".main-tasks .task-list .task-btn:not(.locked) .wrapped-btn:not([disabled])")) {
var key = qs.innerHTML.toLowerCase();
if (!tc_actions.get(key)) {
tc_actions.set(key, qs);
log("Action stored: " + qs.innerHTML);
}
}
for (let qs of document.querySelectorAll(".main-tasks .task-list .task-btn:not(.locked) .wrapped-btn:not([disabled])")) {
var key = qs.innerHTML.toLowerCase();
if (!tc_actions.get(key)) {
tc_actions.set(key, qs);
log("Action stored: " + qs.innerHTML);
}
}
}
// Call every second to look for new adventures and adventures that are now active.
function tc_populate_adventures() {
if (tc_gettab() !== "adventure") return;
//Map is set up as: name, [progress, needed, button]
for (let qs of document.querySelectorAll("div.game-main div.locales div.locale")) {
if (!qs.children[0].children[0].children[1].disabled) {
var name = qs.children[0].children[0].children[0].innerText // name of dungeon
var vals = qs.children[1].innerText.split("/")
tc_adventures.set(name, [vals[0], vals[1], qs.children[0].children[0].children[1]]);
}
}
}
// Call every second to check what you are doing.
function tc_populate_running() {
tc_running.clear();
for (let qs of document.querySelectorAll("div.running div")) {
var key = qs.lastElementChild.innerHTML.toLowerCase();
tc_running.set(key, qs.firstChild);
}
}
// Check if a resource is above a percentage. example: tc_check_resource("gold",.5); // that's not a % lol
function tc_check_resource(resource, percent) {
return !tc_resources.get(resource) || tc_resources.get(resource)[0] >= tc_resources.get(resource)[1] * percent;
}
// Check if a bar(mana etc) is above a percentage.
function tc_check_bars(bars, percent) {
return !tc_bars.get(bars) || tc_bars.get(bars)[0] >= tc_bars.get(bars)[1] * percent;
}
// Check if you are in an adventure
function tc_check_running_adv() {
for (let qs of tc_running.keys()) {
if (qs.split(/⚔|🎃|🌳|📖/).length == 2) return true;
}
return false;
}
// Return name of current tab
function tc_gettab() {
for (let tab of document.querySelectorAll("div.menu-items div.menu-item span")) {
var s = tab.innerHTML;
if (! /<\/u>/.test(s))
return s.slice(1, -1); // strip off leading and trailing space
}
}
// Set current tab to "name", return false if no such tab available.
function tc_settab(newtab) {
for (let tab of document.querySelectorAll("div.menu-items div.menu-item span")) {
if (tab.innerHTML.indexOf(newtab) != -1) {
tab.click();
return true;
}
}
return false; // name not recognised, maybe not unlocked yet
}
// Clicks the selected adventure button
function tc_click_adv(adventure) {
var lcl = tc_adventures.get(adventure);
if (!lcl) return;
if (tc_suspend) return;
if (lcl.disabled) {
log("Adventure '" + adventure + "' was disabled - deleting it");
tc_adventures.delete(adventure);
return;
}
log("Clicking: " + adventure);
lcl[2].click();
return;
}
// Clicks the action button
function tc_click_action(action) {
var act = tc_actions.get(action);
if (!act) return false;
if (act.disabled) { // not sure how this happens, but seems to prevent action ever being called again
log("Action '" + action + "' was disabled - deleting it");
tc_actions.delete(action);
return false;
}
log("Clicking: " + action);
act.click();
return true; // click might still have failed
}
// Clicks the spell button
function tc_cast_spell(spell) {
var spl = tc_spells.get(spell);
if (!spl) return false;
if (spl.disabled) { // not sure how this happens, but seems to prevent action ever being called again
log("Spell '" + spell + "' was disabled - deleting it");
tc_spells.delete(spell);
return false;
}
log("Casting: " + spell);
spl.click();
return true;
}
// Adds an input field to each button on the quickbar to allow casting at regular intervals. Author: iko
function iko_autocast() {
// Stuff for quickslot bar
for (let qs of document.querySelectorAll(".quickslot")) {
// If it doesn't have the text entry box yet then add it.
if (!qs.lastElementChild.classList.contains("timeset")) {
var box = document.createElement("input");
box.setAttribute("type", "text");
box.setAttribute("class", "timeset");
box.setAttribute("style", "position:absolute;bottom:0px;left:0px;width:100%;font-weight:bold;opacity:0.75;text-align:center;");
qs.appendChild(box);
}
var val = parseInt(qs.lastElementChild.value);
if (val > 0 && tc_time_offset % val == 0 && qs.firstElementChild.firstElementChild !== null && qs.lastElementChild !== document.activeElement) {
qs.firstElementChild.firstElementChild.click()
}
}
}
//Function to check spell exclusion rules
function tc_skip_cast(spell) {
//Add sanity checks to some autocasting spells
//No heals at full health
if (spell == "pulsing light" && tc_bars.get("hp")[1] - tc_bars.get("hp")[0] <= 5) return true;
if (spell == "pulsing light II" && tc_bars.get("hp")[1] - tc_bars.get("hp")[0] <= 25) return true;
if (spell == "pulsing light III" && tc_bars.get("hp")[1] - tc_bars.get("hp")[0] <= 50) return true;
//No herb generating spells if herbs are full
if (spell == "wild growth" && tc_check_resource("herbs", 1)) return true;
if (spell == "abundance" && tc_check_resource("herbs", 1)) return true;
//No gem generating spells if gems are full
if (spell == "unearth" && tc_check_resource("gems", 1)) return true;
//No need for unseen servant if gold is full
if (spell == "unseen servant" && tc_check_resource("gold", 1)) return true;
//No exclusion triggered, return false
return false;
}
// For AUTOING. Casts spells listed under autospells
function tc_autocast() {
if (tc_suspend) return;
if (!tc_auto_cast) return;
for (var spell in tc_autospells) {
var rpt = Math.floor(tc_autospells[spell]*1000/tc_auto_speed_spells);
if (tc_time_offset % rpt == 0 && !(tc_skipcast && tc_skip_cast(spell))) {
log("try casting " + spell);
tc_cast_spell(spell);
}
}
}
// For AUTOING. Does several actions
function tc_automate() {
if (tc_suspend) return;
if (!tc_auto_misc) return;
tc_populate_resources();
if (tc_check_resource("herbs", 1) && !tc_check_resource("gold", 1))
var sellamount = tc_resources.get("herbs")[1] / 2;
for (let i = 0; i < sellamount; ++i)
tc_click_action("sell herbs");
if (tc_check_resource("research", 1) && !tc_check_resource("scrolls", 1) && tc_check_bars("mana", .75))
tc_click_action("scribe scroll");
if (!tc_check_resource("codices", 1) && tc_check_resource("scrolls", 1) && tc_check_bars("mana", .5))
tc_click_action("bind codex");
// Selling scrolls can be useful late game when we're automatically generating them,
// and buying scrolls is useful at the start,
// but there was a problem here when scrolls = max-1 and we bought a scroll, then scrolls were maxed but money wasn't
// so next tick we'd sell the scroll and so we'd never be able to max either.
if (tc_resources.get("gold")[0] < tc_resources.get("gold")[1] - 20 && tc_check_resource("scrolls", 1))
tc_click_action("sell scroll");
else if (tc_check_resource("gold", 1) && !tc_check_resource("scrolls", 1))
tc_click_action("buy scroll"); // could fail if scribe above maxed them
// If money maxed, buy gem
if (tc_check_resource("gold", 1) && !tc_check_resource("gems", 1))
tc_click_action("purchase gem");
// If gems maxed, try making some different ones
if (tc_check_resource("gems", 1)) {
var bought_gem = false;
for (var gem in tc_gems) { // try to make one of each
if (!tc_check_resource(gem, 1)) {
log("not maxed " + gem + " calling " + tc_gems[gem]);
if (tc_click_action(tc_gems[gem]))
bought_gem = true;
}
}
// or buy the gem box
if (!bought_gem)
tc_click_action("gem box");
}
// Sublimate lore
if (tc_use_sublimate && tc_check_resource("codices", 1)) {
if (tc_click_action("sublimate lore"))
for (let qs of document.querySelectorAll(".popup"))
// will get some errors in console here as popup matches config screen
if (qs.children[0].children[0] && qs.children[0].children[0].innerHTML == "sublimate lore")
qs.children[1].children[0].click();
}
//Gather herbs if stamina full and herbs are not
if (tc_auto_gather && tc_check_bars("stamina", 1) && !tc_check_resource("herbs", 1))
for (let i = 0; i < 10; ++i)
tc_click_action("gather herbs");
//Grind out some max reserach if max mana
if (tc_auto_grind && tc_check_bars("mana", 1))
for (let i = 0; i < 20; ++i)
tc_click_action("grind");
}
function tc_autoadv() {
if (tc_suspend) return;
if (!tc_auto_adv) return;
/* Only works on Adventure tab but I am not ready to get rid of it yet. ~linspatz
if (tc_gettab()=="adventure"){
var lcl = tc_adventures.get(tc_auto_adventure);
if (!lcl) return;
var advper = lcl[0]/lcl[1];
if (advper == 0 || advper == 1) tc_click_adv(tc_auto_adventure); // this might just need to be advper==1
}
*/
if (tc_check_running_adv() == false) {
if (tc_adventure_wait_cd <= 0) {
tc_click_adv(tc_auto_adventure);
tc_adventure_wait_cd = tc_adventure_wait; // resets countdown
} else {
tc_adventure_wait_cd--;
}
}
}
// Sells all items that are considered junk
function tc_selljunk() {
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"];
var sell_match = ["silk ", "cotton ", "stone ", "leather ", "^wood ", "bone ", "bronze ", "iron ", "^steel "]; // aggressive
// "silk ", "cotton ", "stone ", "leather ", "^wood ", "bone ", "bronze ", "iron ", "^steel ", "quicksteel ", "mithril ", "ebonwood ", "ethereal ", "adamant "
function checkmatch(m) { for (let i of sell_match) if (RegExp(i).test(m)) return true; return false; }
var itemlocation = document.querySelectorAll(".adventure .raid-bottom .inv .item-table .item")
if (itemlocation.length == 0)
itemlocation = document.querySelectorAll(".adventure .raid-bottom .inv table tr")
for (let row of itemlocation) {
// table has 4 columns: name + 3 buttons: Equip, Take, Sell
if (row.children[3].children[0].innerText == "Sell") {
var item = row.children[0].innerText;
if (sell_exact.indexOf(item) != -1 || checkmatch(item)) {
log("Selling: " + item);
row.children[3].click();
}
}
}
}
// Sells any item that you have more than one of
function tc_selldups() {
var items = new Map(); // test
// Build a map of item -> qty
var itemlocation = document.querySelectorAll(".adventure .raid-bottom .inv .item-table .item");
if (itemlocation.length == 0)
itemlocation = document.querySelectorAll(".adventure .raid-bottom .inv table tr");
for (let row of itemlocation) {
// table has 4 columns: name + 3 buttons: Equip, Take, Sell
if (row.children[3].innerText == "Sell") {
var item = row.children[0].innerText;
var qty = items.get(item);
items.set(item, qty ? qty + 1 : 1);
}
}
// Now iterate over rows, selling items where qty > 1
for (let row of itemlocation) {
// table has 4 columns: name + 3 buttons: Equip, Take, Sell
if (row.children[3].innerText == "Sell") {
var item = row.children[0].innerText;
var qty = items.get(item);
var maxqty = 1;
var itemtype = "";
switch (item.split(" ").pop()) {
case "pendant": case "collar": case "amulet": case "necklace":
maxqty = 3;
break;
case "band": case "loop": case "ring":
maxqty = 4;
break;
case "shortsword": case "club": case "cane": case "knife": case "broomstick": case "dagger": case "axe": case "mace":
maxqty = 2;
break;
default:
maxqty = 1
break;
}
}
if (qty > maxqty) {
log("Selling: " + item);
row.children[3].click();
items.set(item, qty - 1);
}
}
}
// Adds a filter input for loot gained from adventures.
function tc_lootfilter() {
var input = document.getElementById("lootfilter");
if (!input) return;
var filter = input.value;
log("filter: " + filter);
if (filter.length == 0) {
// Clear all hidden
for (let row of document.querySelectorAll(".adventure .raid-bottom .inv table tr"))
row.style.display = "";
}
else {
for (let row of document.querySelectorAll(".adventure .raid-bottom .inv table tr"))
if (row.children[0].innerText.indexOf(filter) != -1)
row.style.display = "";
else
row.style.display = "none";
}
}
// Create junk and dupe sell buttons and a loot filter if not already present
function tc_sellsetup() {
if (tc_gettab() != "adventure") return;
if (document.getElementById("selldups")) return;
var sellall = document.querySelectorAll(".adventure .raid-bottom .inv span.top span button");
if (sellall.length = 0)
sellall = document.querySelectorAll(".adventure .raid-bottom .inv div.flex-row button");
if (sellall.length == 0) return; // nothing to sell on tab yet
sellall = sellall[0];
var selljunk = document.createElement("button");
var t1 = document.createTextNode("Sell Junk");
selljunk.appendChild(t1);
selljunk.addEventListener("click", tc_selljunk);
var selldups = document.createElement("button");
var t2 = document.createTextNode("Sell Dupes");
selldups.appendChild(t2);
selldups.addEventListener("click", tc_selldups);
selldups.id = "selldups";
sellall.parentNode.insertBefore(selljunk, null);
sellall.parentNode.insertBefore(selldups, null);
log("Sell buttons added");
}
// Puts a button to set which dungeon to auto and pressing flee will cancel. Code based off of code by Bz
function tc_advsetup() {
if (tc_gettab() !== "adventure") return;
if (tc_suspend) return;
if (!tc_auto_adv) return;
// makes clicking flee disable the auto adventure
if (document.querySelector("div.game-main div.adventure div.explore .raid-btn")) {
document.querySelector("div.game-main div.adventure div.explore .raid-btn").addEventListener("click", function () { tc_auto_adventure = ""; })
}
// Creates an auto button for every adventure.
for (let qs of document.querySelectorAll("div.game-mid div.adventure div.locales div.locale span.separate:first-child")) {
if (qs.lastElementChild.innerText !== "Auto") {
var seldungeon = document.createElement("button");
seldungeon.appendChild(document.createTextNode("Auto"));
seldungeon.addEventListener("click", function () {
tc_auto_adventure = qs.firstElementChild.firstElementChild.innerText;
tc_click_adv(tc_auto_adventure)
});
if (tc_auto_adventure == qs.children[0].children[0].innerText)
seldungeon.setAttribute("style", "color:#1B5E20");
qs.appendChild(seldungeon);
}
}
}
function tc_get_auto_earn() {
// Find which action we can do
var act = tc_auto_earn_gold_override.trim();
if (tc_auto_earn_gold_override.trim()) {
var a = tc_actions.get(act);
if (a && !a.disabled) {
return act;
}
}
for (let act in tc_actions_gold) {
var a = tc_actions.get(act);
if (a && !a.disabled) {
return act;
}
}
return undefined;
}
// Quick and dirty. Needs to be worked on to allow you to set which action to press for gold
function tc_autoearngold() {
if (tc_suspend) return;
if (!tc_auto_earn_gold) return;
var action = tc_get_auto_earn();
if (!action) return; // no money-making actions available to us
var stam = tc_actions_gold[action] || '0.3';
var amt = tc_bars.get("stamina")[0];
var max = tc_bars.get("stamina")[1];
// This is quite efficient while resting, but then need to make sure we don't hit max stamina or resting will stop.
// However if adventuring, this will mean we delay re-entering dungeon as resting never stops until we hit max gold.
var min = max < 11 ? max - 2 : max - 5;
if (amt >= min) {
for (let i = (amt - min) / stam; i > 0; i--) {
if (tc_check_resource("gold", 1)) return; // not sure if this will get updated every click
tc_click_action(action);
}
}
}
var tc_skill_saved = "";
var tc_skill_last = "";
/* This function is called when tc_auto_focus_aggressive is enabled and we have more than one runner.
One runner should always be resting and the other learning the skill. (If more than 2 runners could try multi-rest).
If someone chooses a skill then stick with that until maxed, otherwise rotate amongst them choosing lowest level.
How to tell if someone has specifically chosen a skill? Save current skill and next timeslice see if it's changed.
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.
If there are 3 runners available, we need to switch off the old skill when a new one becomes lowest.
*/
function tc_autofocus_multi(amt) {
var lowest_lvl = 1000;
var lowest_skill = "";
var lowest_btn;
var skill_btn;
var skill_to_learn = "";
var prev_btn;
var prev_skill = "";
for (let qs of document.querySelectorAll(".skills .skill")) {
var skill = qs.firstElementChild.firstElementChild.innerHTML;
var btn = qs.querySelectorAll("button")[0];
var text = btn.innerHTML.trim(); // Can be Unlock, Train, Stop
if (text == "Unlock" || btn.disabled) continue;
// qs.firstElementChild.children[1].childNodes[0].data - to get "Lvl: 3/4"
var lvl = parseInt(qs.firstElementChild.children[1].childNodes[0].data.substr(5).split('/')[0]);
if (lvl < lowest_lvl) {
lowest_lvl = lvl;
lowest_skill = skill;
lowest_btn = btn;
}
if (text == "Stop") { // it means we're training this skill
if (skill != tc_skill_last || skill == tc_skill_saved) {
// the user changed to this skill or originally chose this one so learn it.
log("Learning " + skill + ", last = " + tc_skill_last + ", saved = " + tc_skill_saved);
tc_skill_saved = skill_to_learn = skill;
skill_btn = btn;
}
else if (skill == tc_skill_last) {
// If we don't end up learning this skill, we'll stop training it.
prev_btn = btn;
prev_skill = skill;
}
}
}
if (skill_to_learn == "") {
if (lowest_skill == "") // nothing available to learn
return;
skill_to_learn = lowest_skill;
skill_btn = lowest_btn;
log("Learn lowest skill: " + skill_to_learn);
}
if (skill_btn.innerHTML.trim() == "Train")
skill_btn.click();
tc_skill_last = skill_to_learn;
if (prev_skill != "" && prev_skill != skill_to_learn) {
prev_btn.click();
log("Switch off " + prev_skill);
}
// We're not going to be doing anything else while focussing (apart from autocast spells),
// so just keep enough mana for the 3 mana spells.
if (amt > 2.0) {
tc_focus.disabled = false; // in case button hasn't been updated yet
for (let i = 10 * (amt - 2); i > 0; i--) // 0.1 mana per focus
tc_focus.click();
}
// if tc_runners > 2 could try clicking other sorts of rest as well
tc_rest.click(); // Has no effect if already resting.
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function tc_is_progress_complete(skillElement) {
var progressRaw = skillElement.children[1].childNodes[1].data.trim().substr(10);
var progress = parseInt(progressRaw.split('/')[0].trim().replace('K','000'));
var progressMax = parseInt(progressRaw.split('/')[1].trim().replace('K','000'));
var progressComplete = progress >= progressMax;
if (tc_debug) console.log("Skill progress: " + progress + " / " + progressMax + " complete " + (progressComplete ? "yes" : "no"));
return progressComplete;
}
// Uses focus until you have only 14 mana left.
async function tc_autofocus()
{
if (tc_suspend) return;
if (!tc_auto_focus) return;
if (!tc_focus || !tc_rest)
for (let qs of document.querySelectorAll(".vitals div.separate button.btn-sm")) {
if (!tc_focus && qs.innerHTML === "Focus")
tc_focus = qs;
if (!tc_rest && qs.innerHTML.trim() === "rest")
tc_rest = qs;
}
if (!tc_bars.get("mana")) return;
var amt = tc_bars.get("mana")[0];
var max = tc_bars.get("mana")[1];
if (tc_gettab() != "skills" || !tc_auto_focus_aggressive) {
tc_skill_saved = tc_skill_last = "";
// 10 mana required for compile tome, 3 for Bind Codex, 1 for Scribe Scroll
var min = max < 15 ? max - 1 : 14;
if (amt >= min) {
for (let i = 10 * (amt - min); i > 0; i--)
tc_focus.click();
}
return;
}
// We're on the skills tab - try to: repeat {use up all mana with focusing, then rest to restore mana }
// Note that if we switch tabs while resting, we won't restart learning skill when finished resting.
// As click() actions don't get processed immediately, we need to cheat by un-disabling some buttons.
// Try to find which skill we're currently learning - the user might have switched since we last looked.
// Otherwise if we have a saved skill which isn't maxed, choose that,
// Otherwise learn cheapest skill (only checks level, not learning rate)
// Most useful at start of game, probably won't work well when multiple runners are unlocked.
// If there are multiple runners available, the optimum strategy will be to have (at least) one resting continously,
// while the other one learns.
var r = document.querySelectorAll(".running div button").length;
if (r > tc_runners) tc_runners = r;
if (tc_runners > 1) { // don't want to mess up the working single runner code
tc_autofocus_multi(amt) // available mana
return;
}
var lowest_lvl = 1000;
var lowest_skill = "";
var lowest_btn;
var skill_btn;
var skill_to_learn = "";
var skill_element;
var lowest_element;
for (let qs of document.querySelectorAll(".skills .skill")) {
var skill = qs.firstElementChild.firstElementChild.innerHTML;
var btn = qs.querySelectorAll("button")[0];
var text = btn.innerHTML.trim(); // Can be Unlock, Train, Stop
if (text == "Unlock" || btn.disabled) continue;
if (text == "Stop") { // it means we're training this skill
skill_to_learn = skill;
tc_skill_saved = skill;
skill_btn = btn;
skill_element = qs;
if (tc_debug) console.log("Learning " + skill);
break; // this takes precedence over anything else
}
if (skill == tc_skill_saved) {
// skill still available to be learnt - we'll end up learning this unless another is active
skill_to_learn = skill;
skill_btn = btn;
}
// qs.firstElementChild.children[1].childNodes[0].data - to get "Lvl: 3/4"
var lvl = parseInt(qs.firstElementChild.children[1].childNodes[0].data.substr(5).split('/')[0]);
if (lvl < lowest_lvl) {
lowest_lvl = lvl;
lowest_skill = skill;
lowest_btn = btn;
lowest_element = qs;
}
}
if (skill_to_learn == "") {
if (lowest_skill == "") // nothing available to learn
return;
skill_to_learn = lowest_skill;
skill_btn = lowest_btn;
skill_element = lowest_element;
if (tc_debug) console.log("Learn lowest skill: " + skill_to_learn);
}
if (skill_btn.innerHTML.trim() == "Train") {
if (tc_debug) console.log("Clicking train for skill");
skill_btn.click();
if (tc_is_progress_complete(skill_element)) {
if (tc_debug) console.log("Waiting for train to register");
await sleep(200);
}
}
// We're not going to be doing anything else while focussing (apart from autocast spells),
// so just keep enough mana for the 3 mana spells.
if (amt > 2.0) {
tc_focus.disabled = false; // button probably hasn't been updated yet, but we can cheat
for (let i = 10 * (amt - 2); i > 0; i--) // 0.1 mana per focus
tc_focus.click();
}
tc_rest.disabled = false;
tc_rest.click();
}
// Autoheal based on avalibility of spells and need.
function tc_autoheal() {
if (tc_suspend) return;
if (!tc_auto_heal) return;
if (tc_spells.has("sealing light iii")) {
if (tc_bars.get("hp")[1] - tc_bars.get("hp")[0] >= 100 && tc_bars.get("light")[1] >= 7)
tc_cast_spell("sealing light iii")
return;
}
if (tc_spells.has("sealing light ii")) {
if (tc_bars.get("hp")[1] - tc_bars.get("hp")[0] >= 50 && tc_bars.get("light")[1] >= 5)
tc_cast_spell("sealing light ii");
} else if (tc_spells.has("sealing light")) {
if (tc_bars.get("hp")[1] - tc_bars.get("hp")[0] >= 15 && tc_bars.get("light")[1] >= 5)
tc_cast_spell("sealing light");
}
}
//coloring potions in red :D
function tc_colorpot() {
if (tc_gettab() !== "equip") return;
for (let item of document.querySelectorAll(".item-table .separate")) {
if (item.children[1].innerHTML == "Use") {
item.children[0].style["background-color"] = "red";
}
}
}
var tc_menu_inv_state = 0;
/* We need to switch tabs to get lists of equipment, but need to allow time for tab to populate,
so create a state machine, and do one action each tick.
*/
function tc_menu_inv() {
var materials = ["", "silk ", "cotton ", "stone ", "leather ", "wood ", "bone ", "bronze ", "iron ", "steel ", "quicksteel ", "ethereal ", "ebonwood ", "idrasil ", "mithril ", "adamant "]; // worst to best
var armors = { // items ordered worst to best within groups
"back": ["cape", "robe", "cloak", "wings"],
"chest": ["jerkin", "padded armor", "chainmail", "plate mail"],
"feet": ["boots"],
"fingers": ["loop", "band", "ring"],
"hands": ["gloves"],
"head": ["hat", "cap", "helm", "conical helm"],
"neck": ["collar", "necklace", "pendant", "amulet"],
"shins": ["greaves"],
"waist": ["sash", "belt", "girdle", "cincture"],
"trinket": []
};
var weapons = ["club", "knife", "broomstick", "cane", "dagger", "staff", "axe", "mace", "shortsword", "spear", "battleaxe", "longsword", "warhammer"]; // roughly worst to best
switch (tc_menu_inv_state) {
case 0: return;
case 1: // Switch to Equip
tc_settab("equip");
tc_menu_inv_state++;
break;
case 2: // Grab lists of equipment, inventory and switch to adventure
tc_menu_inv.equip = document.querySelectorAll(".inv-equip .equip .equip-slot"); // store in static var
tc_menu_inv.inv = document.querySelectorAll(".item-table tr");
// Build a map of item -> qty
/* for (let row of document.querySelectorAll(".adventure .raid-bottom .inv .item-table tr")) {
// table has 4 columns: name + 3 buttons: Equip, Take, Sell
if (row.children[3].children[0].innerText == "Sell") {
var item = row.children[0].innerText;
var qty = items.get(item);
items.set(item, qty ? qty+1 : 1);
}
} */
tc_settab("adventure");
tc_menu_inv_state++;
break;
case 3: // Grab list of loot and populate inventory screen
var loots = document.querySelectorAll(".inv .item-table tr");
var equips = tc_menu_inv.equip;
var invs = tc_menu_inv.inv;
// equip is always going to be 11 (as it's body slots, not items), so not actually much use
log("Loot items = " + loots.length + ", equip = " + equips.length + ", inv = " + invs.length);
// Can use document.querySelectorAll(".menu-content")[0].clientWidth and clientHeight to get size of area to use for display.
// Can also use getBoundingClientRect(). x y width height top left etc. Not sure if this is useful.
var size = document.querySelectorAll(".menu.game-mid")[0].getBoundingClientRect();
var divstyle = document.getElementById("tc_inv_main").style;
divstyle.background = window.getComputedStyle(document.body, null).getPropertyValue('background-color');
divstyle.width = size.width + "px"
divstyle.height = size.height + "px"
divstyle.top = size.top + "px"
divstyle.left = size.left + "px"
divstyle.display = "block";
var html;
// Loot
html = "";
var loot = new Map();
for (let row of loots) {
// table has 4 columns: name + 3 buttons: Equip, Take, Sell, except for e.g. Nymie's Charm and Pumpkin Cider
var item = row.children[0].innerText;
if (row.children.length == 4 && row.children[1].children[0].innerText == "Equip") {
var rows = loot.get(item);
if (!rows)
loot.set(item, [row]);
else {
rows.push(row);
loot.set(item, rows);
}
}
else {
// Some sort of unusual item, add it to a list at the bottom
html += item + "<br>";
}
}