如何用JS写个外挂--不是针对某游

辣鸡游戏毁我青春废我钱财颓我精神,我不做骑空士了!JOJO(🐶

为了流程控制下面会看到乱七八糟的ObserverPromise和让人无语的跳转这些我都没这篇里写

为啥不用await和async!!!

1.鼠标点击

来自某个eval加密的poker脚本,解密随手改的更容易看懂。作用是模拟鼠标点击在某元素内的随即位置上:

position获取元素的起始和大小,还贴心的考虑了缩放;mousetap替代了tigger模拟了点击和松开鼠标两个事件

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
function position(t) {
t instanceof jQuery || (t = $(t));
var e, n = $(document.documentElement).css('zoom');
var o = {
x: 0,
y: 0,
w: t.innerWidth() * n,
h: t.innerHeight() * n
};
var a = t.parents();
if (0 == o.w || 0 == o.h) {
t = t.parent();
o.w = t.innerWidth() * n;
o.h = t.innerHeight() * n
}
if (a.is(document.body)) {
for (var s = t; s[0] != document.body; s = s.parent()) {
e = s.position();
o.y = o.y + e.top * n;
o.x = o.x + e.left * n;
}
}

return o
}

function mousetap(t) {
var e = position(t);
if (!(e.y < 1 || e.x < 1)) {
var n = Math.round(e.x + Math.random() * e.w);
var o = Math.round(e.y + Math.random() * e.h);
var a = new MouseEvent('mouseup', {
view: window,
bubbles: true,
clientX: n,
clientY: o,
cancelable: true
});
var s = new MouseEvent('mousedown', {
view: window,
bubbles: true,
clientX: n,
clientY: o,
cancelable: true
});
var d = $(t);
if (0 != d.length) {
d[0].dispatchEvent(s);
d[0].dispatchEvent(a);
}
}
}

2.页面监听

大致上就是使用MutationObserver嵌套和偶尔监听的click事件

MutationObserver大致如下,注意在合适的地方断开就行了;options只监听当前元素的属性,如显示隐藏,常用于attack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const load = '#wrapper';
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
const options = {
'attributes': true,
'attributeFilter': ['class', 'style']
};
const subtree = {
'attributes': true,
'characterData': true,
'childList': true,
'subtree': true
};
//快点吃药
const elixir = new MutationObserver(function (mutations) {
if ($('.pop-usual.pop-stamina').is(":visible")) {
localStorage.used_elixir++;

setTimeout(function () {
mousetap($('.btn-use-full').eq(1));
}, delay() / 2);
elixir.disconnect();
}
});
elixir.observe(document.querySelector(load), subtree);

3.读取raid信息

乱七八糟的流程就不写了,反正就是Promise then和 = 的大串烧

这个是获取人物信息,至于boss信息,呵呵

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function role() {
//角色id
const role =
[$('.lis-character0').eq(0).find('.img-chara-command').attr('src').split("/").pop().split("_")[0],
$('.lis-character1').eq(0).find('.img-chara-command').attr('src').split("/").pop().split("_")[0],
$('.lis-character2').eq(0).find('.img-chara-command').attr('src').split("/").pop().split("_")[0],
$('.lis-character3').eq(0).find('.img-chara-command').attr('src').split("/").pop().split("_")[0]];
//hp
const heal =
[parseInt($('.lis-character0').eq(0).find('.txt-hp-value').text()),
parseInt($('.lis-character1').eq(0).find('.txt-hp-value').text()),
parseInt($('.lis-character2').eq(0).find('.txt-hp-value').text()),
parseInt($('.lis-character3').eq(0).find('.txt-hp-value').text())];
//bp
const ener = [parseInt($('.lis-character0').eq(0).find('.prt-gauge-special-inner').attr("style").replace(/[^0-9]/g, "")),
parseInt($('.lis-character1').eq(0).find('.prt-gauge-special-inner').attr("style").replace(/[^0-9]/g, "")),
parseInt($('.lis-character2').eq(0).find('.prt-gauge-special-inner').attr("style").replace(/[^0-9]/g, "")),
parseInt($('.lis-character3').eq(0).find('.prt-gauge-special-inner').attr("style").replace(/[^0-9]/g, ""))];

return {role: role, heal: heal, ener: ener};
}

4.战斗流程控制

因为在同一个页面里,只需要加一个全局变量表示当前正在战斗中,一轮过完释放就行

放一段核心代码,嗯看看就好,外层还有不少

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
if ($('#ready').is(":hidden") && $('#loading').is(":hidden") &&
$('.prt-black-bg').is(":hidden")) {
loading.disconnect();
// raid_multi = battle(attack, raid_multi, 1);

let inBattle = false;
const ready = new MutationObserver(function (mutations) {
if ($(attack).hasClass('display-on') && !datas.raid.now) {
println("进入战斗", 1);
inBattle = true;
raid_multi = battle(raid_multi);
}
});
ready.observe(document.querySelector(attack), options);

setTimeout(function () {
if ($(attack).hasClass('display-on') && !inBattle && !datas.raid.now) {
println("进入战斗-重新挂载", 1);
datas.raid.now = true;
raid_multi = battle(raid_multi);
}
}, delay() * 2)
}

技能嘛,进入前根据url hash设置模式、进入后也可以通过回合数改变模式、然后嵌套switch、配置维拉插件没啥难度(说的和真的一样

5.定时任务?

恶心斯我勒,这玩意真能用?定时监听raid还可以,每日裱根本不能看,无论存储localStorage还是像维拉插件那样直接从api读数据都会导致代码复杂度大大增加,容我放弃(快去撸electron啊,看看poi(guna人家是nwjs好吧

6.收尾

某些地方可能因为加载问题导致点击未触发或绑定失败

如果绑定失败很简单sleep后接着点击;如果绑定失败,同上加变量未执行则手动加载一边。当然要注意别加载两遍以上了

我应该不会秃的,大概吧


如何用JS写个外挂--不是针对某游
https://back.pub/post/game-gbf-auto-raid/
作者
Dash
发布于
2016年10月4日
许可协议