@bettergi/utils 0.0.5 → 0.0.6

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/README.md CHANGED
@@ -8,6 +8,24 @@ npm install @bettergi/utils
8
8
 
9
9
  ## 函数清单
10
10
 
11
+ ### 游戏内操作
12
+
13
+ ```ts
14
+ import { openMenu, openMenuPage, openPaimonMenu, setTime } from "@bettergi/utils";
15
+
16
+ // 打开派蒙菜单
17
+ await openPaimonMenu();
18
+
19
+ // 打开菜单
20
+ await openMenu("邮件");
21
+
22
+ // 打开菜单页面
23
+ await openMenuPage("问卷");
24
+
25
+ // 调整游戏时间
26
+ await setTime("evening");
27
+ ```
28
+
11
29
  ### 图文识别
12
30
 
13
31
  > 对 RecognitionObject 代码的封装,对于简单的找图、找字操作,不再需要编写复杂的代码。
package/dist/flow.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 等待直到条件满足或超时
3
+ * @param condition 等待的条件判断函数,返回 true 表示条件满足
4
+ * @param timeout 超时时间(毫秒),默认 3000 毫秒
5
+ * @param interval 等待间隔(毫秒),默认 300 毫秒
6
+ * @param action 每次等待循环中执行的操作(可选)
7
+ * @returns - true 在超时前条件已满足
8
+ * - false 在超时后条件仍未满足
9
+ */
10
+ export declare const waitUntil: (condition: (context: Record<string, any>) => boolean, timeout?: number, interval?: number, action?: (context: Record<string, any>) => void) => Promise<boolean>;
package/dist/flow.js ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 等待直到条件满足或超时
3
+ * @param condition 等待的条件判断函数,返回 true 表示条件满足
4
+ * @param timeout 超时时间(毫秒),默认 3000 毫秒
5
+ * @param interval 等待间隔(毫秒),默认 300 毫秒
6
+ * @param action 每次等待循环中执行的操作(可选)
7
+ * @returns - true 在超时前条件已满足
8
+ * - false 在超时后条件仍未满足
9
+ */
10
+ export const waitUntil = async (condition, timeout, interval, action) => {
11
+ const context = {};
12
+ const deadline = Date.now() + (timeout ?? 3 * 1000);
13
+ while (Date.now() < deadline) {
14
+ if (condition(context))
15
+ return true;
16
+ action?.(context);
17
+ await sleep(interval ?? 300);
18
+ }
19
+ return false;
20
+ };
package/dist/game.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * 打开派蒙菜单
3
+ */
4
+ export declare const openPaimonMenu: () => Promise<void>;
5
+ /**
6
+ * 打开游戏菜单(左侧按钮)
7
+ * @param name 菜单名称
8
+ * @param reverse 是否反向搜索(可选)
9
+ * @param config 搜索参数(可选)
10
+ */
11
+ export declare const openMenu: (name: "\u62CD\u7167" | "\u516C\u544A" | "\u90AE\u4EF6" | "\u65F6\u95F4" | "\u8BBE\u7F6E" | (string & {}), reverse?: boolean, config?: {
12
+ x?: number;
13
+ step?: number;
14
+ timeout?: number;
15
+ }) => Promise<void>;
16
+ /**
17
+ * 打开游戏菜单页面(菜单按钮列表)
18
+ * @param name 菜单页面名称
19
+ * @param config 菜单页面视图参数
20
+ */
21
+ export declare const openMenuPage: (name: string, config?: {
22
+ x?: number;
23
+ y?: number;
24
+ w?: number;
25
+ h?: number;
26
+ lineHeight?: number;
27
+ }) => Promise<void>;
28
+ /**
29
+ * 调整游戏时间
30
+ * @param period 时间段
31
+ * @param config 时钟参数
32
+ */
33
+ export declare const setTime: (period: "night" | "morning" | "noon" | "evening", config?: {
34
+ centerX?: number;
35
+ centerY?: number;
36
+ radius?: number;
37
+ offset?: number;
38
+ }) => Promise<void>;
package/dist/game.js ADDED
@@ -0,0 +1,103 @@
1
+ import { waitUntil } from "./flow";
2
+ import { mouseSlide } from "./mouse";
3
+ import { findTextInDirection, findTextWithinBounds, findTextWithinListView } from "./ocr";
4
+ /**
5
+ * 打开派蒙菜单
6
+ */
7
+ export const openPaimonMenu = async () => {
8
+ // 1.返回主界面
9
+ await genshin.returnMainUi();
10
+ // 2.打开派蒙菜单
11
+ const findWorldLevel = () => findTextInDirection("世界等级", false, true, "north-west");
12
+ const ok = await waitUntil(() => findWorldLevel() !== undefined, 5000, 1000, () => keyPress("ESCAPE"));
13
+ if (!ok)
14
+ throw new Error("打开派蒙菜单超时");
15
+ };
16
+ /**
17
+ * 打开游戏菜单(左侧按钮)
18
+ * @param name 菜单名称
19
+ * @param reverse 是否反向搜索(可选)
20
+ * @param config 搜索参数(可选)
21
+ */
22
+ export const openMenu = async (name, reverse, config) => {
23
+ // 1.打开派蒙菜单
24
+ await openPaimonMenu();
25
+ // 2.搜索菜单按钮
26
+ const { x = 50, step = 30, timeout = 3000 } = config || {};
27
+ const findTooltip = () => findTextWithinBounds(name, false, true, 0, 0, x + 150, genshin.height);
28
+ let result = undefined;
29
+ const steps = Math.ceil(genshin.height / step);
30
+ for (let i = 0; i < steps; i++) {
31
+ const o = i * step;
32
+ const y = reverse ? genshin.height - o : o;
33
+ moveMouseTo(x, y);
34
+ await sleep(30);
35
+ if ((result = findTooltip()) !== undefined)
36
+ break;
37
+ }
38
+ // 3.点击菜单按钮
39
+ if (result != undefined) {
40
+ const ok = await waitUntil(() => findTooltip() === undefined, timeout, 1000, () => {
41
+ click(x, result.y);
42
+ });
43
+ if (!ok)
44
+ throw new Error(`打开菜单 ${name} 超时`);
45
+ }
46
+ else {
47
+ throw new Error(`打开菜单 ${name} 失败`);
48
+ }
49
+ };
50
+ /**
51
+ * 打开游戏菜单页面(菜单按钮列表)
52
+ * @param name 菜单页面名称
53
+ * @param config 菜单页面视图参数
54
+ */
55
+ export const openMenuPage = async (name, config) => {
56
+ // 1.打开派蒙菜单
57
+ await openPaimonMenu();
58
+ // 2.搜索菜单页面
59
+ const { x = 100, y = 330, w = 670, h = 730, lineHeight = 142 } = config || {};
60
+ const pageButton = await findTextWithinListView(name, false, true, {
61
+ x,
62
+ y,
63
+ w,
64
+ h,
65
+ maxListItems: 5,
66
+ lineHeight
67
+ });
68
+ if (!pageButton)
69
+ throw new Error(`搜索菜单页面 ${name} 失败`);
70
+ // 3.点击菜单页面
71
+ pageButton.click();
72
+ };
73
+ /**
74
+ * 调整游戏时间
75
+ * @param period 时间段
76
+ * @param config 时钟参数
77
+ */
78
+ export const setTime = async (period, config) => {
79
+ // 1.打开时间页面
80
+ await openMenu("时间", true);
81
+ // 2.拨动指针
82
+ const { centerX = 1440, centerY = 502, radius = 400, offset = 5 } = config || {};
83
+ const index = ["night", "morning", "noon", "evening"].indexOf(period);
84
+ const periodsDirections = [
85
+ () => mouseSlide(centerX, centerY, centerX - offset, centerY + radius),
86
+ () => mouseSlide(centerX, centerY, centerX - radius, centerY - offset),
87
+ () => mouseSlide(centerX, centerY, centerX + offset, centerY - radius),
88
+ () => mouseSlide(centerX, centerY, centerX + radius, centerY + offset)
89
+ ];
90
+ const jobs = Array.from({ length: 4 }, (_, i) => periodsDirections[(index + i + 1) % periodsDirections.length]);
91
+ for (const job of jobs)
92
+ await job();
93
+ // 3.点击确认按钮,等待调整结束
94
+ const findTooShort = () => findTextInDirection("时间少于", true, true, "south-east");
95
+ const findConfirmButton = () => findTextInDirection("确认", false, true, "south-east");
96
+ const ok = await waitUntil(() => findTooShort() !== undefined, 20 * 1000, 1000, () => {
97
+ findConfirmButton()?.click();
98
+ });
99
+ if (!ok)
100
+ throw new Error("调整时间超时");
101
+ // 4.返回主界面
102
+ await genshin.returnMainUi();
103
+ };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- export * from "./action";
1
+ export * from "./flow";
2
+ export * from "./game";
2
3
  export * from "./mouse";
3
4
  export * from "./ocr";
4
5
  export * from "./store";
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- export * from "./action";
1
+ export * from "./flow";
2
+ export * from "./game";
2
3
  export * from "./mouse";
3
4
  export * from "./ocr";
4
5
  export * from "./store";
package/dist/ocr.d.ts CHANGED
@@ -53,4 +53,22 @@ export declare const findTextWithinBounds: (text: string, contains: boolean, ign
53
53
  * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
54
54
  */
55
55
  export declare const findTextInDirection: (text: string, contains: boolean, ignoreCase: boolean, direction: Direction) => Region | undefined;
56
+ /**
57
+ * 在列表视图中滚动搜索文本
58
+ * @param text 待搜索文本
59
+ * @param contains 是否包含
60
+ * @param ignoreCase 是否忽略大小写
61
+ * @param listView 列表视图参数
62
+ * @param timeout 搜索超时
63
+ * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
64
+ */
65
+ export declare const findTextWithinListView: (text: string, contains: boolean, ignoreCase: boolean, listView: {
66
+ x: number;
67
+ y: number;
68
+ w: number;
69
+ h: number;
70
+ maxListItems: number;
71
+ lineHeight: number;
72
+ padding?: number;
73
+ }, timeout?: number) => Promise<Region | undefined>;
56
74
  export {};
package/dist/ocr.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { waitUntil } from "./flow";
2
+ import { mouseScrollDownLines } from "./mouse";
1
3
  const findFirst = (ir, ro, predicate) => {
2
4
  const candidates = ir.findMulti(ro);
3
5
  for (let i = 0; i < candidates.count; i++) {
@@ -108,3 +110,40 @@ export const findTextInDirection = (text, contains, ignoreCase, direction) => {
108
110
  const { x, y, w, h } = directionToBounds(direction);
109
111
  return findTextWithinBounds(text, contains, ignoreCase, x, y, w, h);
110
112
  };
113
+ /**
114
+ * 在列表视图中滚动搜索文本
115
+ * @param text 待搜索文本
116
+ * @param contains 是否包含
117
+ * @param ignoreCase 是否忽略大小写
118
+ * @param listView 列表视图参数
119
+ * @param timeout 搜索超时
120
+ * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
121
+ */
122
+ export const findTextWithinListView = async (text, contains, ignoreCase, listView, timeout) => {
123
+ const { x, y, w, h, maxListItems, lineHeight, padding = 10 } = listView;
124
+ const find = () => {
125
+ return findTextWithinBounds(text, contains, ignoreCase, x, y, w, h);
126
+ };
127
+ let firstRegion;
128
+ const isBottomTouched = () => {
129
+ const ro = RecognitionObject.ocr(x, y, w, h);
130
+ const list = captureGameRegion().findMulti(ro);
131
+ if (list.count > 0) {
132
+ if (firstRegion?.text === list[0].text && Math.abs(list[0].y - firstRegion.y) < lineHeight) {
133
+ return true;
134
+ }
135
+ else {
136
+ firstRegion = list[0];
137
+ return false;
138
+ }
139
+ }
140
+ else {
141
+ return true;
142
+ }
143
+ };
144
+ const ok = await waitUntil(() => find() != undefined || isBottomTouched(), timeout ?? 30 * 1000, 1000, async () => {
145
+ moveMouseTo(x + w - padding, y + padding);
146
+ await mouseScrollDownLines(maxListItems, lineHeight);
147
+ });
148
+ return ok ? find() : undefined;
149
+ };
package/dist/time.d.ts ADDED
File without changes
package/dist/time.js ADDED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bettergi/utils",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Utils for BetterGI JavaScript Development",
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.0.5",
37
- "typescript": "^5.6.3"
36
+ "@bettergi/types": "^0.0.10",
37
+ "typescript": "5.6.3"
38
38
  }
39
39
  }