用nodejs写一个yys外挂
为什么要用node来写
曾经用python写过一个自用御魂脚本,作为一个前端码农,就考虑能不能用js实现,js如何来使用天使插件(TSPlug.dll)实现后台操作呢?经google,github发现,已经有大佬实现了轮子winax,能够调用通用COM组件。之后用js写脚本的想法开始付诸行动。
封装TSPlug
考虑为了更方便的调用TSPlug的一些方法,自己动手用ts封装了一个天使插件的库
export default class TSPlug {
private ts: TSInstance;
constructor() {
this.ts = TSPlug.init('ts.tssoft');
}
private static init(COM: string): TSInstance {
try {
return new winax.Object(COM);
} catch {
execSync(`regsvr32 ${resolve(__dirname, '../lib/TSPlug.dll')}`); // 注册插件
return new winax.Object(COM);
}
}
...
}
封装一个TSPlug的类,包含天使插件的所有方法,详细代码ts.dll,有了通用插件之后开始正式写脚本
开始
我们主要实现的是单人多开刷业原火,御灵,组队双开输御魂
项目的目录结构
yys-robot
├── src
│ └── app
│ ├── actions
│ │ ├── bind-window.ts
│ │ ├── find-window.ts
│ │ └── get-screen-config.ts
│ ├── config
│ │ ├── config.ts
│ │ └── shared.ts
│ ├── interfaces
│ │ └── interfaces.ts
│ ├── mode
│ │ ├── single.mode.ts
│ │ └── team.mode.ts
│ ├── workers // 工作进程
│ │ ├── single.worker.js
│ │ ├── single.worker.ts
│ │ ├── team-driver.worker.js
│ │ ├── team-driver.worker.ts
│ │ ├── team-fighter.worker.js
│ │ └── team-fighter.worker.ts
│ ├── app.single.ts // 单人模式入口
│ ├── app.team.ts // 组队模式入口
│ └── app.ts // 程序入口
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── single.bat // 批处理命令快速启动单人模式
├── team.bat // .....快速启动组队模式
├── tsconfig.json
└── tslint.json
config基本配置文件
//config.ts
export const screenConfig = {
color: {
yellow: 'f3b25e', // 黄色的挑战按钮
auto: 'f8f3e0', // 自动战斗
fighterAutoAccept: 'edc791', // 组队模式打手自动接受邀请
normalAccept: '54b05f', // 组队模式常规邀请
blankBattle: '2b2b2b', // 空白的挑战按钮
reward: 'dd7260', // 悬赏关闭按钮
team: 'e9cd73', // 组队挑战按钮
defaultInvitation: '725f4d', // 默认邀请按钮,
fontCooperative: 'f8f3e0', // 协战队伍文字颜色,
fighterReady: '221611'
},
position: { // range 为随机点击区域的范围
singleBattle: [807, 422],
singleBattleRange: [807, 807 + 74, 422, 422 + 17],
teamBattle: [1091, 576],
teamBattleRange: [1070, 1105, 572, 630],
auto: [71, 577],
settlement: [980, 1030, 225, 275],
teamSettlement: [189, 333, 23, 76],
continueInviteButton: [724, 396],
continueInviteButtonRange: [724 - 5, 724 + 5, 396 - 5, 396 + 5],
rejectRewardButton: [750, 458],
rejectRewardButtonRange: [745, 755, 453, 463],
defaultInvitationButton: [499, 321],
defaultInvitationButtonRange: [489, 509, 311, 331],
blankBattleButton: [1067, 585],
autoAcceptButtonRange: [16, 366, 122, 465], // 自动接受按钮出现的区域,方便findColor找色
fontCooperativeRange: [68, 204, 14, 60], // 协战队伍文字出现的区域,
fighterReadyPosition: [985, 587] // 辅助位置,组队打手进入了备战界面
}
};
// shared.ts 全局共享对象
import TSPlug from 'ts.dll';
import {Shared} from '../interfaces/interfaces';
export const shared: Shared = {
original: new TSPlug(),
handles: [] // all window handle
};
actions存放基本通用方法
//find-window.ts 查找窗口句柄
import TSPlug from 'ts.dll';
import {shared} from '../config/shared';
export function findWindow(ts: TSPlug) {
//通过进程id获取窗口句柄,返回逗号分隔的字符串
const windowHandelString = ts.enumWindowByProcess('client.exe', '', '', 16); // onmyoji.exe 进程id可能会不一样
if (windowHandelString) {
const windowHandelRaw = windowHandelString.split(',');
// 存储全局共享的handle
const windowHandel = shared.handles = windowHandelRaw.map(v => Number(v));
return [...windowHandel];
} else {
console.log('not found window handle');
return [];
}
}
// bind-window.ts 通过窗口句柄绑定窗口实现后台运行
import TSPlug from 'ts.dll';
export function bindWindow(handle: number) {
const ts = new TSPlug();
const ret = ts.bindWindow(handle, 'dx2', 'windows', 'windows', 0);
if (ret === 1) {
console.log('bind window success');
return ts;
}
throw {message: 'bind window failed'};
}
// get-screen-config.ts 根据传入的不同分辨率的比值返回不同的坐标配置(暂时只测试了480*852分辨率,默认分辨率自行修改代码测试)
import {screenConfig} from '../config/config';
export function getScreenConfig(resolution: 1 | 1.333333333 = 1.333333333) {
if (resolution === 1) {
return screenConfig;
}
return transConfig(resolution);
}
function transConfig(resolution: 1 | 1.333333333) {
let i: keyof typeof screenConfig.position;
for (i in screenConfig.position) {
if (screenConfig.position.hasOwnProperty(i)) {
const item = screenConfig.position[i];
screenConfig.position[i] = item.map(v => v / resolution);
}
}
screenConfig.color.auto = 'f4efdc';
return screenConfig;
}
mode单人或双人模式
// single.mode.ts
import {fork} from 'child_process';
import {shared} from '../config/shared';
import {resolve} from 'path';
// 单人模式,每一个窗口应该独立运行,通过child_process 创建子进程
// node子进程不能共享主进程的object,但是可以共享数字,向子进程传输全局共享的handle
export function single() {
for (const handle of shared.handles) {
const worker = fork(resolve(__dirname, '../workers/single.worker.js'));
worker.send(handle);
}
}
// team.mode.ts 组队模式,主逻辑不需要子进程,只需要司机执行一些操作,结算的时候需要分辨创建司机和打手两个子进程来执行不同的操作
import TSPlug from 'ts.dll';
import {shared} from '../config/shared';
import {bindWindow} from '../actions/bind-window';
import {getScreenConfig} from '../actions/get-screen-config';
import {Area} from 'ts.dll/@types/modules/interface';
import {fork} from 'child_process';
import {resolve as pathResolve} from 'path';
export async function team() {
if (shared.handles.length < 2) throw {message: 'window handles less than 2'};
// ======================各种配置=========================
const screenConfig = getScreenConfig();
const {color, position} = screenConfig;
const {team, auto, reward} = color;
const {teamBattleRange, teamBattle, auto: autoButton, settlement, rejectRewardButton, rejectRewardButtonRange} = position;
const teamBattleArea = {
x1: teamBattleRange[0],
x2: teamBattleRange[1],
y1: teamBattleRange[2],
y2: teamBattleRange[3]
};
const rejectRewardArea = {
x1: rejectRewardButtonRange[0],
x2: rejectRewardButtonRange[1],
y1: rejectRewardButtonRange[2],
y2: rejectRewardButtonRange[3]
};
// ===========================================
let driver = bindWindow(shared.handles[0]); // 实例化司机
let fighter = bindWindow(shared.handles[1]); // 实例化打手
const dColor = driver.cmpColor(team, 0.9, teamBattle[0], teamBattle[1]); // 比较指定点的颜色
const fColor = fighter.cmpColor(team, 0.9, teamBattle[0], teamBattle[1]);
if (!fColor) { // 如果打手找到了挑战按钮的颜色,司机和打手交换窗口句柄
console.log('swapping handle');
[driver, fighter] = [fighter, driver];
[shared.handles[0], shared.handles[1]] = [shared.handles[1], shared.handles[0]];
}
if (dColor && fColor) {
console.log('can not found battle button');
throw {message: 'init failed'};
}
console.log(shared.handles);
// 主逻辑
while (true) {
singlePrepare(driver, fighter, teamBattle[0], teamBattle[1], team, teamBattleArea, reward, rejectRewardButton, rejectRewardArea);
inTheBattle(driver, fighter, auto, autoButton, reward, rejectRewardButton, rejectRewardArea);
battleFinished(driver, fighter, auto, autoButton, reward, rejectRewardButton, rejectRewardArea);
await runWorker();
console.log('new cycle');
msleep(1000, 1500);
}
}
// 结算时fork子进程
function runWorker() {
const d = new Promise<any>((resolve) => {
const dWorker = fork(pathResolve(__dirname, '../workers/team-driver.worker.js'));
dWorker.send(shared.handles[0]);
dWorker.on('exit', () => {
resolve();
});
});
const f = new Promise<any>((resolve) => {
const fWorker = fork(pathResolve(__dirname, '../workers/team-fighter.worker.js'));
fWorker.send(shared.handles[1]);
fWorker.on('exit', () => {
resolve();
});
});
return Promise.all([d, f]);
}
// 备战界面
function singlePrepare(driver: TSPlug, fighter: TSPlug, colorX: number, colorY: number, targetColor: string, area: Area, rewardColor: string, rewardButton: number[], rewardArea: Area) {
let flag = false;
while (true) {
rejectReward(driver, rewardColor, rewardButton, rewardArea);
rejectReward(fighter, rewardColor, rewardButton, rewardArea);
const testColor = driver.cmpColor(targetColor, 0.9, colorX, colorY);
if (!testColor) {
flag = true;
}
if (flag) {
randomClick(driver, area);
msleep(1000, 1333);
const testColor1 = driver.cmpColor(targetColor, 0.9, colorX, colorY);
if (testColor1) {
console.log('click battle button');
break;
}
}
msleep(1000, 1888);
}
}
// 战斗进行中
function inTheBattle(driver: TSPlug, fighter: TSPlug, autoColor: string, autoButton: number[], rewardColor: string, rewardButton: number[], rewardArea: Area) {
while (true) {
rejectReward(driver, rewardColor, rewardButton, rewardArea);
rejectReward(fighter, rewardColor, rewardButton, rewardArea);
const battle = isBattle(driver, fighter, autoColor, autoButton);
if (battle) {
break;
}
msleep(200, 400);
}
msleep(100, 200);
console.log('in the battle');
}
// 战斗结束
function battleFinished(driver: TSPlug, fighter: TSPlug, autoColor: string, autoButton: number[], rewardColor: string, rewardButton: number[], rewardArea: Area) {
while (true) {
rejectReward(driver, rewardColor, rewardButton, rewardArea);
rejectReward(fighter, rewardColor, rewardButton, rewardArea);
const battle = isBattle(driver, fighter, autoColor, autoButton);
if (!battle) {
break;
}
msleep(200, 400);
}
msleep(100, 200);
console.log('battle finished');
}
// 判断是否在战斗中
function isBattle(driver: TSPlug, fighter: TSPlug,color: string, position: number[]) {
const testColor = driver.getColor(position[0], position[1]);
const testColor1 = fighter.getColor(position[0], position[1]);
if (testColor === color || testColor1 === color) {
return true;
}
return false;
}
// 正在结算
function inTheSettlement(driver: TSPlug, fighter: TSPlug, settlementButton: number[], battleColor: string, battleButton: number[], rewardColor: string, rewardButton: number[], rewardArea: Area) {
while (true) {
rejectReward(driver, rewardColor, rewardButton, rewardArea);
rejectReward(fighter, rewardColor, rewardButton, rewardArea);
exitSettlement(driver, settlementButton);
exitSettlement(driver, settlementButton);
const testColor = driver.getColor(battleButton[0], battleButton[1]);
if (testColor === battleColor) {
console.log('into ready screen');
break;
}
msleep(500, 1000);
}
console.log('new cycle!');
}
// 随机点击指定区域退出结算
function exitSettlement(ts: TSPlug, position: number[]) {
const [x1, x2, y1, y2] = position;
randomClick(ts, {x1, x2, y1, y2});
}
// 拒绝悬赏,在每一个独立的while循环中必须调用
function rejectReward(ts: TSPlug, rewardColor: string, rewardButton: number[], rewardArea: Area) {
const testColor = ts.getColor(rewardButton[0], rewardButton[1]);
if (testColor === rewardColor) {
randomClick(ts, rewardArea);
msleep(1000, 1232);
console.log('reject reward success');
}
msleep(43, 99);
}
// 随机点击
function randomClick(ts: TSPlug, {x1, x2, y1, y2}: Area) {
const xr = getRandom(x1, x2);
const yr = getRandom(y1, y2);
ts.moveTo(xr, yr);
msleep(100, 300);
ts.leftClick();
msleep(200, 300);
}
// 获取随机整数
function getRandomInt(min: number, max: number) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// 获取随机数
export function getRandom(min: number, max: number) {
return Math.random() * (Math.abs(max - min)) + min;
}
// 随机休眠,阻塞进程
function msleep(a: number, b: number) {
const n = getRandomInt(a, b);
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, n);
}
workers工作进程,由于本项目使用ts开发,fork子进程需要用到.js文件,为了调试方便每一个worker对应需要一个.js文件
在子进程中调用一些其他模块的函数会出现重复定义模块问题,所以下面有大量重复代码,暂时无解
// register ts worker
const path = require('path');
require('ts-node').register();
require(path.resolve(__dirname, './single.worker.ts')); // 替换成对应的.ts文件名
// team-driver.worker.ts 司机结算
import {bindWindow} from '../actions/bind-window';
import TSPlug from 'ts.dll';
import {Area} from 'ts.dll/@types/modules/interface';
import {getScreenConfig} from '../actions/get-screen-config';
// 监听主进程发送过来的窗口句柄
process.on('message', handle => {
console.log('[driver] ' + handle);
// 由于不能共享对象,只能通过窗口句柄,重新实例化TS,和获取各种配置
const ts = bindWindow(handle);
const screenConfig = getScreenConfig();
const {color, position} = screenConfig;
const {yellow, reward, defaultInvitation, blankBattle, team} = color;
const {teamBattle, blankBattleButton, teamSettlement, rejectRewardButton, rejectRewardButtonRange, continueInviteButton, continueInviteButtonRange, defaultInvitationButton, defaultInvitationButtonRange} = position;
const rejectRewardArea = {
x1: rejectRewardButtonRange[0],
x2: rejectRewardButtonRange[1],
y1: rejectRewardButtonRange[2],
y2: rejectRewardButtonRange[3]
};
const defaultInvitationArea = {
x1: defaultInvitationButtonRange[0],
x2: defaultInvitationButtonRange[1],
y1: defaultInvitationButtonRange[2],
y2: defaultInvitationButtonRange[3]
};
const continueInviteArea = {
x1: continueInviteButtonRange[0],
x2: continueInviteButtonRange[1],
y1: continueInviteButtonRange[2],
y2: continueInviteButtonRange[3]
};
// 主逻辑
while (true) {
rejectReward(ts, reward, rejectRewardButton, rejectRewardArea);
exitSettlement(ts, teamSettlement);
const continueColor = ts.getColor(continueInviteButton[0], continueInviteButton[1]);
if (continueColor === yellow) {
const testColor = ts.getColor(defaultInvitationButton[0], defaultInvitationButton[1]);
if (testColor === defaultInvitation) {
findColorAndClick(ts, defaultInvitationButton[0], defaultInvitationButton[1], defaultInvitation, defaultInvitationArea, reward, rejectRewardButton, rejectRewardArea);
console.log('[driver] ticked default Invitation button');
}
findColorAndClick(ts, continueInviteButton[0], continueInviteButton[1], yellow, continueInviteArea, reward, rejectRewardButton, rejectRewardArea);
console.log('[driver] clicked continue invite fighter');
break;
}
const testColor = ts.cmpColor(blankBattle, 0.9, blankBattleButton[0], blankBattleButton[1]);
const testColor1 = ts.cmpColor(team, 0.9, teamBattle[0], teamBattle[1]);
if (!testColor || !testColor1) {
console.log('[driver] driver into ready screen');
break;
}
msleep(500, 1000);
}
console.log('[driver] driver process exit');
process.exit();
});
function findColorAndClick(ts: TSPlug, colorX: number, colorY: number, targetColor: string, area: Area, rewardColor: string, rewardButton: number[], rewardArea: Area) {
let flag = false;
while (true) {
rejectReward(ts, rewardColor, rewardButton, rewardArea);
const testColor = ts.getColor(colorX, colorY);
if (testColor === targetColor) {
flag = true;
}
if (flag) {
randomClick(ts, area);
msleep(1000, 1333);
const testColor1 = ts.getColor(colorX, colorY);
if (testColor1 !== targetColor) {
break;
}
}
msleep(1000, 1888);
}
}
function exitSettlement(ts: TSPlug, position: number[]) {
const [x1, x2, y1, y2] = position;
randomClick(ts, {x1, x2, y1, y2});
}
function rejectReward(ts: TSPlug, rewardColor: string, rewardButton: number[], rewardArea: Area) {
const testColor = ts.getColor(rewardButton[0], rewardButton[1]);
if (testColor === rewardColor) {
randomClick(ts, rewardArea);
msleep(1000, 1232);
console.log('[driver] reject reward success');
}
msleep(43, 99);
}
function randomClick(ts: TSPlug, {x1, x2, y1, y2}: Area) {
const xr = getRandom(x1, x2);
const yr = getRandom(y1, y2);
ts.moveTo(xr, yr);
msleep(100, 300);
ts.leftClick();
msleep(200, 300);
}
function getRandomInt(min: number, max: number) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
export function getRandom(min: number, max: number) {
return Math.random() * (Math.abs(max - min)) + min;
}
function msleep(a: number, b: number) {
const n = getRandomInt(a, b);
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, n);
}
// team-fighter.worker.ts
import {bindWindow} from '../actions/bind-window';
import TSPlug from 'ts.dll';
import {Area} from 'ts.dll/@types/modules/interface';
import {getScreenConfig} from '../actions/get-screen-config';
process.on('message', handle => {
console.log('[fighter] ' + handle);
const ts = bindWindow(handle);
const screenConfig = getScreenConfig();
const {color, position} = screenConfig;
const {reward, fighterAutoAccept, fighterReady} = color;
const {teamSettlement, rejectRewardButton, rejectRewardButtonRange, continueInviteButtonRange, defaultInvitationButtonRange, autoAcceptButtonRange, fighterReadyPosition} = position;
const rejectRewardArea = {
x1: rejectRewardButtonRange[0],
x2: rejectRewardButtonRange[1],
y1: rejectRewardButtonRange[2],
y2: rejectRewardButtonRange[3]
};
const autoAcceptButtonArea = {
x1: autoAcceptButtonRange[0],
x2: autoAcceptButtonRange[1],
y1: autoAcceptButtonRange[2],
y2: autoAcceptButtonRange[3]
};
while (true) {
rejectReward(ts, reward, rejectRewardButton, rejectRewardArea);
exitSettlement(ts, teamSettlement);
const ret1 = ts.findColor(fighterAutoAccept, 1.0, 0, autoAcceptButtonArea);
const {ret: code, x, y} = ret1;
if (code > -1) {
console.log('[fighter] fighter (auto) accept found at', x, y);
const area = {
x1: x - 5,
x2: x + 5,
y1: y - 5,
y2: y + 5
};
findColorAndClick(ts, x, y, fighterAutoAccept, area, reward, rejectRewardButton, rejectRewardArea);
console.log('[fighter] fighter clicked (auto) accept');
}
const testColor = ts.cmpColor(fighterReady, 0.9, fighterReadyPosition[0], fighterReadyPosition[1]);
if (!testColor) {
console.log('[fighter] fighter into ready screen');
break;
}
msleep(500, 1000);
}
console.log('[fighter] fighter process exit');
process.exit();
});
function findColorAndClick(ts: TSPlug, colorX: number, colorY: number, targetColor: string, area: Area, rewardColor: string, rewardButton: number[], rewardArea: Area) {
let flag = false;
while (true) {
rejectReward(ts, rewardColor, rewardButton, rewardArea);
const testColor = ts.getColor(colorX, colorY);
console.log(testColor, targetColor);
if (testColor === targetColor) {
flag = true;
}
if (flag) {
randomClick(ts, area);
msleep(1000, 1333);
const testColor1 = ts.getColor(colorX, colorY);
if (testColor1 !== targetColor) {
break;
}
}
msleep(1000, 1888);
}
}
function exitSettlement(ts: TSPlug, position: number[]) {
const [x1, x2, y1, y2] = position;
randomClick(ts, {x1, x2, y1, y2});
}
function rejectReward(ts: TSPlug, rewardColor: string, rewardButton: number[], rewardArea: Area) {
const testColor = ts.getColor(rewardButton[0], rewardButton[1]);
if (testColor === rewardColor) {
randomClick(ts, rewardArea);
msleep(1000, 1232);
console.log('[fighter] reject reward success');
}
msleep(43, 99);
}
function randomClick(ts: TSPlug, {x1, x2, y1, y2}: Area) {
const xr = getRandom(x1, x2);
const yr = getRandom(y1, y2);
ts.moveTo(xr, yr);
msleep(100, 300);
ts.leftClick();
msleep(200, 300);
}
function getRandomInt(min: number, max: number) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
export function getRandom(min: number, max: number) {
return Math.random() * (Math.abs(max - min)) + min;
}
function msleep(a: number, b: number) {
const n = getRandomInt(a, b);
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, n);
}
// single.worker.ts
import {bindWindow} from '../actions/bind-window';
import {getScreenConfig} from '../actions/get-screen-config';
import {Area} from 'ts.dll/@types/modules/interface';
import TSPlug from 'ts.dll';
process.on('message', handle => {
const screenConfig = getScreenConfig();
const ts = bindWindow(handle);
const {color, position} = screenConfig;
const {yellow, auto, reward} = color;
const {singleBattle, singleBattleRange, auto: autoButton, settlement, rejectRewardButton, rejectRewardButtonRange} = position;
const singleBattleArea = {
x1: singleBattleRange[0],
x2: singleBattleRange[1],
y1: singleBattleRange[2],
y2: singleBattleRange[3]
};
const rejectRewardArea = {
x1: rejectRewardButtonRange[0],
x2: rejectRewardButtonRange[1],
y1: rejectRewardButtonRange[2],
y2: rejectRewardButtonRange[3]
};
while (true) {
singlePrepare(ts, singleBattle[0], singleBattle[1], yellow, singleBattleArea, reward, rejectRewardButton, rejectRewardArea);
inTheBattle(ts, auto, autoButton, reward, rejectRewardButton, rejectRewardArea);
battleFinished(ts, auto, autoButton, reward, rejectRewardButton, rejectRewardArea);
inTheSettlement(ts, settlement, yellow, singleBattle, reward, rejectRewardButton, rejectRewardArea);
msleep(1000, 1500);
}
});
function singlePrepare(ts: TSPlug, colorX: number, colorY: number, targetColor: string, area: Area, rewardColor: string, rewardButton: number[], rewardArea: Area) {
let retryNum = 0;
let retryNum1 = 0;
let flag = false;
while (true) {
retryNum++;
rejectReward(ts, rewardColor, rewardButton, rewardArea);
const testColor = ts.getColor(colorX, colorY);
if (testColor === targetColor) {
flag = true;
} else if (retryNum > 10) {
ts.unBindWindow();
console.log('exit process');
process.exit();
}
if (flag) {
randomClick(ts, area);
msleep(1000, 1333);
const testColor1 = ts.getColor(colorX, colorY);
retryNum1++;
if (testColor1 !== targetColor) {
retryNum1 = 0;
console.log('click battle button');
break;
} else if (retryNum1 > 10) {
ts.unBindWindow();
console.log('exit process');
process.exit();
}
}
msleep(1000, 1888);
}
}
function inTheBattle(ts: TSPlug, autoColor: string, autoButton: number[], rewardColor: string, rewardButton: number[], rewardArea: Area) {
while (true) {
rejectReward(ts, rewardColor, rewardButton, rewardArea);
const battle = isBattle(ts, autoColor, autoButton);
if (battle) {
break;
}
msleep(200, 400);
}
msleep(100, 200);
console.log('in the battle');
}
function battleFinished(ts: TSPlug, autoColor: string, autoButton: number[], rewardColor: string, rewardButton: number[], rewardArea: Area) {
while (true) {
rejectReward(ts, rewardColor, rewardButton, rewardArea);
const battle = isBattle(ts, autoColor, autoButton);
if (!battle) {
break;
}
msleep(200, 400);
}
msleep(100, 200);
console.log('battle finished');
}
function isBattle(ts: TSPlug, color: string, position: number[]) {
const testColor = ts.getColor(position[0], position[1]);
if (testColor === color) {
return true;
}
return false;
}
function inTheSettlement(ts: TSPlug, settlementButton: number[], battleColor: string, battleButton: number[], rewardColor: string, rewardButton: number[], rewardArea: Area) {
while (true) {
rejectReward(ts, rewardColor, rewardButton, rewardArea);
exitSettlement(ts, settlementButton);
const testColor = ts.getColor(battleButton[0], battleButton[1]);
if (testColor === battleColor) {
console.log('into ready screen');
break;
}
msleep(500, 1000);
}
console.log('new cycle!');
}
function exitSettlement(ts: TSPlug, position: number[]) {
const [x1, x2, y1, y2] = position;
randomClick(ts, {x1, x2, y1, y2});
}
function rejectReward(ts: TSPlug, rewardColor: string, rewardButton: number[], rewardArea: Area) {
const testColor = ts.getColor(rewardButton[0], rewardButton[1]);
if (testColor === rewardColor) {
randomClick(ts, rewardArea);
msleep(1000, 1232);
console.log('reject reward success');
}
msleep(43, 99);
}
function randomClick(ts: TSPlug, {x1, x2, y1, y2}: Area) {
const xr = getRandom(x1, x2);
const yr = getRandom(y1, y2);
ts.moveTo(xr, yr);
msleep(100, 300);
ts.leftClick();
msleep(200, 300);
}
function getRandomInt(min: number, max: number) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
export function getRandom(min: number, max: number) {
return Math.random() * (Math.abs(max - min)) + min;
}
function msleep(a: number, b: number) {
const n = getRandomInt(a, b);
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, n);
}
程序主入口
// app.ts 通过传递不同的参数,开启单人或组队模式,也可以创建不同的住入口文件分别启动不同模式,例如:app.single.ts / app.team.ts
import {findWindow} from './actions/find-window';
import {Mode} from './interfaces/interfaces';
import {shared} from './config/shared';
import {single} from './mode/single.mode';
import {team} from './mode/team.mode';
start(Mode.Team);
export function start(mode: Mode = Mode.Single) {
const windowHandles = findWindow(shared.original);
if (windowHandles.length === 0) return;
if (mode === Mode.Single) {
single();
} else if (mode === Mode.Team) {
// tslint:disable-next-line: no-floating-promises
team();
}
}
本程序需要使用管理员权限启动程序,为了方便写了两个批处理来快速启动程序
其他
全部代码已经发布到github,yys-robot。如果有大佬能解决worker的重复代码问题,请发布issues和我联系
版权声明:本文为huzzzz原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。