1
+ // ==UserScript==
2
+ // @name DIM - Auto Ghost Switcher [Steam only]
3
+ // @description DIM - Auto Ghost Switcher [Steam only]
4
+ // @namespace Revadike
5
+ // @author Revadike
6
+ // @version 1.0.0
7
+ // @include https://app.destinyitemmanager.com/*/d2/inventory
8
+ // @connect steamcommunity.com
9
+ // @grant GM.xmlHttpRequest
10
+ // @grant unsafeWindow
11
+ // ==/UserScript==
12
+
13
+ // ==Config==
14
+ const STEAMID3 = 82699538 ; // steamID3, get from https://steamid.io/lookup/
15
+ const INTERVAL = 1 ; // seconds, time between checking current in-game status
16
+ // ==/Config==
17
+
18
+ // ==Code==
19
+ ( ( ) => {
20
+ "use strict" ;
21
+
22
+ function pickGhost ( ghosts , o_area ) {
23
+ var area = o_area . toLowerCase ( ) ;
24
+ var ghost = ghosts . default ;
25
+
26
+ switch ( true ) {
27
+ case area . includes ( "edz" ) :
28
+ case area . includes ( "earth" ) :
29
+ ghost = ghosts . edzplus || ghosts . edz || ghosts . default ;
30
+ break ;
31
+ case area . includes ( "titan" ) :
32
+ ghost = ghosts . titanplus || ghosts . titan || ghosts . default ;
33
+ break ;
34
+ case area . includes ( "nessus" ) :
35
+ ghost = ghosts . nessusplus || ghosts . nessus || ghosts . default ;
36
+ break ;
37
+ case o_area . includes ( "Io" ) :
38
+ ghost = ghosts . ioplus || ghosts . io || ghosts . default ;
39
+ break ;
40
+ case area . includes ( "mercury" ) :
41
+ ghost = ghosts . mercuryplus || ghosts . mercury || ghosts . default ;
42
+ break ;
43
+ case area . includes ( "mars" ) :
44
+ case area . includes ( "hellas basin" ) :
45
+ ghost = ghosts . marsplus || ghosts . mars || ghosts . default ;
46
+ break ;
47
+ case area . includes ( "tangled shore" ) :
48
+ ghost = ghosts . tangledplus || ghosts . tangled || ghosts . default ;
49
+ break ;
50
+ case area . includes ( "dreaming city" ) :
51
+ ghost = ghosts . dreamingplus || ghosts . dreaming || ghosts . default ;
52
+ break ;
53
+ case area . includes ( "vanguard" ) :
54
+ case area . includes ( "strike" ) :
55
+ ghost = ghosts . strikesplus || ghosts . strike || ghosts . default ;
56
+ break ;
57
+ case area . includes ( "crucible" ) :
58
+ ghost = ghosts . crucibleplus || ghosts . crucible || ghosts . default ;
59
+ break ;
60
+ case area . includes ( "gambit" ) :
61
+ ghost = ghosts . gambitplus || ghosts . gambit || ghosts . default ;
62
+ break ;
63
+ case area . includes ( "leviathan" ) :
64
+ ghost = ghosts . leviathanplus || ghosts . leviathan || ghosts . default ;
65
+ break ;
66
+ case area . includes ( "moon" ) :
67
+ ghost = ghosts . moonplus || ghosts . moon || ghosts . default ;
68
+ break ;
69
+ }
70
+
71
+ console . log ( "Equipping ghost: " + ghost . name ) ;
72
+ ghost . equip ( ) ;
73
+ }
74
+
75
+ function getD2Area ( doc ) {
76
+ var elem = doc . querySelector ( ".rich_presence" ) ;
77
+ return elem ? elem . innerText . trim ( ) : null ;
78
+ }
79
+
80
+ function monitorInGameStatus ( ghosts ) {
81
+ var currentArea = null ;
82
+ var monitor = setInterval ( ( ) => {
83
+ GM . xmlHttpRequest ( {
84
+ method : "GET" ,
85
+ url : "https://steamcommunity.com/miniprofile/" + STEAMID3 + "?t=" + Date . now ( ) ,
86
+ onload : ( response ) => {
87
+ var html = response . responseText ;
88
+ var parser = new DOMParser ( ) ;
89
+ var doc = parser . parseFromString ( html , "text/html" )
90
+ var area = getD2Area ( doc ) ;
91
+ if ( ! area || currentArea === area ) { return ; }
92
+
93
+ console . log ( area ) ;
94
+ currentArea = area ;
95
+ pickGhost ( ghosts , area ) ;
96
+ }
97
+ } ) ;
98
+ } , INTERVAL * 1000 ) ;
99
+ }
100
+
101
+ function doubleClick ( elem ) {
102
+ var event = new MouseEvent ( "dblclick" , {
103
+ view : unsafeWindow ,
104
+ bubbles : true ,
105
+ cancelable : true
106
+ } ) ;
107
+ elem . dispatchEvent ( event ) ;
108
+ }
109
+
110
+ function initGhostSwitcher ( ) {
111
+ console . log ( "DIM ready" ) ;
112
+
113
+ var ghosts = { } ;
114
+ var rows = document . querySelectorAll ( ".store-row" ) ;
115
+
116
+ for ( var row of rows ) {
117
+ if ( ! row . querySelector ( "[aria-label=Ghost]" ) ) { continue ; }
118
+
119
+ var items = row . querySelectorAll ( ".item" ) ;
120
+
121
+ for ( let item of items ) {
122
+ var text = item . querySelector ( "span" ) ;
123
+ if ( ! text ) { continue ; }
124
+
125
+ var aoe = text . innerText . replace ( "+" , "plus" ) ; // area of effect
126
+ if ( ! aoe ) { continue ; }
127
+
128
+ var equip = ( ) => doubleClick ( item ) ;
129
+ var name = item . title ;
130
+ var node = item ;
131
+ var ghost = { equip, name, ghost } ;
132
+ ghosts [ aoe ] = ghosts [ aoe ] || ghost ; // keep first, assuming best is first
133
+ ghosts . default = ghosts . default || ghost ;
134
+ }
135
+ }
136
+
137
+ var arr = Object . values ( ghosts ) ;
138
+ console . log ( "Found " + arr . length + " special ghosts:\n" + arr . map ( i => i . name ) . join ( "\n" ) ) ;
139
+ monitorInGameStatus ( ghosts ) ;
140
+ }
141
+
142
+ function observeDIM ( ) {
143
+ var observer = new MutationObserver ( ( records ) => {
144
+ for ( var record of records ) {
145
+ for ( var node of record . removedNodes ) {
146
+ if ( node . classList && node . classList . contains ( "exit-done" ) ) {
147
+ initGhostSwitcher ( ) ;
148
+ return ;
149
+ }
150
+ }
151
+ }
152
+ } ) ;
153
+
154
+ observer . observe ( document . getElementById ( 'app' ) , { childList : true , subtree : true } ) ;
155
+ }
156
+
157
+ observeDIM ( ) ;
158
+ } ) ( ) ;
159
+ // ==/Code==
0 commit comments