Compare commits
16 Commits
6cbc4f0788
...
84f78230d2
| Author | SHA1 | Date | |
|---|---|---|---|
| 84f78230d2 | |||
| f506616ba9 | |||
| c1d9ac4f64 | |||
| fb28fe297a | |||
| 9b17f1948a | |||
| e82c4f875c | |||
| 51ecdf4908 | |||
| 8d2882e752 | |||
| 1a66e767f3 | |||
| 04fab819b4 | |||
| 023972682d | |||
| 2cefb26759 | |||
| 7ed7cabbaa | |||
| b62e301c55 | |||
| 93e8fd1f0c | |||
| b9e0cc3026 |
11
README.en.md
@ -12,12 +12,15 @@
|
|||||||
|
|
||||||
The script is still under active development. The following features have been implemented:
|
The script is still under active development. The following features have been implemented:
|
||||||
|
|
||||||
- [x] **Cafe** Claim rewards / Interact / Second cafe
|
- [x] **Cafe** Claim rewards / Interact / Second floor
|
||||||
- [x] **Circle** Claim AP
|
- [x] **Club** Claim AP
|
||||||
- [x] **Mailbox** Claim rewards
|
- [x] **Mailbox** Claim rewards
|
||||||
- [x] **Tactical Challenge** Claim rewards / Auto battle
|
- [x] **Tactical Challenge** Claim rewards / Auto battle
|
||||||
|
|
||||||
_Currently only supports JP server._
|
Supported servers:
|
||||||
|
|
||||||
|
- [x] JP
|
||||||
|
- [x] OVERSEA - Global
|
||||||
|
|
||||||
## Relative projects
|
## Relative projects
|
||||||
|
|
||||||
@ -38,3 +41,5 @@ Thanks to [6bir](https://github.com/6bir) for the icon design.
|
|||||||
|
|
||||||
Thanks to [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) and [SRC](https://github.com/LmeSzinc/StarRailCopilot)
|
Thanks to [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) and [SRC](https://github.com/LmeSzinc/StarRailCopilot)
|
||||||
for the development framework.
|
for the development framework.
|
||||||
|
|
||||||
|
Thanks to [RedDeadDepresso](https://github.com/RedDeadDepresso) for EN support.
|
||||||
|
|||||||
@ -17,7 +17,10 @@
|
|||||||
- [x] **邮箱** 领取奖励
|
- [x] **邮箱** 领取奖励
|
||||||
- [x] **战术对抗赛** 领取奖励 / 自动战斗
|
- [x] **战术对抗赛** 领取奖励 / 自动战斗
|
||||||
|
|
||||||
_目前仅支持日服。_
|
目前支持的服务器:
|
||||||
|
|
||||||
|
- [x] 日服
|
||||||
|
- [x] 国际服 - 全球
|
||||||
|
|
||||||
## 相关项目
|
## 相关项目
|
||||||
|
|
||||||
@ -36,3 +39,5 @@ _目前仅支持日服。_
|
|||||||
|
|
||||||
感谢 [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) 以及 [SRC](https://github.com/LmeSzinc/StarRailCopilot)
|
感谢 [Alas](https://github.com/LmeSzinc/AzurLaneAutoScript) 以及 [SRC](https://github.com/LmeSzinc/StarRailCopilot)
|
||||||
提供的开发框架。
|
提供的开发框架。
|
||||||
|
|
||||||
|
感谢 [RedDeadDepresso](https://github.com/RedDeadDepresso) 提供英语支持。
|
||||||
|
|||||||
BIN
assets/jp/bounty/CHECK_DESERT_RAILROAD.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
assets/jp/bounty/CHECK_HIGHWAY.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/jp/bounty/CHECK_SCHOOLHOUSE.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/jp/bounty/SELECT_DESERT_RAILROAD.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
assets/jp/bounty/SELECT_HIGHWAY.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
assets/jp/bounty/SELECT_SCHOOLHOUSE.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
assets/jp/school_exchange/CHECK_GEHENNA.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
assets/jp/school_exchange/CHECK_MILLENNIUM.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
assets/jp/school_exchange/CHECK_TRINITY.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
assets/jp/school_exchange/SELECT_GEHENNA.png
Normal file
|
After Width: | Height: | Size: 7.8 KiB |
BIN
assets/jp/school_exchange/SELECT_MILLENNIUM.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
assets/jp/school_exchange/SELECT_TRINITY.png
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
assets/jp/stage/list/OCR_INDEX.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
assets/jp/stage/list/STAGE_ENTER.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/jp/stage/list/STAGE_ITEM.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
assets/jp/stage/list/STAGE_LIST.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
assets/jp/stage/list/STAGE_STARS.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
BIN
assets/jp/stage/sweep/CHECK_SWEEP.png
Normal file
|
After Width: | Height: | Size: 8.0 KiB |
BIN
assets/jp/stage/sweep/ENTER.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/jp/stage/sweep/EXIT.png
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
assets/jp/stage/sweep/MAX.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
assets/jp/stage/sweep/MIN.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
assets/jp/stage/sweep/MINUS.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/jp/stage/sweep/OCR_NUM.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/jp/stage/sweep/PLUS.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
assets/jp/stage/sweep/SKIP_OK_LOWER.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/jp/stage/sweep/SKIP_OK_UPPER.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/jp/stage/sweep/SKIP_SKIP.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/jp/stage/sweep/SWEEP.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/jp/stage/sweep/SWEEP_CONFIRM.BUTTON.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
assets/jp/stage/sweep/SWEEP_CONFIRM.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
@ -205,8 +205,8 @@
|
|||||||
"help": "Auto adjust cafe interface for better student interaction"
|
"help": "Auto adjust cafe interface for better student interaction"
|
||||||
},
|
},
|
||||||
"SecondCafe": {
|
"SecondCafe": {
|
||||||
"name": "Second Cafe",
|
"name": "Second Floor",
|
||||||
"help": "Enable auto switch to second cafe and perform interaction"
|
"help": "JP server only\nEnable auto switch to second floor and perform interaction"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TacticalChallenge": {
|
"TacticalChallenge": {
|
||||||
|
|||||||
@ -206,7 +206,7 @@
|
|||||||
},
|
},
|
||||||
"SecondCafe": {
|
"SecondCafe": {
|
||||||
"name": "第二咖啡厅",
|
"name": "第二咖啡厅",
|
||||||
"help": "自动切换第二咖啡厅进行互动点击"
|
"help": "仅支持日服\n自动切换第二咖啡厅进行互动点击"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TacticalChallenge": {
|
"TacticalChallenge": {
|
||||||
|
|||||||
@ -863,8 +863,8 @@ class Connection(ConnectionAttr):
|
|||||||
|
|
||||||
# Auto package detection
|
# Auto package detection
|
||||||
if len(packages) == 0:
|
if len(packages) == 0:
|
||||||
logger.critical(f'No Star Rail package found, '
|
logger.critical(f'No Blue Archive package found, '
|
||||||
f'please confirm Star Rail has been installed on device "{self.serial}"')
|
f'please confirm Blue Archive has been installed on device "{self.serial}"')
|
||||||
raise RequestHumanTakeover
|
raise RequestHumanTakeover
|
||||||
if len(packages) == 1:
|
if len(packages) == 1:
|
||||||
logger.info('Auto package detection found only one package, using it')
|
logger.info('Auto package detection found only one package, using it')
|
||||||
@ -877,6 +877,6 @@ class Connection(ConnectionAttr):
|
|||||||
# set_server(self.package)
|
# set_server(self.package)
|
||||||
else:
|
else:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
f'Multiple Star Rail packages found, auto package detection cannot decide which to choose, '
|
f'Multiple Blue Archive packages found, auto package detection cannot decide which to choose, '
|
||||||
'please copy one of the available devices listed above to Alas.Emulator.PackageName')
|
'please copy one of the available devices listed above to Alas.Emulator.PackageName')
|
||||||
raise RequestHumanTakeover
|
raise RequestHumanTakeover
|
||||||
|
|||||||
71
tasks/bounty/assets/assets_bounty.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
from module.base.button import Button, ButtonWrapper
|
||||||
|
|
||||||
|
# This file was auto-generated, do not modify it manually. To generate:
|
||||||
|
# ``` python -m dev_tools.button_extract ```
|
||||||
|
|
||||||
|
CHECK_DESERT_RAILROAD = ButtonWrapper(
|
||||||
|
name='CHECK_DESERT_RAILROAD',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/bounty/CHECK_DESERT_RAILROAD.png',
|
||||||
|
area=(106, 147, 401, 179),
|
||||||
|
search=(86, 127, 421, 199),
|
||||||
|
color=(172, 179, 183),
|
||||||
|
button=(106, 147, 401, 179),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
CHECK_HIGHWAY = ButtonWrapper(
|
||||||
|
name='CHECK_HIGHWAY',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/bounty/CHECK_HIGHWAY.png',
|
||||||
|
area=(107, 147, 400, 179),
|
||||||
|
search=(87, 127, 420, 199),
|
||||||
|
color=(191, 199, 203),
|
||||||
|
button=(107, 147, 400, 179),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
CHECK_SCHOOLHOUSE = ButtonWrapper(
|
||||||
|
name='CHECK_SCHOOLHOUSE',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/bounty/CHECK_SCHOOLHOUSE.png',
|
||||||
|
area=(106, 147, 314, 179),
|
||||||
|
search=(86, 127, 334, 199),
|
||||||
|
color=(176, 183, 187),
|
||||||
|
button=(106, 147, 314, 179),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SELECT_DESERT_RAILROAD = ButtonWrapper(
|
||||||
|
name='SELECT_DESERT_RAILROAD',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/bounty/SELECT_DESERT_RAILROAD.png',
|
||||||
|
area=(1066, 271, 1224, 311),
|
||||||
|
search=(1046, 251, 1244, 331),
|
||||||
|
color=(178, 188, 199),
|
||||||
|
button=(1066, 271, 1224, 311),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SELECT_HIGHWAY = ButtonWrapper(
|
||||||
|
name='SELECT_HIGHWAY',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/bounty/SELECT_HIGHWAY.png',
|
||||||
|
area=(1065, 165, 1223, 203),
|
||||||
|
search=(1045, 145, 1243, 223),
|
||||||
|
color=(214, 221, 228),
|
||||||
|
button=(1065, 165, 1223, 203),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SELECT_SCHOOLHOUSE = ButtonWrapper(
|
||||||
|
name='SELECT_SCHOOLHOUSE',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/bounty/SELECT_SCHOOLHOUSE.png',
|
||||||
|
area=(1154, 381, 1223, 417),
|
||||||
|
search=(1134, 361, 1243, 437),
|
||||||
|
color=(173, 185, 198),
|
||||||
|
button=(1154, 381, 1223, 417),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
@ -5,6 +5,7 @@ from enum import Enum
|
|||||||
from module.logger import logger
|
from module.logger import logger
|
||||||
from module.base.timer import Timer
|
from module.base.timer import Timer
|
||||||
from module.base.button import ClickButton
|
from module.base.button import ClickButton
|
||||||
|
from module.base.decorator import Config
|
||||||
from module.base.utils.utils import area_offset
|
from module.base.utils.utils import area_offset
|
||||||
from module.ocr.ocr import Digit
|
from module.ocr.ocr import Digit
|
||||||
from module.ui.switch import Switch
|
from module.ui.switch import Switch
|
||||||
@ -33,6 +34,8 @@ class CafeStatus(Enum):
|
|||||||
|
|
||||||
|
|
||||||
class Cafe(UI):
|
class Cafe(UI):
|
||||||
|
template = CLICKABLE_TEMPLATE
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def merge_points(points, threshold=3):
|
def merge_points(points, threshold=3):
|
||||||
if len(points) <= 1:
|
if len(points) <= 1:
|
||||||
@ -51,8 +54,7 @@ class Cafe(UI):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _extract_clickable_from_image(image):
|
def _extract_clickable_from_image(image):
|
||||||
# convert to hsv for better color matching
|
# convert to hsv for better color matching
|
||||||
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
|
hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV)
|
||||||
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
|
|
||||||
# set color range
|
# set color range
|
||||||
lower_hsv = np.array([18, 200, 220])
|
lower_hsv = np.array([18, 200, 220])
|
||||||
upper_hsv = np.array([30, 255, 255])
|
upper_hsv = np.array([30, 255, 255])
|
||||||
@ -62,8 +64,8 @@ class Cafe(UI):
|
|||||||
return cv2.bitwise_and(image, image, mask=mask)
|
return cv2.bitwise_and(image, image, mask=mask)
|
||||||
|
|
||||||
def _match_clickable_points(self, image, threshold=0.8):
|
def _match_clickable_points(self, image, threshold=0.8):
|
||||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
||||||
template = cv2.cvtColor(self.btn.matched_button.image, cv2.COLOR_BGR2GRAY)
|
template = cv2.cvtColor(self.template.matched_button.image, cv2.COLOR_RGB2GRAY)
|
||||||
|
|
||||||
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
|
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
|
||||||
loc = np.where(res >= threshold)
|
loc = np.where(res >= threshold)
|
||||||
@ -78,11 +80,11 @@ class Cafe(UI):
|
|||||||
points = self.merge_points(points)
|
points = self.merge_points(points)
|
||||||
if not points:
|
if not points:
|
||||||
return []
|
return []
|
||||||
area = area_offset((0, 0, self.btn.width, self.btn.height), offset)
|
area = area_offset((0, 0, self.template.width, self.template.height), offset)
|
||||||
return [
|
return [
|
||||||
ClickButton(
|
ClickButton(
|
||||||
button=area_offset(area, offset=point),
|
button=area_offset(area, offset=point),
|
||||||
name=self.btn.name
|
name=self.template.name
|
||||||
)
|
)
|
||||||
for point in points
|
for point in points
|
||||||
]
|
]
|
||||||
@ -188,15 +190,23 @@ class Cafe(UI):
|
|||||||
logger.warning(f'Invalid status: {status}')
|
logger.warning(f'Invalid status: {status}')
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
@Config.when(Emulator_GameLanguage='jp')
|
||||||
|
def is_second_cafe_on(self):
|
||||||
|
return self.config.Cafe_SecondCafe
|
||||||
|
|
||||||
|
@Config.when(Emulator_GameLanguage=None)
|
||||||
|
def is_second_cafe_on(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
is_second_cafe_on = property(is_second_cafe_on)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.btn = CLICKABLE_TEMPLATE
|
|
||||||
self.click = 0
|
self.click = 0
|
||||||
self.check = 0
|
self.check = 0
|
||||||
|
|
||||||
is_reward_on = self.config.Cafe_Reward
|
is_reward_on = self.config.Cafe_Reward
|
||||||
is_touch_on = self.config.Cafe_Touch
|
is_touch_on = self.config.Cafe_Touch
|
||||||
self.is_adjust_on = self.config.Cafe_AutoAdjust
|
self.is_adjust_on = self.config.Cafe_AutoAdjust
|
||||||
is_second_cafe_on = self.config.Cafe_SecondCafe
|
|
||||||
|
|
||||||
self.ui_ensure(page_cafe)
|
self.ui_ensure(page_cafe)
|
||||||
|
|
||||||
@ -240,7 +250,7 @@ class Cafe(UI):
|
|||||||
is_reset = True
|
is_reset = True
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if is_second_cafe_on and not is_second and status == CafeStatus.FINISHED:
|
if self.is_second_cafe_on and not is_second and status == CafeStatus.FINISHED:
|
||||||
if not SWITCH_CAFE.appear(main=self):
|
if not SWITCH_CAFE.appear(main=self):
|
||||||
logger.warning('Cafe switch not found')
|
logger.warning('Cafe switch not found')
|
||||||
continue
|
continue
|
||||||
@ -266,11 +276,14 @@ class Cafe(UI):
|
|||||||
logger.attr('Status', status)
|
logger.attr('Status', status)
|
||||||
status = self._handle_cafe(status)
|
status = self._handle_cafe(status)
|
||||||
|
|
||||||
if not is_second_cafe_on:
|
if not self.is_second_cafe_on:
|
||||||
if status is CafeStatus.FINISHED:
|
if status is CafeStatus.FINISHED:
|
||||||
|
logger.info('Second cafe is not supported or disabled')
|
||||||
|
logger.info('Cafe finished')
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
if is_second and status is CafeStatus.FINISHED:
|
if is_second and status is CafeStatus.FINISHED:
|
||||||
|
logger.info('Cafe finished')
|
||||||
break
|
break
|
||||||
|
|
||||||
self.config.task_delay(server_update=True, minute=180)
|
self.config.task_delay(server_update=True, minute=180)
|
||||||
|
|||||||
71
tasks/school_exchange/assets/assets_school_exchange.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
from module.base.button import Button, ButtonWrapper
|
||||||
|
|
||||||
|
# This file was auto-generated, do not modify it manually. To generate:
|
||||||
|
# ``` python -m dev_tools.button_extract ```
|
||||||
|
|
||||||
|
CHECK_GEHENNA = ButtonWrapper(
|
||||||
|
name='CHECK_GEHENNA',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/school_exchange/CHECK_GEHENNA.png',
|
||||||
|
area=(109, 149, 195, 177),
|
||||||
|
search=(89, 129, 215, 197),
|
||||||
|
color=(201, 205, 206),
|
||||||
|
button=(109, 149, 195, 177),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
CHECK_MILLENNIUM = ButtonWrapper(
|
||||||
|
name='CHECK_MILLENNIUM',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/school_exchange/CHECK_MILLENNIUM.png',
|
||||||
|
area=(108, 148, 254, 178),
|
||||||
|
search=(88, 128, 274, 198),
|
||||||
|
color=(202, 206, 208),
|
||||||
|
button=(108, 148, 254, 178),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
CHECK_TRINITY = ButtonWrapper(
|
||||||
|
name='CHECK_TRINITY',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/school_exchange/CHECK_TRINITY.png',
|
||||||
|
area=(115, 148, 250, 178),
|
||||||
|
search=(95, 128, 270, 198),
|
||||||
|
color=(204, 208, 210),
|
||||||
|
button=(115, 148, 250, 178),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SELECT_GEHENNA = ButtonWrapper(
|
||||||
|
name='SELECT_GEHENNA',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/school_exchange/SELECT_GEHENNA.png',
|
||||||
|
area=(1125, 275, 1224, 310),
|
||||||
|
search=(1105, 255, 1244, 330),
|
||||||
|
color=(207, 217, 225),
|
||||||
|
button=(1125, 275, 1224, 310),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SELECT_MILLENNIUM = ButtonWrapper(
|
||||||
|
name='SELECT_MILLENNIUM',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/school_exchange/SELECT_MILLENNIUM.png',
|
||||||
|
area=(1069, 381, 1217, 417),
|
||||||
|
search=(1049, 361, 1237, 437),
|
||||||
|
color=(206, 214, 222),
|
||||||
|
button=(1069, 381, 1217, 417),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SELECT_TRINITY = ButtonWrapper(
|
||||||
|
name='SELECT_TRINITY',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/school_exchange/SELECT_TRINITY.png',
|
||||||
|
area=(1074, 165, 1221, 204),
|
||||||
|
search=(1054, 145, 1241, 224),
|
||||||
|
color=(213, 221, 228),
|
||||||
|
button=(1074, 165, 1221, 204),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
60
tasks/stage/assets/assets_stage_list.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
from module.base.button import Button, ButtonWrapper
|
||||||
|
|
||||||
|
# This file was auto-generated, do not modify it manually. To generate:
|
||||||
|
# ``` python -m dev_tools.button_extract ```
|
||||||
|
|
||||||
|
OCR_INDEX = ButtonWrapper(
|
||||||
|
name='OCR_INDEX',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/list/OCR_INDEX.png',
|
||||||
|
area=(701, 149, 740, 656),
|
||||||
|
search=(681, 129, 760, 676),
|
||||||
|
color=(195, 196, 193),
|
||||||
|
button=(701, 149, 740, 656),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
STAGE_ENTER = ButtonWrapper(
|
||||||
|
name='STAGE_ENTER',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/list/STAGE_ENTER.png',
|
||||||
|
area=(1093, 173, 1142, 199),
|
||||||
|
search=(1073, 153, 1162, 219),
|
||||||
|
color=(106, 171, 200),
|
||||||
|
button=(1093, 173, 1142, 199),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
STAGE_ITEM = ButtonWrapper(
|
||||||
|
name='STAGE_ITEM',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/list/STAGE_ITEM.png',
|
||||||
|
area=(687, 148, 1181, 227),
|
||||||
|
search=(667, 128, 1201, 247),
|
||||||
|
color=(212, 228, 233),
|
||||||
|
button=(687, 148, 1181, 227),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
STAGE_LIST = ButtonWrapper(
|
||||||
|
name='STAGE_LIST',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/list/STAGE_LIST.png',
|
||||||
|
area=(675, 136, 1190, 676),
|
||||||
|
search=(655, 116, 1210, 696),
|
||||||
|
color=(194, 204, 209),
|
||||||
|
button=(675, 136, 1190, 676),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
STAGE_STARS = ButtonWrapper(
|
||||||
|
name='STAGE_STARS',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/list/STAGE_STARS.png',
|
||||||
|
area=(693, 192, 746, 212),
|
||||||
|
search=(673, 172, 766, 232),
|
||||||
|
color=(225, 214, 166),
|
||||||
|
button=(693, 192, 746, 212),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
148
tasks/stage/assets/assets_stage_sweep.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
from module.base.button import Button, ButtonWrapper
|
||||||
|
|
||||||
|
# This file was auto-generated, do not modify it manually. To generate:
|
||||||
|
# ``` python -m dev_tools.button_extract ```
|
||||||
|
|
||||||
|
CHECK_SWEEP = ButtonWrapper(
|
||||||
|
name='CHECK_SWEEP',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/CHECK_SWEEP.png',
|
||||||
|
area=(638, 188, 722, 212),
|
||||||
|
search=(618, 168, 742, 232),
|
||||||
|
color=(174, 184, 197),
|
||||||
|
button=(638, 188, 722, 212),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
ENTER = ButtonWrapper(
|
||||||
|
name='ENTER',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/ENTER.png',
|
||||||
|
area=(791, 514, 1080, 568),
|
||||||
|
search=(771, 494, 1100, 588),
|
||||||
|
color=(223, 207, 68),
|
||||||
|
button=(791, 514, 1080, 568),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
EXIT = ButtonWrapper(
|
||||||
|
name='EXIT',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/EXIT.png',
|
||||||
|
area=(1114, 127, 1141, 154),
|
||||||
|
search=(1094, 107, 1161, 174),
|
||||||
|
color=(185, 193, 203),
|
||||||
|
button=(1114, 127, 1141, 154),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
MAX = ButtonWrapper(
|
||||||
|
name='MAX',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/MAX.png',
|
||||||
|
area=(1054, 279, 1111, 321),
|
||||||
|
search=(1034, 259, 1131, 341),
|
||||||
|
color=(216, 222, 228),
|
||||||
|
button=(1054, 279, 1111, 321),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
MIN = ButtonWrapper(
|
||||||
|
name='MIN',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/MIN.png',
|
||||||
|
area=(760, 278, 816, 322),
|
||||||
|
search=(740, 258, 836, 342),
|
||||||
|
color=(194, 194, 194),
|
||||||
|
button=(760, 278, 816, 322),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
MINUS = ButtonWrapper(
|
||||||
|
name='MINUS',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/MINUS.png',
|
||||||
|
area=(838, 279, 876, 320),
|
||||||
|
search=(818, 259, 896, 340),
|
||||||
|
color=(221, 222, 222),
|
||||||
|
button=(838, 279, 876, 320),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
OCR_NUM = ButtonWrapper(
|
||||||
|
name='OCR_NUM',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/OCR_NUM.png',
|
||||||
|
area=(896, 281, 975, 323),
|
||||||
|
search=(876, 261, 995, 343),
|
||||||
|
color=(81, 94, 113),
|
||||||
|
button=(896, 281, 975, 323),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
PLUS = ButtonWrapper(
|
||||||
|
name='PLUS',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/PLUS.png',
|
||||||
|
area=(995, 278, 1034, 322),
|
||||||
|
search=(975, 258, 1054, 342),
|
||||||
|
color=(233, 243, 246),
|
||||||
|
button=(995, 278, 1034, 322),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SKIP_OK_LOWER = ButtonWrapper(
|
||||||
|
name='SKIP_OK_LOWER',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/SKIP_OK_LOWER.png',
|
||||||
|
area=(541, 551, 740, 616),
|
||||||
|
search=(521, 531, 760, 636),
|
||||||
|
color=(112, 212, 247),
|
||||||
|
button=(541, 551, 740, 616),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SKIP_OK_UPPER = ButtonWrapper(
|
||||||
|
name='SKIP_OK_UPPER',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/SKIP_OK_UPPER.png',
|
||||||
|
area=(542, 474, 738, 545),
|
||||||
|
search=(522, 454, 758, 565),
|
||||||
|
color=(112, 212, 248),
|
||||||
|
button=(542, 474, 738, 545),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SKIP_SKIP = ButtonWrapper(
|
||||||
|
name='SKIP_SKIP',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/SKIP_SKIP.png',
|
||||||
|
area=(545, 475, 736, 540),
|
||||||
|
search=(525, 455, 756, 560),
|
||||||
|
color=(110, 207, 243),
|
||||||
|
button=(545, 475, 736, 540),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SWEEP = ButtonWrapper(
|
||||||
|
name='SWEEP',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/SWEEP.png',
|
||||||
|
area=(796, 385, 1067, 427),
|
||||||
|
search=(776, 365, 1087, 447),
|
||||||
|
color=(109, 202, 235),
|
||||||
|
button=(796, 385, 1067, 427),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
|
SWEEP_CONFIRM = ButtonWrapper(
|
||||||
|
name='SWEEP_CONFIRM',
|
||||||
|
jp=Button(
|
||||||
|
file='./assets/jp/stage/sweep/SWEEP_CONFIRM.png',
|
||||||
|
area=(611, 147, 669, 177),
|
||||||
|
search=(591, 127, 689, 197),
|
||||||
|
color=(143, 156, 170),
|
||||||
|
button=(664, 470, 871, 534),
|
||||||
|
),
|
||||||
|
en=None,
|
||||||
|
)
|
||||||
@ -2,11 +2,12 @@ import cv2
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from module.base.base import ModuleBase
|
from module.base.base import ModuleBase
|
||||||
from module.base.button import ButtonWrapper, ClickButton
|
from module.base.button import ClickButton, match_template
|
||||||
from module.base.timer import Timer
|
from module.base.timer import Timer
|
||||||
from module.base.utils import area_pad, area_size, area_offset, random_rectangle_vector_opted
|
from module.base.utils import area_pad, area_size, area_offset, random_rectangle_vector_opted
|
||||||
from module.logger import logger
|
from module.logger import logger
|
||||||
from module.ocr.ocr import Ocr
|
from module.ocr.ocr import Ocr
|
||||||
|
from tasks.stage.assets.assets_stage_list import *
|
||||||
|
|
||||||
|
|
||||||
class StageList:
|
class StageList:
|
||||||
@ -15,17 +16,19 @@ class StageList:
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
name,
|
name,
|
||||||
area_stage: ButtonWrapper,
|
button_list: ButtonWrapper = None,
|
||||||
area_index: ButtonWrapper,
|
button_index: ButtonWrapper = None,
|
||||||
area_item: ButtonWrapper,
|
button_item: ButtonWrapper = None,
|
||||||
button_enter: ButtonWrapper,
|
button_enter: ButtonWrapper = None,
|
||||||
|
button_stars: ButtonWrapper = None,
|
||||||
drag_direction: str = "down"
|
drag_direction: str = "down"
|
||||||
):
|
):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.stage = area_stage
|
self.stage = button_list if button_list else STAGE_LIST
|
||||||
self.index_ocr = Ocr(area_index, lang='en')
|
self.index_ocr = Ocr(button_index if button_index else OCR_INDEX, lang='en')
|
||||||
self.stage_item = area_item.button
|
self.stage_item = (button_item if button_item else STAGE_ITEM).button
|
||||||
self.enter = button_enter
|
self.enter = button_enter if button_enter else STAGE_ENTER
|
||||||
|
self.sweepable = button_stars if button_stars else STAGE_STARS
|
||||||
self.drag_direction = drag_direction
|
self.drag_direction = drag_direction
|
||||||
|
|
||||||
self.current_index_min = 1
|
self.current_index_min = 1
|
||||||
@ -43,7 +46,8 @@ class StageList:
|
|||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.name)
|
return hash(self.name)
|
||||||
|
|
||||||
def _get_indexes(self) -> list[int]:
|
@property
|
||||||
|
def _indexes(self) -> list[int]:
|
||||||
return list(map(lambda x: int(x.ocr_text), self.current_indexes))
|
return list(map(lambda x: int(x.ocr_text), self.current_indexes))
|
||||||
|
|
||||||
def load_stage_indexes(self, main: ModuleBase):
|
def load_stage_indexes(self, main: ModuleBase):
|
||||||
@ -53,7 +57,7 @@ class StageList:
|
|||||||
if not self.current_indexes:
|
if not self.current_indexes:
|
||||||
logger.warning(f'No valid index in {self.index_ocr.name}')
|
logger.warning(f'No valid index in {self.index_ocr.name}')
|
||||||
return
|
return
|
||||||
indexes = self._get_indexes()
|
indexes = self._indexes
|
||||||
|
|
||||||
self.current_index_min = min(indexes)
|
self.current_index_min = min(indexes)
|
||||||
self.current_index_max = max(indexes)
|
self.current_index_max = max(indexes)
|
||||||
@ -119,7 +123,7 @@ class StageList:
|
|||||||
timeout=Timer(1.5, 5)
|
timeout=Timer(1.5, 5)
|
||||||
)
|
)
|
||||||
|
|
||||||
indexes = self._get_indexes()
|
indexes = self._indexes
|
||||||
if indexes and last_indexes == set(indexes):
|
if indexes and last_indexes == set(indexes):
|
||||||
logger.warning(f'No more index {index}')
|
logger.warning(f'No more index {index}')
|
||||||
return False
|
return False
|
||||||
@ -129,20 +133,28 @@ class StageList:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _match_clickable_points(image, template, threshold=0.85):
|
def _match_clickable_points(image, template, threshold=0.85):
|
||||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
|
||||||
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
|
template = cv2.cvtColor(template, cv2.COLOR_RGB2GRAY)
|
||||||
|
|
||||||
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
|
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
|
||||||
loc = np.where(res >= threshold)
|
loc = np.where(res >= threshold)
|
||||||
return [point for point in zip(*loc[::-1])]
|
return [point for point in zip(*loc[::-1])]
|
||||||
|
|
||||||
|
def is_sweepable(self, image, main: ModuleBase, skip_first_screenshot=True) -> bool:
|
||||||
|
if not skip_first_screenshot:
|
||||||
|
main.device.screenshot()
|
||||||
|
|
||||||
|
return match_template(image, self.sweepable.matched_button.image)
|
||||||
|
|
||||||
def select_index_enter(
|
def select_index_enter(
|
||||||
self,
|
self,
|
||||||
index: int,
|
index: int,
|
||||||
main: ModuleBase,
|
main: ModuleBase,
|
||||||
insight: bool = True,
|
insight: bool = True,
|
||||||
|
sweepable: bool = True,
|
||||||
|
offset: tuple[int, int] = (-20, -15),
|
||||||
skip_first_screenshot: bool = True,
|
skip_first_screenshot: bool = True,
|
||||||
interval: int = 5
|
interval: int = 2
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if insight and not self.insight_index(index, main, skip_first_screenshot):
|
if insight and not self.insight_index(index, main, skip_first_screenshot):
|
||||||
return False
|
return False
|
||||||
@ -164,9 +176,14 @@ class StageList:
|
|||||||
logger.warning(f'No index {index} in {self.index_ocr.name}')
|
logger.warning(f'No index {index} in {self.index_ocr.name}')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
stage_item_box = area_pad((0, 0, *area_size(self.stage_item)))
|
stage_item_box = area_pad((*offset, *area_size(self.stage_item)))
|
||||||
search_box = area_offset(stage_item_box, index_box.box[:2])
|
search_box = area_offset(stage_item_box, index_box.box[:2])
|
||||||
search_image = main.image_crop(search_box)
|
search_image = main.image_crop(search_box)
|
||||||
|
|
||||||
|
if sweepable and not self.is_sweepable(search_image, main, skip_first_screenshot):
|
||||||
|
logger.warning(f'Index {index} is not sweepable')
|
||||||
|
return False
|
||||||
|
|
||||||
points = self._match_clickable_points(search_image, self.enter.matched_button.image)
|
points = self._match_clickable_points(search_image, self.enter.matched_button.image)
|
||||||
|
|
||||||
if not points:
|
if not points:
|
||||||
254
tasks/stage/sweep.py
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
from module.base.base import ModuleBase
|
||||||
|
from module.base.timer import Timer
|
||||||
|
from module.logger import logger
|
||||||
|
from module.ocr.ocr import Digit
|
||||||
|
from enum import Enum
|
||||||
|
from tasks.stage.assets.assets_stage_sweep import *
|
||||||
|
|
||||||
|
|
||||||
|
class SweepStatus(Enum):
|
||||||
|
SELECT = 1
|
||||||
|
START = 2
|
||||||
|
CONFIRM = 3
|
||||||
|
SKIP = 4
|
||||||
|
END = 5
|
||||||
|
FINISH = 6
|
||||||
|
|
||||||
|
|
||||||
|
class StageSweep:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
sweep_num: int,
|
||||||
|
max_sweep: int,
|
||||||
|
):
|
||||||
|
self.name = name
|
||||||
|
self.sweep_num = sweep_num
|
||||||
|
|
||||||
|
self.check: ButtonWrapper = None
|
||||||
|
self.num: Digit = None
|
||||||
|
self.plus: ButtonWrapper = None
|
||||||
|
self.minus: ButtonWrapper = None
|
||||||
|
self.max: ButtonWrapper = None
|
||||||
|
self.min: ButtonWrapper = None
|
||||||
|
self.sweep: ButtonWrapper = None
|
||||||
|
self.sweep_confirm: ButtonWrapper = None
|
||||||
|
self.enter: ButtonWrapper = None
|
||||||
|
self.exit: ButtonWrapper = None
|
||||||
|
self.skip_skip: ButtonWrapper = None
|
||||||
|
self.skip_ok_upper: ButtonWrapper = None
|
||||||
|
self.skip_ok_lower: ButtonWrapper = None
|
||||||
|
self.set_button()
|
||||||
|
|
||||||
|
self.min_sweep = 1
|
||||||
|
self.max_sweep = max_sweep
|
||||||
|
self.current_sweep = 0
|
||||||
|
|
||||||
|
self.sweep_method = None
|
||||||
|
self.set_mode()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'StageSweep({self.name})'
|
||||||
|
|
||||||
|
__repr__ = __str__
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return str(self) == str(other)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.name)
|
||||||
|
|
||||||
|
def set_button(
|
||||||
|
self,
|
||||||
|
button_check: ButtonWrapper = None,
|
||||||
|
button_num: ButtonWrapper = None,
|
||||||
|
button_plus: ButtonWrapper = None,
|
||||||
|
button_minus: ButtonWrapper = None,
|
||||||
|
button_max: ButtonWrapper = None,
|
||||||
|
button_min: ButtonWrapper = None,
|
||||||
|
button_sweep: ButtonWrapper = None,
|
||||||
|
button_sweep_confirm: ButtonWrapper = None,
|
||||||
|
button_enter: ButtonWrapper = None,
|
||||||
|
button_exit: ButtonWrapper = None,
|
||||||
|
button_skip_skip: ButtonWrapper = None,
|
||||||
|
button_skip_ok_upper: ButtonWrapper = None,
|
||||||
|
button_skip_ok_lower: ButtonWrapper = None,
|
||||||
|
):
|
||||||
|
self.check = button_check if button_check else CHECK_SWEEP
|
||||||
|
self.num = Digit(button_num if button_num else OCR_NUM)
|
||||||
|
self.plus = button_plus if button_plus else PLUS
|
||||||
|
self.minus = button_minus if button_minus else MINUS
|
||||||
|
self.max = button_max if button_max else MAX
|
||||||
|
self.min = button_min if button_min else MIN
|
||||||
|
self.sweep = button_sweep if button_sweep else SWEEP
|
||||||
|
self.sweep_confirm = button_sweep_confirm if button_sweep_confirm else SWEEP_CONFIRM
|
||||||
|
self.enter = button_enter if button_enter else ENTER
|
||||||
|
self.exit = button_exit if button_exit else EXIT
|
||||||
|
self.skip_skip = button_skip_skip if button_skip_skip else SKIP_SKIP
|
||||||
|
self.skip_ok_upper = button_skip_ok_upper if button_skip_ok_upper else SKIP_OK_UPPER
|
||||||
|
self.skip_ok_lower = button_skip_ok_lower if button_skip_ok_lower else SKIP_OK_LOWER
|
||||||
|
|
||||||
|
def set_mode(self, mode: str = None):
|
||||||
|
if mode is None:
|
||||||
|
match self.sweep_num:
|
||||||
|
case 0:
|
||||||
|
self.sweep_method = self.set_sweep_min
|
||||||
|
case -1:
|
||||||
|
self.sweep_method = self.set_sweep_max
|
||||||
|
case x if x > 0:
|
||||||
|
self.sweep_method = self.set_sweep_num
|
||||||
|
case _:
|
||||||
|
logger.warning(f'Invalid sweep num: {self.sweep_num}')
|
||||||
|
return
|
||||||
|
match mode:
|
||||||
|
case 'max':
|
||||||
|
self.sweep_method = self.set_sweep_max
|
||||||
|
case 'min':
|
||||||
|
self.sweep_method = self.set_sweep_min
|
||||||
|
case _:
|
||||||
|
logger.warning(f'Invalid sweep mode: {mode}')
|
||||||
|
|
||||||
|
def check_sweep(self, main: ModuleBase):
|
||||||
|
return main.appear(self.check)
|
||||||
|
|
||||||
|
def check_skip(self, main: ModuleBase):
|
||||||
|
return main.appear(self.skip_skip) or main.appear(self.skip_ok_upper) or main.appear(self.skip_ok_lower)
|
||||||
|
|
||||||
|
def load_sweep_num(self, main: ModuleBase):
|
||||||
|
while 1:
|
||||||
|
main.device.screenshot()
|
||||||
|
ocr_result = self.num.detect_and_ocr(main.device.image)
|
||||||
|
if not ocr_result:
|
||||||
|
logger.warning(f'No valid num in {self.num.name}')
|
||||||
|
continue
|
||||||
|
if len(ocr_result) == 1:
|
||||||
|
self.current_sweep = int(ocr_result[0].ocr_text)
|
||||||
|
return
|
||||||
|
|
||||||
|
def set_sweep_num(self, main: ModuleBase, skip_first_screenshot=True) -> bool:
|
||||||
|
num = self.sweep_num
|
||||||
|
if num < self.min_sweep or num > self.max_sweep:
|
||||||
|
logger.warning(f'Invalid sweep num: {num}')
|
||||||
|
return False
|
||||||
|
logger.info(f'Set sweep num: {num}')
|
||||||
|
retry = Timer(1, 2)
|
||||||
|
while 1:
|
||||||
|
if skip_first_screenshot:
|
||||||
|
skip_first_screenshot = False
|
||||||
|
else:
|
||||||
|
main.device.screenshot()
|
||||||
|
|
||||||
|
self.load_sweep_num(main)
|
||||||
|
|
||||||
|
if self.current_sweep == num:
|
||||||
|
logger.info(f'Sweep num reaches {num}')
|
||||||
|
return True
|
||||||
|
elif self.current_sweep == 0:
|
||||||
|
logger.info(f'Current sweep num is 0')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if retry.reached_and_reset():
|
||||||
|
diff = num - self.current_sweep
|
||||||
|
button = self.plus if diff > 0 else self.minus
|
||||||
|
main.device.multi_click(button, abs(diff), interval=(0.2, 0.3))
|
||||||
|
|
||||||
|
def set_sweep_max(self, main: ModuleBase, skip_first_screenshot=True):
|
||||||
|
logger.info(f'Set sweep max: {self.max_sweep}')
|
||||||
|
retry = Timer(1, 2)
|
||||||
|
count = 0
|
||||||
|
while 1:
|
||||||
|
if skip_first_screenshot:
|
||||||
|
skip_first_screenshot = False
|
||||||
|
else:
|
||||||
|
main.device.screenshot()
|
||||||
|
|
||||||
|
self.load_sweep_num(main)
|
||||||
|
|
||||||
|
if self.current_sweep == self.max_sweep:
|
||||||
|
logger.info(f'Sweep max reaches {self.max_sweep}')
|
||||||
|
return True
|
||||||
|
elif count == 1 and self.current_sweep != 1:
|
||||||
|
logger.info("Set sweep max")
|
||||||
|
return True
|
||||||
|
elif self.current_sweep == 0:
|
||||||
|
logger.info(f'Current sweep num is 0')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if retry.reached_and_reset():
|
||||||
|
main.click_with_interval(self.max, interval=0)
|
||||||
|
count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if count > 2:
|
||||||
|
logger.info("Set sweep max")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def set_sweep_min(self, main: ModuleBase, skip_first_screenshot=True):
|
||||||
|
logger.info(f'Set sweep min: {self.min_sweep}')
|
||||||
|
retry = Timer(1, 2)
|
||||||
|
while 1:
|
||||||
|
if skip_first_screenshot:
|
||||||
|
skip_first_screenshot = False
|
||||||
|
else:
|
||||||
|
main.device.screenshot()
|
||||||
|
|
||||||
|
self.load_sweep_num(main)
|
||||||
|
|
||||||
|
if self.current_sweep == self.min_sweep:
|
||||||
|
logger.info(f'Sweep min reaches {self.min_sweep}')
|
||||||
|
return True
|
||||||
|
elif self.current_sweep == 0:
|
||||||
|
logger.info(f'Current sweep num is 0')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if retry.reached_and_reset():
|
||||||
|
main.click_with_interval(self.min, interval=0)
|
||||||
|
|
||||||
|
def do_sweep(self, main: ModuleBase, skip_first_screenshot=True) -> bool:
|
||||||
|
timer = Timer(0.5, 1)
|
||||||
|
timer_stable = Timer(0.5, 1).start()
|
||||||
|
status = SweepStatus.SELECT
|
||||||
|
while 1:
|
||||||
|
if not timer_stable.reached():
|
||||||
|
continue
|
||||||
|
|
||||||
|
if skip_first_screenshot:
|
||||||
|
skip_first_screenshot = False
|
||||||
|
else:
|
||||||
|
main.device.screenshot()
|
||||||
|
|
||||||
|
if timer.reached_and_reset():
|
||||||
|
logger.attr("Status", status)
|
||||||
|
match status:
|
||||||
|
case SweepStatus.SELECT:
|
||||||
|
if self.sweep_method(main, skip_first_screenshot):
|
||||||
|
status = SweepStatus.START
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
case SweepStatus.START:
|
||||||
|
main.appear_then_click(self.sweep, interval=1)
|
||||||
|
if main.appear(self.sweep_confirm):
|
||||||
|
status = SweepStatus.CONFIRM
|
||||||
|
case SweepStatus.CONFIRM:
|
||||||
|
main.appear_then_click(self.sweep_confirm, interval=1)
|
||||||
|
if self.check_skip(main):
|
||||||
|
status = SweepStatus.SKIP
|
||||||
|
case SweepStatus.SKIP:
|
||||||
|
main.appear_then_click(self.skip_skip)
|
||||||
|
main.appear_then_click(self.skip_ok_upper)
|
||||||
|
main.appear_then_click(self.skip_ok_lower)
|
||||||
|
if self.check_sweep(main):
|
||||||
|
status = SweepStatus.END
|
||||||
|
case SweepStatus.END:
|
||||||
|
main.appear_then_click(self.exit, interval=1)
|
||||||
|
if not main.appear(self.check):
|
||||||
|
status = SweepStatus.FINISH
|
||||||
|
case SweepStatus.FINISH:
|
||||||
|
pass
|
||||||
|
case _:
|
||||||
|
logger.warning(f'Invalid status: {status}')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if status == SweepStatus.FINISH:
|
||||||
|
logger.info(f'Sweep finish')
|
||||||
|
return True
|
||||||