1
0
mirror of https://github.com/TheFunny/ArisuAutoSweeper synced 2026-01-20 16:05:13 +00:00

Compare commits

..

No commits in common. "84f78230d25d4dcad4f260fbd2ae169198f37d1d" and "6cbc4f0788ce7f0a46fe9eea8df1b8361cc926ed" have entirely different histories.

43 changed files with 36 additions and 680 deletions

View File

@ -12,15 +12,12 @@
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 floor - [x] **Cafe** Claim rewards / Interact / Second cafe
- [x] **Club** Claim AP - [x] **Circle** Claim AP
- [x] **Mailbox** Claim rewards - [x] **Mailbox** Claim rewards
- [x] **Tactical Challenge** Claim rewards / Auto battle - [x] **Tactical Challenge** Claim rewards / Auto battle
Supported servers: _Currently only supports JP server._
- [x] JP
- [x] OVERSEA - Global
## Relative projects ## Relative projects
@ -41,5 +38,3 @@ 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.

View File

@ -17,10 +17,7 @@
- [x] **邮箱** 领取奖励 - [x] **邮箱** 领取奖励
- [x] **战术对抗赛** 领取奖励 / 自动战斗 - [x] **战术对抗赛** 领取奖励 / 自动战斗
目前支持的服务器: _目前仅支持日服。_
- [x] 日服
- [x] 国际服 - 全球
## 相关项目 ## 相关项目
@ -39,5 +36,3 @@
感谢 [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) 提供英语支持。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@ -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 Floor", "name": "Second Cafe",
"help": "JP server only\nEnable auto switch to second floor and perform interaction" "help": "Enable auto switch to second cafe and perform interaction"
} }
}, },
"TacticalChallenge": { "TacticalChallenge": {

View File

@ -206,7 +206,7 @@
}, },
"SecondCafe": { "SecondCafe": {
"name": "第二咖啡厅", "name": "第二咖啡厅",
"help": "仅支持日服\n自动切换第二咖啡厅进行互动点击" "help": "自动切换第二咖啡厅进行互动点击"
} }
}, },
"TacticalChallenge": { "TacticalChallenge": {

View File

@ -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 Blue Archive package found, ' logger.critical(f'No Star Rail package found, '
f'please confirm Blue Archive has been installed on device "{self.serial}"') f'please confirm Star Rail 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 Blue Archive packages found, auto package detection cannot decide which to choose, ' f'Multiple Star Rail 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

View File

@ -2,12 +2,11 @@ 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 ClickButton, match_template from module.base.button import ButtonWrapper, ClickButton
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:
@ -16,19 +15,17 @@ class StageList:
def __init__( def __init__(
self, self,
name, name,
button_list: ButtonWrapper = None, area_stage: ButtonWrapper,
button_index: ButtonWrapper = None, area_index: ButtonWrapper,
button_item: ButtonWrapper = None, area_item: ButtonWrapper,
button_enter: ButtonWrapper = None, button_enter: ButtonWrapper,
button_stars: ButtonWrapper = None,
drag_direction: str = "down" drag_direction: str = "down"
): ):
self.name = name self.name = name
self.stage = button_list if button_list else STAGE_LIST self.stage = area_stage
self.index_ocr = Ocr(button_index if button_index else OCR_INDEX, lang='en') self.index_ocr = Ocr(area_index, lang='en')
self.stage_item = (button_item if button_item else STAGE_ITEM).button self.stage_item = area_item.button
self.enter = button_enter if button_enter else STAGE_ENTER self.enter = button_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
@ -46,8 +43,7 @@ class StageList:
def __hash__(self): def __hash__(self):
return hash(self.name) return hash(self.name)
@property def _get_indexes(self) -> list[int]:
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):
@ -57,7 +53,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._indexes indexes = self._get_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)
@ -123,7 +119,7 @@ class StageList:
timeout=Timer(1.5, 5) timeout=Timer(1.5, 5)
) )
indexes = self._indexes indexes = self._get_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
@ -133,28 +129,20 @@ 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_RGB2GRAY) image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
template = cv2.cvtColor(template, cv2.COLOR_RGB2GRAY) template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
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 = 2 interval: int = 5
) -> 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
@ -176,14 +164,9 @@ 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((*offset, *area_size(self.stage_item))) stage_item_box = area_pad((0, 0, *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:

View File

@ -1,71 +0,0 @@
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,
)

View File

@ -5,7 +5,6 @@ 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
@ -34,8 +33,6 @@ 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:
@ -54,7 +51,8 @@ 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
hsv = cv2.cvtColor(image, cv2.COLOR_RGB2HSV) image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
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])
@ -64,8 +62,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_RGB2GRAY) image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
template = cv2.cvtColor(self.template.matched_button.image, cv2.COLOR_RGB2GRAY) template = cv2.cvtColor(self.btn.matched_button.image, cv2.COLOR_BGR2GRAY)
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)
@ -80,11 +78,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.template.width, self.template.height), offset) area = area_offset((0, 0, self.btn.width, self.btn.height), offset)
return [ return [
ClickButton( ClickButton(
button=area_offset(area, offset=point), button=area_offset(area, offset=point),
name=self.template.name name=self.btn.name
) )
for point in points for point in points
] ]
@ -190,23 +188,15 @@ 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)
@ -250,7 +240,7 @@ class Cafe(UI):
is_reset = True is_reset = True
continue continue
if self.is_second_cafe_on and not is_second and status == CafeStatus.FINISHED: if 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
@ -276,14 +266,11 @@ class Cafe(UI):
logger.attr('Status', status) logger.attr('Status', status)
status = self._handle_cafe(status) status = self._handle_cafe(status)
if not self.is_second_cafe_on: if not 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)

View File

@ -1,71 +0,0 @@
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,
)

View File

@ -1,60 +0,0 @@
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,
)

View File

@ -1,148 +0,0 @@
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,
)

View File

@ -1,254 +0,0 @@
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