@bettergi/utils 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/game.d.ts +25 -0
- package/dist/game.js +25 -5
- package/dist/ocr.js +45 -17
- package/package.json +2 -2
package/dist/game.d.ts
CHANGED
|
@@ -53,4 +53,29 @@ export declare const setTimeTo: (hour: number, minute: number, options?: ClockOp
|
|
|
53
53
|
* @param options 时钟参数
|
|
54
54
|
*/
|
|
55
55
|
export declare const setTime: (period: "night" | "morning" | "noon" | "evening", options?: ClockOptions) => Promise<void>;
|
|
56
|
+
/** tab 翻页配置 */
|
|
57
|
+
type TabNavigationOptions = {
|
|
58
|
+
/** 标签页图标宽度(默认:96) */
|
|
59
|
+
tabIconWidth?: number;
|
|
60
|
+
/** 翻页按钮垂直偏移(默认:540) */
|
|
61
|
+
verticalOffset?: number;
|
|
62
|
+
/** 翻页按钮左侧内边距(默认:72) */
|
|
63
|
+
paddingLeft?: number;
|
|
64
|
+
/** 翻页按钮右侧内边距(默认:72) */
|
|
65
|
+
paddingRight?: number;
|
|
66
|
+
/** 是否向前翻页(默认:false) */
|
|
67
|
+
backwards?: boolean;
|
|
68
|
+
/** 最大尝试次数(默认:屏幕宽度 / 标签页图标宽度) */
|
|
69
|
+
maxAttempts?: number;
|
|
70
|
+
/** 每次尝试间隔时间(毫秒,默认:1000) */
|
|
71
|
+
retryInterval?: number;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* 导航到指定标签页
|
|
75
|
+
* @param condition 导航成功的条件
|
|
76
|
+
* @param options 翻页配置
|
|
77
|
+
* @returns - true 条件满足
|
|
78
|
+
* - false 达到最大重试次数
|
|
79
|
+
*/
|
|
80
|
+
export declare const navigateToTab: (condition: () => boolean, options?: TabNavigationOptions) => Promise<boolean>;
|
|
56
81
|
export {};
|
package/dist/game.js
CHANGED
|
@@ -10,13 +10,13 @@ import { findTextInDirection, findTextWithinBounds, findTextWithinListView } fro
|
|
|
10
10
|
* @param action 执行动作
|
|
11
11
|
*/
|
|
12
12
|
export const withGameMetrics = async (w, h, dpi, action) => {
|
|
13
|
-
const
|
|
13
|
+
const [_w, _h, _dpi] = globalThis["getGameMetrics"] ? getGameMetrics() : [];
|
|
14
14
|
try {
|
|
15
15
|
setGameMetrics(w, h, dpi);
|
|
16
16
|
return await action();
|
|
17
17
|
}
|
|
18
18
|
finally {
|
|
19
|
-
setGameMetrics(
|
|
19
|
+
globalThis["getGameMetrics"] && setGameMetrics(_w, _h, _dpi);
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
22
|
/**
|
|
@@ -39,13 +39,13 @@ export const openMenu = async (name, stepBackwards = false, options = {}) => {
|
|
|
39
39
|
await openPaimonMenu();
|
|
40
40
|
// 2.步进搜索菜单按钮
|
|
41
41
|
const { navWidth = 95, steps: step = 30 } = options;
|
|
42
|
-
const findTooltip = () => findTextWithinBounds(name, navWidth, 0, 20 + name.length * 20,
|
|
42
|
+
const findTooltip = () => findTextWithinBounds(name, navWidth, 0, 20 + name.length * 20, 1080);
|
|
43
43
|
const tooltip = await withGameMetrics(1920, 1080, 1.5, async () => {
|
|
44
44
|
let result = undefined;
|
|
45
|
-
const steps = Math.ceil(
|
|
45
|
+
const steps = Math.ceil(1080 / step);
|
|
46
46
|
for (let i = 0; i < steps; i++) {
|
|
47
47
|
const o = i * step;
|
|
48
|
-
const y = stepBackwards ?
|
|
48
|
+
const y = stepBackwards ? 1080 - o : o;
|
|
49
49
|
moveMouseTo(Math.round(navWidth / 2), y);
|
|
50
50
|
await sleep(30); // 等待提示文字出现
|
|
51
51
|
if ((result = findTooltip()) !== undefined)
|
|
@@ -130,3 +130,23 @@ export const setTime = async (period, options) => {
|
|
|
130
130
|
return setTimeTo(18, 5, options);
|
|
131
131
|
}
|
|
132
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* 导航到指定标签页
|
|
135
|
+
* @param condition 导航成功的条件
|
|
136
|
+
* @param options 翻页配置
|
|
137
|
+
* @returns - true 条件满足
|
|
138
|
+
* - false 达到最大重试次数
|
|
139
|
+
*/
|
|
140
|
+
export const navigateToTab = async (condition, options) => {
|
|
141
|
+
const { tabIconWidth = 96, verticalOffset = 540, paddingLeft = 72, paddingRight = 72, backwards = false, retryInterval = 1000 } = options || {};
|
|
142
|
+
const attempts = Math.floor(options?.maxAttempts ?? 1920 / tabIconWidth);
|
|
143
|
+
for (let i = 0; i < attempts; i++) {
|
|
144
|
+
if (i === 0 && condition())
|
|
145
|
+
return true; // fast path
|
|
146
|
+
click(backwards ? paddingLeft : 1920 - paddingRight, verticalOffset); // prev or next
|
|
147
|
+
await sleep(retryInterval);
|
|
148
|
+
if (condition())
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
};
|
package/dist/ocr.js
CHANGED
|
@@ -13,7 +13,13 @@ const directionToBounds = (direction) => {
|
|
|
13
13
|
const y = direction.includes("south") ? genshin.height / 2 : 0;
|
|
14
14
|
const w = direction === "north" || direction === "south" ? genshin.width : genshin.width / 2;
|
|
15
15
|
const h = direction === "west" || direction === "east" ? genshin.height : genshin.height / 2;
|
|
16
|
-
|
|
16
|
+
const scale = genshin.width / 1920;
|
|
17
|
+
if (scale <= 1) {
|
|
18
|
+
return { x, y, w, h };
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return { x: x / scale, y: y / scale, w: w / scale, h: h / scale };
|
|
22
|
+
}
|
|
17
23
|
};
|
|
18
24
|
/**
|
|
19
25
|
* 在整个画面内搜索图片
|
|
@@ -21,13 +27,16 @@ const directionToBounds = (direction) => {
|
|
|
21
27
|
* @returns 如果找到匹配的图片区域,则返回该区域
|
|
22
28
|
*/
|
|
23
29
|
export const findImage = (path) => {
|
|
30
|
+
const ir = captureGameRegion();
|
|
24
31
|
try {
|
|
25
|
-
const ir = captureGameRegion();
|
|
26
32
|
const ro = RecognitionObject.templateMatch(file.readImageMatSync(path));
|
|
27
33
|
return findFirst(ir, ro, region => region.isExist());
|
|
28
34
|
}
|
|
29
35
|
catch (err) {
|
|
30
|
-
|
|
36
|
+
log.warn(`${err.message || err}`);
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
ir.dispose();
|
|
31
40
|
}
|
|
32
41
|
};
|
|
33
42
|
/**
|
|
@@ -40,13 +49,16 @@ export const findImage = (path) => {
|
|
|
40
49
|
* @returns 如果找到匹配的图片区域,则返回该区域
|
|
41
50
|
*/
|
|
42
51
|
export const findImageWithinBounds = (path, x, y, w, h) => {
|
|
52
|
+
const ir = captureGameRegion();
|
|
43
53
|
try {
|
|
44
|
-
const ir = captureGameRegion();
|
|
45
54
|
const ro = RecognitionObject.templateMatch(file.readImageMatSync(path), x, y, w, h);
|
|
46
55
|
return findFirst(ir, ro, region => region.isExist());
|
|
47
56
|
}
|
|
48
57
|
catch (err) {
|
|
49
|
-
|
|
58
|
+
log.warn(`${err.message || err}`);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
ir.dispose();
|
|
50
62
|
}
|
|
51
63
|
};
|
|
52
64
|
/**
|
|
@@ -69,12 +81,20 @@ export const findText = (text, options) => {
|
|
|
69
81
|
const { ignoreCase = true, contains = false } = options || {};
|
|
70
82
|
const searchText = ignoreCase ? text.toLowerCase() : text;
|
|
71
83
|
const ir = captureGameRegion();
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
try {
|
|
85
|
+
const ro = RecognitionObject.ocrThis;
|
|
86
|
+
return findFirst(ir, ro, region => {
|
|
87
|
+
const itemText = ignoreCase ? region.text.toLowerCase() : region.text;
|
|
88
|
+
const isMatch = contains ? itemText.includes(searchText) : itemText === searchText;
|
|
89
|
+
return isMatch && region.isExist();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
log.warn(`${err.message || err}`);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
ir.dispose();
|
|
97
|
+
}
|
|
78
98
|
};
|
|
79
99
|
/**
|
|
80
100
|
* 在指定区域内搜索文本
|
|
@@ -90,12 +110,20 @@ export const findTextWithinBounds = (text, x, y, w, h, options) => {
|
|
|
90
110
|
const { ignoreCase = true, contains = false } = options || {};
|
|
91
111
|
const searchText = ignoreCase ? text.toLowerCase() : text;
|
|
92
112
|
const ir = captureGameRegion();
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
113
|
+
try {
|
|
114
|
+
const ro = RecognitionObject.ocr(x, y, w, h);
|
|
115
|
+
return findFirst(ir, ro, region => {
|
|
116
|
+
const itemText = ignoreCase ? region.text.toLowerCase() : region.text;
|
|
117
|
+
const isMatch = contains ? itemText.includes(searchText) : itemText === searchText;
|
|
118
|
+
return isMatch && region.isExist();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
log.warn(`${err.message || err}`);
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
ir.dispose();
|
|
126
|
+
}
|
|
99
127
|
};
|
|
100
128
|
/**
|
|
101
129
|
* 在指定方向上搜索文本
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bettergi/utils",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "开发 BetterGI 脚本常用工具集",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Bread Grocery<https://github.com/breadgrocery>",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"build": "tsc"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@bettergi/types": "^0.1.
|
|
36
|
+
"@bettergi/types": "^0.1.3",
|
|
37
37
|
"typescript": "^5.9.3"
|
|
38
38
|
}
|
|
39
39
|
}
|