mirror of
https://github.com/TheFunny/ArisuAutoSweeper
synced 2026-06-09 20:04:52 +00:00
Upload code
This commit is contained in:
@@ -0,0 +1,385 @@
|
||||
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 ```
|
||||
|
||||
ACCOUNT_INFO_CHECK = ButtonWrapper(
|
||||
name='ACCOUNT_INFO_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/ACCOUNT_INFO_CHECK.png',
|
||||
area=(108, 11, 235, 36),
|
||||
search=(88, 0, 255, 56),
|
||||
color=(193, 201, 210),
|
||||
button=(108, 11, 235, 36),
|
||||
),
|
||||
)
|
||||
BACK = ButtonWrapper(
|
||||
name='BACK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/BACK.png',
|
||||
area=(34, 19, 81, 56),
|
||||
search=(14, 0, 101, 76),
|
||||
color=(93, 118, 164),
|
||||
button=(34, 19, 81, 56),
|
||||
),
|
||||
)
|
||||
BOUNTY_CHECK = ButtonWrapper(
|
||||
name='BOUNTY_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/BOUNTY_CHECK.png',
|
||||
area=(107, 10, 158, 37),
|
||||
search=(87, 0, 178, 57),
|
||||
color=(150, 164, 177),
|
||||
button=(107, 10, 158, 37),
|
||||
),
|
||||
)
|
||||
CAFE_CHECK = ButtonWrapper(
|
||||
name='CAFE_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/CAFE_CHECK.png',
|
||||
area=(264, 11, 337, 35),
|
||||
search=(244, 0, 357, 55),
|
||||
color=(188, 197, 205),
|
||||
button=(264, 11, 337, 35),
|
||||
),
|
||||
)
|
||||
CIRCLE_CHECK = ButtonWrapper(
|
||||
name='CIRCLE_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/CIRCLE_CHECK.png',
|
||||
area=(107, 11, 211, 36),
|
||||
search=(87, 0, 231, 56),
|
||||
color=(195, 203, 211),
|
||||
button=(107, 11, 211, 36),
|
||||
),
|
||||
)
|
||||
COMMISSIONS_CHECK = ButtonWrapper(
|
||||
name='COMMISSIONS_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/COMMISSIONS_CHECK.png',
|
||||
area=(107, 10, 158, 37),
|
||||
search=(87, 0, 178, 57),
|
||||
color=(146, 160, 174),
|
||||
button=(107, 10, 158, 37),
|
||||
),
|
||||
)
|
||||
CRAFTING_CHECK = ButtonWrapper(
|
||||
name='CRAFTING_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/CRAFTING_CHECK.png',
|
||||
area=(108, 10, 214, 37),
|
||||
search=(88, 0, 234, 57),
|
||||
color=(200, 208, 215),
|
||||
button=(108, 10, 214, 37),
|
||||
),
|
||||
)
|
||||
GACHA_CHECK = ButtonWrapper(
|
||||
name='GACHA_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/GACHA_CHECK.png',
|
||||
area=(159, 10, 210, 37),
|
||||
search=(139, 0, 230, 57),
|
||||
color=(135, 148, 164),
|
||||
button=(159, 10, 210, 37),
|
||||
),
|
||||
)
|
||||
HOME = ButtonWrapper(
|
||||
name='HOME',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/HOME.png',
|
||||
area=(1218, 8, 1253, 41),
|
||||
search=(1198, 0, 1273, 61),
|
||||
color=(168, 182, 205),
|
||||
button=(1218, 8, 1253, 41),
|
||||
),
|
||||
)
|
||||
LOADING_CHECK = ButtonWrapper(
|
||||
name='LOADING_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/LOADING_CHECK.png',
|
||||
area=(1084, 659, 1120, 674),
|
||||
search=(1064, 639, 1140, 694),
|
||||
color=(173, 196, 219),
|
||||
button=(1084, 659, 1120, 674),
|
||||
),
|
||||
)
|
||||
MAIL_CHECK = ButtonWrapper(
|
||||
name='MAIL_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIL_CHECK.png',
|
||||
area=(108, 9, 186, 37),
|
||||
search=(88, 0, 206, 57),
|
||||
color=(205, 212, 219),
|
||||
button=(108, 9, 186, 37),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_CAFE = ButtonWrapper(
|
||||
name='MAIN_GO_TO_CAFE',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_CAFE.png',
|
||||
area=(81, 638, 102, 668),
|
||||
search=(61, 618, 122, 688),
|
||||
color=(156, 209, 233),
|
||||
button=(81, 638, 102, 668),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_CIRCLE = ButtonWrapper(
|
||||
name='MAIN_GO_TO_CIRCLE',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_CIRCLE.png',
|
||||
area=(540, 631, 583, 660),
|
||||
search=(520, 611, 603, 680),
|
||||
color=(131, 204, 234),
|
||||
button=(540, 631, 583, 660),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_CRAFTING = ButtonWrapper(
|
||||
name='MAIN_GO_TO_CRAFTING',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_CRAFTING.png',
|
||||
area=(665, 622, 693, 664),
|
||||
search=(645, 602, 713, 684),
|
||||
color=(192, 229, 241),
|
||||
button=(665, 622, 693, 664),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_GACHA = ButtonWrapper(
|
||||
name='MAIN_GO_TO_GACHA',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_GACHA.png',
|
||||
area=(900, 623, 924, 670),
|
||||
search=(880, 603, 944, 690),
|
||||
color=(157, 219, 241),
|
||||
button=(900, 623, 924, 670),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_MAIL = ButtonWrapper(
|
||||
name='MAIN_GO_TO_MAIL',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_MAIL.png',
|
||||
area=(1130, 29, 1156, 49),
|
||||
search=(1110, 9, 1176, 69),
|
||||
color=(94, 121, 166),
|
||||
button=(1130, 29, 1156, 49),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_MOMOTALK = ButtonWrapper(
|
||||
name='MAIN_GO_TO_MOMOTALK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_MOMOTALK.png',
|
||||
area=(154, 134, 177, 158),
|
||||
search=(134, 114, 197, 178),
|
||||
color=(255, 219, 227),
|
||||
button=(154, 134, 177, 158),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_PURCHASE = ButtonWrapper(
|
||||
name='MAIN_GO_TO_PURCHASE',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_PURCHASE.png',
|
||||
area=(148, 204, 183, 253),
|
||||
search=(128, 184, 203, 273),
|
||||
color=(172, 214, 239),
|
||||
button=(148, 204, 183, 253),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_SCHEDULE = ButtonWrapper(
|
||||
name='MAIN_GO_TO_SCHEDULE',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_SCHEDULE.png',
|
||||
area=(194, 638, 216, 672),
|
||||
search=(174, 618, 236, 692),
|
||||
color=(149, 194, 222),
|
||||
button=(194, 638, 216, 672),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_SHOP = ButtonWrapper(
|
||||
name='MAIN_GO_TO_SHOP',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_SHOP.png',
|
||||
area=(773, 630, 816, 667),
|
||||
search=(753, 610, 836, 687),
|
||||
color=(146, 208, 235),
|
||||
button=(773, 630, 816, 667),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_TASK = ButtonWrapper(
|
||||
name='MAIN_GO_TO_TASK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_TASK.png',
|
||||
area=(52, 220, 78, 248),
|
||||
search=(32, 200, 98, 268),
|
||||
color=(226, 207, 203),
|
||||
button=(52, 220, 78, 248),
|
||||
),
|
||||
)
|
||||
MAIN_GO_TO_WORK = ButtonWrapper(
|
||||
name='MAIN_GO_TO_WORK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MAIN_GO_TO_WORK.png',
|
||||
area=(1167, 605, 1241, 632),
|
||||
search=(1147, 585, 1261, 652),
|
||||
color=(135, 149, 169),
|
||||
button=(1167, 605, 1241, 632),
|
||||
),
|
||||
)
|
||||
MISSION_CHECK = ButtonWrapper(
|
||||
name='MISSION_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MISSION_CHECK.png',
|
||||
area=(108, 12, 183, 36),
|
||||
search=(88, 0, 203, 56),
|
||||
color=(188, 197, 206),
|
||||
button=(108, 12, 183, 36),
|
||||
),
|
||||
)
|
||||
MOMOTALK_CHECK = ButtonWrapper(
|
||||
name='MOMOTALK_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MOMOTALK_CHECK.png',
|
||||
area=(144, 107, 169, 130),
|
||||
search=(124, 87, 189, 150),
|
||||
color=(253, 211, 219),
|
||||
button=(144, 107, 169, 130),
|
||||
),
|
||||
)
|
||||
MOMOTALK_GO_TO_MAIN = ButtonWrapper(
|
||||
name='MOMOTALK_GO_TO_MAIN',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/MOMOTALK_GO_TO_MAIN.png',
|
||||
area=(1108, 105, 1134, 131),
|
||||
search=(1088, 85, 1154, 151),
|
||||
color=(252, 182, 194),
|
||||
button=(1108, 105, 1134, 131),
|
||||
),
|
||||
)
|
||||
SCHEDULE_CHECK = ButtonWrapper(
|
||||
name='SCHEDULE_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/SCHEDULE_CHECK.png',
|
||||
area=(108, 12, 159, 36),
|
||||
search=(88, 0, 179, 56),
|
||||
color=(188, 197, 206),
|
||||
button=(108, 12, 159, 36),
|
||||
),
|
||||
)
|
||||
SCHOOL_EXCHANGE_CHECK = ButtonWrapper(
|
||||
name='SCHOOL_EXCHANGE_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/SCHOOL_EXCHANGE_CHECK.png',
|
||||
area=(107, 11, 158, 36),
|
||||
search=(87, 0, 178, 56),
|
||||
color=(134, 149, 164),
|
||||
button=(107, 11, 158, 36),
|
||||
),
|
||||
)
|
||||
SHOP_CHECK = ButtonWrapper(
|
||||
name='SHOP_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/SHOP_CHECK.png',
|
||||
area=(108, 10, 212, 36),
|
||||
search=(88, 0, 232, 56),
|
||||
color=(200, 208, 215),
|
||||
button=(108, 10, 212, 36),
|
||||
),
|
||||
)
|
||||
STORY_CHECK = ButtonWrapper(
|
||||
name='STORY_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/STORY_CHECK.png',
|
||||
area=(108, 11, 157, 36),
|
||||
search=(88, 0, 177, 56),
|
||||
color=(194, 203, 211),
|
||||
button=(108, 11, 157, 36),
|
||||
),
|
||||
)
|
||||
TACTICAL_CHALLENGE_CHECK = ButtonWrapper(
|
||||
name='TACTICAL_CHALLENGE_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/TACTICAL_CHALLENGE_CHECK.png',
|
||||
area=(107, 11, 133, 37),
|
||||
search=(87, 0, 153, 57),
|
||||
color=(129, 145, 161),
|
||||
button=(107, 11, 133, 37),
|
||||
),
|
||||
)
|
||||
TASK_CHECK = ButtonWrapper(
|
||||
name='TASK_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/TASK_CHECK.png',
|
||||
area=(109, 12, 155, 36),
|
||||
search=(89, 0, 175, 56),
|
||||
color=(189, 198, 207),
|
||||
button=(109, 12, 155, 36),
|
||||
),
|
||||
)
|
||||
WORK_CHECK = ButtonWrapper(
|
||||
name='WORK_CHECK',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/WORK_CHECK.png',
|
||||
area=(107, 10, 185, 38),
|
||||
search=(87, 0, 205, 58),
|
||||
color=(164, 175, 187),
|
||||
button=(107, 10, 185, 38),
|
||||
),
|
||||
)
|
||||
WORK_GO_TO_BOUNTY = ButtonWrapper(
|
||||
name='WORK_GO_TO_BOUNTY',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/WORK_GO_TO_BOUNTY.png',
|
||||
area=(669, 412, 765, 436),
|
||||
search=(649, 392, 785, 456),
|
||||
color=(165, 181, 208),
|
||||
button=(669, 412, 765, 436),
|
||||
),
|
||||
)
|
||||
WORK_GO_TO_COMMISSIONS = ButtonWrapper(
|
||||
name='WORK_GO_TO_COMMISSIONS',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/WORK_GO_TO_COMMISSIONS.png',
|
||||
area=(655, 494, 751, 518),
|
||||
search=(635, 474, 771, 538),
|
||||
color=(165, 179, 204),
|
||||
button=(655, 494, 751, 518),
|
||||
),
|
||||
)
|
||||
WORK_GO_TO_MISSION = ButtonWrapper(
|
||||
name='WORK_GO_TO_MISSION',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/WORK_GO_TO_MISSION.png',
|
||||
area=(720, 160, 803, 199),
|
||||
search=(700, 140, 823, 219),
|
||||
color=(165, 178, 204),
|
||||
button=(720, 160, 803, 199),
|
||||
),
|
||||
)
|
||||
WORK_GO_TO_SCHOOL_EXCHANGE = ButtonWrapper(
|
||||
name='WORK_GO_TO_SCHOOL_EXCHANGE',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/WORK_GO_TO_SCHOOL_EXCHANGE.png',
|
||||
area=(641, 575, 758, 599),
|
||||
search=(621, 555, 778, 619),
|
||||
color=(165, 179, 204),
|
||||
button=(641, 575, 758, 599),
|
||||
),
|
||||
)
|
||||
WORK_GO_TO_STORY = ButtonWrapper(
|
||||
name='WORK_GO_TO_STORY',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/WORK_GO_TO_STORY.png',
|
||||
area=(995, 163, 1032, 197),
|
||||
search=(975, 143, 1052, 217),
|
||||
color=(191, 201, 219),
|
||||
button=(995, 163, 1032, 197),
|
||||
),
|
||||
)
|
||||
WORK_GO_TO_TACTICAL_CHALLENGE = ButtonWrapper(
|
||||
name='WORK_GO_TO_TACTICAL_CHALLENGE',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/page/WORK_GO_TO_TACTICAL_CHALLENGE.png',
|
||||
area=(1012, 535, 1151, 562),
|
||||
search=(992, 515, 1171, 582),
|
||||
color=(159, 174, 200),
|
||||
button=(1012, 535, 1151, 562),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,75 @@
|
||||
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 ```
|
||||
|
||||
AFFECTION_LEVEL_UP = ButtonWrapper(
|
||||
name='AFFECTION_LEVEL_UP',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/popup/AFFECTION_LEVEL_UP.png',
|
||||
area=(643, 599, 773, 641),
|
||||
search=(623, 579, 793, 661),
|
||||
color=(208, 223, 243),
|
||||
button=(882, 244, 1176, 476),
|
||||
),
|
||||
)
|
||||
DAILY_NEWS = ButtonWrapper(
|
||||
name='DAILY_NEWS',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/popup/DAILY_NEWS.png',
|
||||
area=(120, 89, 326, 113),
|
||||
search=(100, 69, 346, 133),
|
||||
color=(150, 204, 253),
|
||||
button=(1128, 89, 1156, 117),
|
||||
),
|
||||
)
|
||||
DAILY_REWARD = ButtonWrapper(
|
||||
name='DAILY_REWARD',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/popup/DAILY_REWARD.png',
|
||||
area=(854, 117, 1008, 165),
|
||||
search=(834, 97, 1028, 185),
|
||||
color=(178, 167, 112),
|
||||
button=(920, 632, 1140, 712),
|
||||
),
|
||||
)
|
||||
GET_NEW_STUDENT = ButtonWrapper(
|
||||
name='GET_NEW_STUDENT',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/popup/GET_NEW_STUDENT.png',
|
||||
area=(32, 93, 158, 114),
|
||||
search=(12, 73, 178, 134),
|
||||
color=(125, 132, 92),
|
||||
button=(934, 643, 1263, 714),
|
||||
),
|
||||
)
|
||||
GET_REWARD = ButtonWrapper(
|
||||
name='GET_REWARD',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/popup/GET_REWARD.png',
|
||||
area=(657, 145, 693, 170),
|
||||
search=(637, 125, 713, 190),
|
||||
color=(230, 222, 93),
|
||||
button=(675, 623, 870, 695),
|
||||
),
|
||||
)
|
||||
GET_REWARD_SKIP = ButtonWrapper(
|
||||
name='GET_REWARD_SKIP',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/popup/GET_REWARD_SKIP.png',
|
||||
area=(1137, 34, 1243, 65),
|
||||
search=(1117, 14, 1263, 85),
|
||||
color=(197, 200, 205),
|
||||
button=(1137, 34, 1243, 65),
|
||||
),
|
||||
)
|
||||
NETWORK_RECONNECT = ButtonWrapper(
|
||||
name='NETWORK_RECONNECT',
|
||||
jp=Button(
|
||||
file='./assets/jp/base/popup/NETWORK_RECONNECT.png',
|
||||
area=(725, 488, 810, 516),
|
||||
search=(705, 468, 830, 536),
|
||||
color=(78, 138, 169),
|
||||
button=(663, 467, 870, 537),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
from tasks.base.popup import PopupHandler
|
||||
|
||||
|
||||
class MainPage(PopupHandler):
|
||||
pass
|
||||
@@ -0,0 +1,162 @@
|
||||
import traceback
|
||||
|
||||
from tasks.base.assets.assets_base_page import *
|
||||
|
||||
|
||||
class Page:
|
||||
# Key: str, page name like "page_main"
|
||||
# Value: Page, page instance
|
||||
all_pages = {}
|
||||
|
||||
@classmethod
|
||||
def clear_connection(cls):
|
||||
for page in cls.all_pages.values():
|
||||
page.parent = None
|
||||
|
||||
@classmethod
|
||||
def init_connection(cls, destination):
|
||||
"""
|
||||
Initialize an A* path finding among pages.
|
||||
|
||||
Args:
|
||||
destination (Page):
|
||||
"""
|
||||
cls.clear_connection()
|
||||
|
||||
visited = [destination]
|
||||
visited = set(visited)
|
||||
while 1:
|
||||
new = visited.copy()
|
||||
for page in visited:
|
||||
for link in cls.iter_pages():
|
||||
if link in visited:
|
||||
continue
|
||||
if page in link.links:
|
||||
link.parent = page
|
||||
new.add(link)
|
||||
if len(new) == len(visited):
|
||||
break
|
||||
visited = new
|
||||
|
||||
@classmethod
|
||||
def iter_pages(cls):
|
||||
return cls.all_pages.values()
|
||||
|
||||
@classmethod
|
||||
def iter_check_buttons(cls):
|
||||
for page in cls.all_pages.values():
|
||||
yield page.check_button
|
||||
|
||||
def __init__(self, check_button):
|
||||
self.check_button = check_button
|
||||
self.links = {}
|
||||
(filename, line_number, function_name, text) = traceback.extract_stack()[-2]
|
||||
self.name = text[:text.find('=')].strip()
|
||||
self.parent = None
|
||||
Page.all_pages[self.name] = self
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.name == other.name
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def link(self, button, destination):
|
||||
self.links[destination] = button
|
||||
|
||||
|
||||
# Main Page
|
||||
page_main = Page(MAIN_GO_TO_PURCHASE)
|
||||
|
||||
# Cafe
|
||||
page_cafe = Page(CAFE_CHECK)
|
||||
page_cafe.link(HOME, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_CAFE, destination=page_cafe)
|
||||
|
||||
# Schedule
|
||||
page_schedule = Page(SCHEDULE_CHECK)
|
||||
page_schedule.link(HOME, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_SCHEDULE, destination=page_schedule)
|
||||
|
||||
# Circle
|
||||
page_circle = Page(CIRCLE_CHECK)
|
||||
page_circle.link(HOME, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_CIRCLE, destination=page_circle)
|
||||
|
||||
# Crafting Chamber
|
||||
page_crafting = Page(CRAFTING_CHECK)
|
||||
page_crafting.link(HOME, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_CRAFTING, destination=page_crafting)
|
||||
|
||||
# Shop
|
||||
page_shop = Page(SHOP_CHECK)
|
||||
page_shop.link(HOME, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_SHOP, destination=page_shop)
|
||||
|
||||
# Gacha
|
||||
page_gacha = Page(GACHA_CHECK)
|
||||
page_gacha.link(HOME, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_GACHA, destination=page_gacha)
|
||||
|
||||
# Account Info
|
||||
page_account_info = Page(ACCOUNT_INFO_CHECK)
|
||||
page_account_info.link(HOME, destination=page_main)
|
||||
|
||||
# Mail
|
||||
page_mail = Page(MAIL_CHECK)
|
||||
page_mail.link(HOME, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_MAIL, destination=page_mail)
|
||||
|
||||
# Task (Daily)
|
||||
page_task = Page(TASK_CHECK)
|
||||
page_task.link(HOME, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_TASK, destination=page_task)
|
||||
|
||||
# MomoTalk
|
||||
page_momo_talk = Page(MOMOTALK_CHECK)
|
||||
page_momo_talk.link(MOMOTALK_GO_TO_MAIN, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_MOMOTALK, destination=page_momo_talk)
|
||||
|
||||
# Work
|
||||
page_work = Page(WORK_CHECK)
|
||||
page_work.link(HOME, destination=page_main)
|
||||
page_main.link(MAIN_GO_TO_WORK, destination=page_work)
|
||||
|
||||
# Mission
|
||||
page_mission = Page(MISSION_CHECK)
|
||||
page_mission.link(HOME, destination=page_main)
|
||||
page_mission.link(BACK, destination=page_work)
|
||||
page_work.link(WORK_GO_TO_MISSION, destination=page_mission)
|
||||
|
||||
# Story
|
||||
page_story = Page(STORY_CHECK)
|
||||
page_story.link(HOME, destination=page_main)
|
||||
page_story.link(BACK, destination=page_work)
|
||||
page_work.link(WORK_GO_TO_STORY, destination=page_story)
|
||||
|
||||
# Bounty
|
||||
page_bounty = Page(BOUNTY_CHECK)
|
||||
page_bounty.link(HOME, destination=page_main)
|
||||
page_bounty.link(BACK, destination=page_work)
|
||||
page_work.link(WORK_GO_TO_BOUNTY, destination=page_bounty)
|
||||
|
||||
# Commissions
|
||||
page_commissions = Page(COMMISSIONS_CHECK)
|
||||
page_commissions.link(HOME, destination=page_main)
|
||||
page_commissions.link(BACK, destination=page_work)
|
||||
page_work.link(WORK_GO_TO_COMMISSIONS, destination=page_commissions)
|
||||
|
||||
# School Exchange
|
||||
page_school_exchange = Page(SCHOOL_EXCHANGE_CHECK)
|
||||
page_school_exchange.link(HOME, destination=page_main)
|
||||
page_school_exchange.link(BACK, destination=page_work)
|
||||
page_work.link(WORK_GO_TO_SCHOOL_EXCHANGE, destination=page_school_exchange)
|
||||
|
||||
# Tactical Challenge
|
||||
page_tactical_challenge = Page(TACTICAL_CHALLENGE_CHECK)
|
||||
page_tactical_challenge.link(HOME, destination=page_main)
|
||||
page_tactical_challenge.link(BACK, destination=page_work)
|
||||
page_work.link(WORK_GO_TO_TACTICAL_CHALLENGE, destination=page_tactical_challenge)
|
||||
@@ -0,0 +1,91 @@
|
||||
from module.base.base import ModuleBase
|
||||
from module.base.timer import Timer
|
||||
from tasks.base.assets.assets_base_popup import *
|
||||
from tasks.base.assets.assets_base_page import LOADING_CHECK
|
||||
|
||||
|
||||
class PopupHandler(ModuleBase):
|
||||
def handle_loading(self, interval=5) -> bool:
|
||||
"""
|
||||
Args:
|
||||
interval:
|
||||
|
||||
Returns:
|
||||
If handled.
|
||||
"""
|
||||
if self.appear(LOADING_CHECK, interval=interval):
|
||||
timer = Timer(0.5).start()
|
||||
while 1:
|
||||
if timer.reached_and_reset():
|
||||
self.device.screenshot()
|
||||
if self.appear(LOADING_CHECK):
|
||||
self.device.stuck_record_clear()
|
||||
continue
|
||||
else:
|
||||
break
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def handle_reward(self, interval=5) -> bool:
|
||||
"""
|
||||
Args:
|
||||
interval:
|
||||
|
||||
Returns:
|
||||
If handled.
|
||||
"""
|
||||
if self.appear_then_click(GET_REWARD, interval=interval):
|
||||
timer = Timer(0.2).start()
|
||||
while 1:
|
||||
if timer.reached_and_reset():
|
||||
self.device.screenshot()
|
||||
if self.appear(GET_REWARD):
|
||||
self.device.click(GET_REWARD)
|
||||
else:
|
||||
break
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def handle_reward_skip(self, interval=5) -> bool:
|
||||
if self.appear_then_click(GET_REWARD_SKIP, interval=interval):
|
||||
return True
|
||||
|
||||
def handle_daily_news(self, interval=5) -> bool:
|
||||
if self.appear_then_click(DAILY_NEWS, interval=interval):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def handle_daily_reward(self, interval=5) -> bool:
|
||||
if self.appear_then_click(DAILY_REWARD, interval=interval):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def handle_network_reconnect(self, interval=5) -> bool:
|
||||
if self.appear_then_click(NETWORK_RECONNECT, interval=interval):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def handle_affection_level_up(self, interval=5) -> bool:
|
||||
if self.appear_then_click(AFFECTION_LEVEL_UP, interval=interval):
|
||||
timer = Timer(0.2).start()
|
||||
while 1:
|
||||
if timer.reached_and_reset():
|
||||
self.device.screenshot()
|
||||
if self.appear(AFFECTION_LEVEL_UP):
|
||||
self.device.click(AFFECTION_LEVEL_UP)
|
||||
else:
|
||||
break
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def handle_new_student(self, interval=5) -> bool:
|
||||
if self.appear_then_click(GET_NEW_STUDENT, interval=interval):
|
||||
return True
|
||||
|
||||
return False
|
||||
@@ -0,0 +1,393 @@
|
||||
from module.base.button import ButtonWrapper
|
||||
from module.base.decorator import run_once
|
||||
from module.base.timer import Timer
|
||||
from module.exception import GameNotRunningError, GamePageUnknownError
|
||||
from module.logger import logger
|
||||
from module.ocr.ocr import Ocr
|
||||
from tasks.base.main_page import MainPage
|
||||
from tasks.base.page import Page, page_main
|
||||
from tasks.base.assets.assets_base_page import BACK
|
||||
|
||||
|
||||
class UI(MainPage):
|
||||
ui_current: Page
|
||||
ui_main_confirm_timer = Timer(0.2, count=2)
|
||||
|
||||
def ui_page_appear(self, page):
|
||||
"""
|
||||
Args:
|
||||
page (Page):
|
||||
"""
|
||||
return self.appear(page.check_button)
|
||||
|
||||
def ui_get_current_page(self, skip_first_screenshot=True):
|
||||
"""
|
||||
Args:
|
||||
skip_first_screenshot:
|
||||
|
||||
Returns:
|
||||
Page:
|
||||
|
||||
Raises:
|
||||
GameNotRunningError:
|
||||
GamePageUnknownError:
|
||||
"""
|
||||
logger.info("UI get current page")
|
||||
|
||||
@run_once
|
||||
def app_check():
|
||||
if not self.device.app_is_running():
|
||||
raise GameNotRunningError("Game not running")
|
||||
|
||||
@run_once
|
||||
def minicap_check():
|
||||
if self.config.Emulator_ControlMethod == "uiautomator2":
|
||||
self.device.uninstall_minicap()
|
||||
|
||||
@run_once
|
||||
def rotation_check():
|
||||
self.device.get_orientation()
|
||||
|
||||
timeout = Timer(10, count=20).start()
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
if not hasattr(self.device, "image") or self.device.image is None:
|
||||
self.device.screenshot()
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
# End
|
||||
if timeout.reached():
|
||||
break
|
||||
|
||||
# Known pages
|
||||
for page in Page.iter_pages():
|
||||
if page.check_button is None:
|
||||
continue
|
||||
if self.ui_page_appear(page=page):
|
||||
logger.attr("UI", page.name)
|
||||
self.ui_current = page
|
||||
return page
|
||||
|
||||
# Unknown page but able to handle
|
||||
logger.info("Unknown ui page")
|
||||
if self.ui_additional():
|
||||
logger.info("Additional ui page handled")
|
||||
timeout.reset()
|
||||
continue
|
||||
logger.info("May be in standby main page")
|
||||
self.device.click(BACK)
|
||||
|
||||
app_check()
|
||||
minicap_check()
|
||||
rotation_check()
|
||||
|
||||
# Unknown page, need manual switching
|
||||
logger.warning("Unknown ui page")
|
||||
logger.attr("EMULATOR__SCREENSHOT_METHOD", self.config.Emulator_ScreenshotMethod)
|
||||
logger.attr("EMULATOR__CONTROL_METHOD", self.config.Emulator_ControlMethod)
|
||||
logger.attr("Lang", self.config.LANG)
|
||||
logger.warning("Starting from current page is not supported")
|
||||
logger.warning(f"Supported page: {[str(page) for page in Page.iter_pages()]}")
|
||||
logger.warning('Supported page: Any page with a "HOME" button on the upper-right')
|
||||
logger.critical("Please switch to a supported page before starting AAS")
|
||||
raise GamePageUnknownError
|
||||
|
||||
def ui_goto(self, destination, skip_first_screenshot=True):
|
||||
"""
|
||||
Args:
|
||||
destination (Page):
|
||||
skip_first_screenshot:
|
||||
"""
|
||||
# Create connection
|
||||
Page.init_connection(destination)
|
||||
self.interval_clear(list(Page.iter_check_buttons()))
|
||||
|
||||
# loading_timer = Timer(0.5)
|
||||
|
||||
logger.hr(f"UI goto {destination}")
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
# # Loading
|
||||
# if self.appear(LOADING_CHECK):
|
||||
# loading_timer.reset()
|
||||
# continue
|
||||
#
|
||||
# if not loading_timer.reached():
|
||||
# continue
|
||||
|
||||
# Destination page
|
||||
if self.ui_page_appear(destination):
|
||||
logger.info(f'Page arrive: {destination}')
|
||||
if self.ui_page_confirm(destination):
|
||||
logger.info(f'Page arrive confirm {destination}')
|
||||
break
|
||||
|
||||
# Other pages
|
||||
clicked = False
|
||||
for page in Page.iter_pages():
|
||||
if page.parent is None or page.check_button is None:
|
||||
continue
|
||||
if self.appear(page.check_button, interval=5):
|
||||
logger.info(f'Page switch: {page} -> {page.parent}')
|
||||
# self.handle_lang_check(page)
|
||||
if self.ui_page_confirm(page):
|
||||
logger.info(f'Page arrive confirm {page}')
|
||||
button = page.links[page.parent]
|
||||
self.device.click(button)
|
||||
self.ui_button_interval_reset(button)
|
||||
clicked = True
|
||||
break
|
||||
if clicked:
|
||||
continue
|
||||
|
||||
# Additional
|
||||
if self.ui_additional():
|
||||
continue
|
||||
|
||||
# Reset connection
|
||||
Page.clear_connection()
|
||||
|
||||
def ui_ensure(self, destination, acquire_lang_checked=True, skip_first_screenshot=True):
|
||||
"""
|
||||
Args:
|
||||
destination (Page):
|
||||
acquire_lang_checked:
|
||||
skip_first_screenshot:
|
||||
|
||||
Returns:
|
||||
bool: If UI switched.
|
||||
"""
|
||||
logger.hr("UI ensure")
|
||||
self.ui_get_current_page(skip_first_screenshot=skip_first_screenshot)
|
||||
|
||||
# self.ui_leave_special()
|
||||
|
||||
# if acquire_lang_checked:
|
||||
# if self.acquire_lang_checked():
|
||||
# self.ui_get_current_page(skip_first_screenshot=skip_first_screenshot)
|
||||
|
||||
if self.ui_current == destination:
|
||||
logger.info("Already at %s" % destination)
|
||||
return False
|
||||
else:
|
||||
logger.info("Goto %s" % destination)
|
||||
self.ui_goto(destination, skip_first_screenshot=True)
|
||||
return True
|
||||
|
||||
def ui_ensure_index(
|
||||
self,
|
||||
index,
|
||||
letter,
|
||||
next_button,
|
||||
prev_button,
|
||||
skip_first_screenshot=False,
|
||||
fast=True,
|
||||
interval=(0.2, 0.3),
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
index (int):
|
||||
letter (Ocr, callable): OCR button.
|
||||
next_button (Button):
|
||||
prev_button (Button):
|
||||
skip_first_screenshot (bool):
|
||||
fast (bool): Default true. False when index is not continuous.
|
||||
interval (tuple, int, float): Seconds between two click.
|
||||
"""
|
||||
logger.hr("UI ensure index")
|
||||
retry = Timer(1, count=2)
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
if isinstance(letter, Ocr):
|
||||
current = letter.ocr_single_line(self.device.image)
|
||||
else:
|
||||
current = letter(self.device.image)
|
||||
|
||||
logger.attr("Index", current)
|
||||
diff = index - current
|
||||
if diff == 0:
|
||||
break
|
||||
if current == 0:
|
||||
logger.warning(f'ui_ensure_index got an empty current value: {current}')
|
||||
continue
|
||||
|
||||
if retry.reached():
|
||||
button = next_button if diff > 0 else prev_button
|
||||
if fast:
|
||||
self.device.multi_click(button, n=abs(diff), interval=interval)
|
||||
else:
|
||||
self.device.click(button)
|
||||
retry.reset()
|
||||
|
||||
def ui_click(
|
||||
self,
|
||||
click_button,
|
||||
check_button,
|
||||
appear_button=None,
|
||||
additional=None,
|
||||
retry_wait=5,
|
||||
skip_first_screenshot=True,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
click_button (ButtonWrapper):
|
||||
check_button (ButtonWrapper, callable, list[ButtonWrapper], tuple[ButtonWrapper]):
|
||||
appear_button (ButtonWrapper, callable, list[ButtonWrapper], tuple[ButtonWrapper]):
|
||||
additional (callable):
|
||||
retry_wait (int, float):
|
||||
skip_first_screenshot (bool):
|
||||
"""
|
||||
if appear_button is None:
|
||||
appear_button = click_button
|
||||
logger.info(f'UI click: {appear_button} -> {check_button}')
|
||||
|
||||
def process_appear(button):
|
||||
if isinstance(button, ButtonWrapper):
|
||||
return self.appear(button)
|
||||
elif callable(button):
|
||||
return button()
|
||||
elif isinstance(button, (list, tuple)):
|
||||
for b in button:
|
||||
if self.appear(b):
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
return self.appear(button)
|
||||
|
||||
click_timer = Timer(retry_wait, count=retry_wait // 0.5)
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
# End
|
||||
if process_appear(check_button):
|
||||
break
|
||||
|
||||
# Click
|
||||
if click_timer.reached():
|
||||
if process_appear(appear_button):
|
||||
self.device.click(click_button)
|
||||
click_timer.reset()
|
||||
continue
|
||||
if additional is not None:
|
||||
if additional():
|
||||
continue
|
||||
|
||||
def is_in_main(self):
|
||||
if self.appear(page_main.check_button):
|
||||
if self.image_color_count(page_main.check_button, color=(235, 235, 235), threshold=221, count=400):
|
||||
return True
|
||||
# if self.appear(MAP_EXIT):
|
||||
# if self.image_color_count(MAP_EXIT, color=(235, 235, 235), threshold=221, count=50):
|
||||
# return True
|
||||
return False
|
||||
|
||||
def ui_goto_main(self):
|
||||
return self.ui_ensure(destination=page_main)
|
||||
|
||||
# def ui_additional(self) -> bool:
|
||||
# """
|
||||
# Handle all possible popups during UI switching.
|
||||
#
|
||||
# Returns:
|
||||
# If handled any popup.
|
||||
# """
|
||||
# if self.handle_reward():
|
||||
# return True
|
||||
# if self.handle_battle_pass_notification():
|
||||
# return True
|
||||
# if self.handle_monthly_card_reward():
|
||||
# return True
|
||||
# if self.appear(COMBAT_PREPARE, interval=5):
|
||||
# logger.info(f'UI additional: {COMBAT_PREPARE} -> {CLOSE}')
|
||||
# self.device.click(CLOSE)
|
||||
# if self.appear_then_click(COMBAT_EXIT, interval=5):
|
||||
# return True
|
||||
# if self.appear_then_click(INFO_CLOSE, interval=5):
|
||||
# return True
|
||||
#
|
||||
# return False
|
||||
|
||||
def ui_additional(self) -> bool:
|
||||
"""
|
||||
Handle all possible popups during UI switching.
|
||||
|
||||
Returns:
|
||||
If handled any popup.
|
||||
"""
|
||||
if self.handle_loading():
|
||||
return True
|
||||
if self.handle_reward_skip():
|
||||
return True
|
||||
if self.handle_reward():
|
||||
return True
|
||||
if self.handle_daily_news():
|
||||
return True
|
||||
if self.handle_network_reconnect():
|
||||
return True
|
||||
if self.handle_affection_level_up():
|
||||
return True
|
||||
if self.handle_new_student():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _ui_button_confirm(
|
||||
self,
|
||||
button,
|
||||
confirm=Timer(0.1, count=0),
|
||||
timeout=Timer(2, count=6),
|
||||
skip_first_screenshot=True
|
||||
):
|
||||
confirm.reset()
|
||||
timeout.reset()
|
||||
while 1:
|
||||
if skip_first_screenshot:
|
||||
skip_first_screenshot = False
|
||||
else:
|
||||
self.device.screenshot()
|
||||
|
||||
if timeout.reached():
|
||||
logger.warning(f'_ui_button_confirm({button}) timeout')
|
||||
break
|
||||
|
||||
if self.appear(button):
|
||||
if confirm.reached():
|
||||
break
|
||||
else:
|
||||
confirm.reset()
|
||||
|
||||
def ui_page_confirm(self, page):
|
||||
"""
|
||||
Args:
|
||||
page (Page):
|
||||
|
||||
Returns:
|
||||
bool: If handled
|
||||
"""
|
||||
if page == page_main:
|
||||
self._ui_button_confirm(page.check_button)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def ui_button_interval_reset(self, button):
|
||||
"""
|
||||
Reset interval of some button to avoid mistaken clicks
|
||||
|
||||
Args:
|
||||
button (Button):
|
||||
"""
|
||||
pass
|
||||
@@ -0,0 +1,85 @@
|
||||
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 ```
|
||||
|
||||
BOX_CAFE = ButtonWrapper(
|
||||
name='BOX_CAFE',
|
||||
jp=Button(
|
||||
file='./assets/jp/cafe/BOX_CAFE.png',
|
||||
area=(33, 130, 1247, 569),
|
||||
search=(13, 110, 1267, 589),
|
||||
color=(175, 181, 186),
|
||||
button=(33, 130, 1247, 569),
|
||||
),
|
||||
)
|
||||
CHECK_REWARD = ButtonWrapper(
|
||||
name='CHECK_REWARD',
|
||||
jp=Button(
|
||||
file='./assets/jp/cafe/CHECK_REWARD.png',
|
||||
area=(1095, 621, 1146, 637),
|
||||
search=(1075, 601, 1166, 657),
|
||||
color=(82, 105, 129),
|
||||
button=(1086, 607, 1225, 685),
|
||||
),
|
||||
)
|
||||
CLICKABLE_TEMPLATE = ButtonWrapper(
|
||||
name='CLICKABLE_TEMPLATE',
|
||||
jp=Button(
|
||||
file='./assets/jp/cafe/CLICKABLE_TEMPLATE.png',
|
||||
area=(0, 0, 42, 56),
|
||||
search=(0, 0, 62, 76),
|
||||
color=(77, 63, 1),
|
||||
button=(0, 0, 42, 56),
|
||||
),
|
||||
)
|
||||
GET_REWARD = ButtonWrapper(
|
||||
name='GET_REWARD',
|
||||
jp=Button(
|
||||
file='./assets/jp/cafe/GET_REWARD.png',
|
||||
area=(611, 514, 669, 543),
|
||||
search=(591, 494, 689, 563),
|
||||
color=(159, 133, 48),
|
||||
button=(546, 494, 735, 563),
|
||||
),
|
||||
)
|
||||
GET_REWARD_CLOSE = ButtonWrapper(
|
||||
name='GET_REWARD_CLOSE',
|
||||
jp=Button(
|
||||
file='./assets/jp/cafe/GET_REWARD_CLOSE.png',
|
||||
area=(891, 144, 917, 170),
|
||||
search=(871, 124, 937, 190),
|
||||
color=(173, 179, 189),
|
||||
button=(891, 144, 917, 170),
|
||||
),
|
||||
)
|
||||
GOT_REWARD = ButtonWrapper(
|
||||
name='GOT_REWARD',
|
||||
jp=Button(
|
||||
file='./assets/jp/cafe/GOT_REWARD.png',
|
||||
area=(609, 507, 672, 535),
|
||||
search=(589, 487, 692, 555),
|
||||
color=(174, 175, 174),
|
||||
button=(609, 507, 672, 535),
|
||||
),
|
||||
)
|
||||
OCR_CAFE = ButtonWrapper(
|
||||
name='OCR_CAFE',
|
||||
jp=Button(
|
||||
file='./assets/jp/cafe/OCR_CAFE.png',
|
||||
area=(1103, 642, 1202, 672),
|
||||
search=(1083, 622, 1222, 692),
|
||||
color=(87, 107, 129),
|
||||
button=(1103, 642, 1202, 672),
|
||||
),
|
||||
)
|
||||
STUDENT_LIST = ButtonWrapper(
|
||||
name='STUDENT_LIST',
|
||||
jp=Button(
|
||||
file='./assets/jp/cafe/STUDENT_LIST.png',
|
||||
area=(612, 172, 667, 201),
|
||||
search=(592, 152, 687, 221),
|
||||
color=(145, 157, 172),
|
||||
button=(545, 423, 738, 495),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,227 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
from enum import Enum
|
||||
|
||||
from module.logger import logger
|
||||
from module.base.timer import Timer
|
||||
from module.base.button import ClickButton
|
||||
from module.base.utils.utils import area_offset
|
||||
from module.ocr.ocr import Digit
|
||||
from tasks.base.page import page_cafe
|
||||
from tasks.base.ui import UI
|
||||
from tasks.cafe.assets.assets_cafe import *
|
||||
|
||||
|
||||
class CafeStatus(Enum):
|
||||
STUDENT_LIST = 0
|
||||
OCR = 1
|
||||
REWARD = 2
|
||||
GOT = 3
|
||||
CLICK = 4
|
||||
CHECK = 5
|
||||
FINISHED = -1
|
||||
|
||||
|
||||
class Cafe(UI):
|
||||
@staticmethod
|
||||
def merge_points(points, threshold=3):
|
||||
if len(points) <= 1:
|
||||
return points
|
||||
result = []
|
||||
for point in points:
|
||||
if not result:
|
||||
result.append(point)
|
||||
continue
|
||||
if point[0] - result[-1][0] < threshold and point[1] - result[-1][1] < threshold:
|
||||
result[-1] = ((point[0] + result[-1][0]) // 2, (point[1] + result[-1][1]) // 2)
|
||||
continue
|
||||
result.append(point)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _extract_clickable_from_image(image):
|
||||
# convert to hsv for better color matching
|
||||
image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
|
||||
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
|
||||
# set color range
|
||||
lower_hsv = np.array([17, 200, 220])
|
||||
upper_hsv = np.array([28, 255, 255])
|
||||
# get mask
|
||||
mask = cv2.inRange(hsv, lower_hsv, upper_hsv)
|
||||
# generate result
|
||||
return cv2.bitwise_and(image, image, mask=mask)
|
||||
|
||||
def _match_clickable_points(self, image, threshold=0.9):
|
||||
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||
template = self.btn.matched_button.image
|
||||
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
|
||||
|
||||
res = cv2.matchTemplate(image, template, cv2.TM_CCOEFF_NORMED)
|
||||
loc = np.where(res >= threshold)
|
||||
return [point for point in zip(*loc[::-1])]
|
||||
|
||||
def _get_clickable_buttons(self, threshold=0.9, offset=(0, 0)):
|
||||
image = cv2.copyMakeBorder(self.device.image, 20, 20, 10, 60, cv2.BORDER_CONSTANT, value=(0, 0, 0))
|
||||
image = self._extract_clickable_from_image(image)
|
||||
points = self._match_clickable_points(image, threshold)
|
||||
points = self.merge_points(points)
|
||||
if not points:
|
||||
return []
|
||||
area = area_offset((0, 0, self.btn.width, self.btn.height), offset)
|
||||
return [
|
||||
ClickButton(
|
||||
button=area_offset(area, offset=point),
|
||||
name=self.btn.name
|
||||
)
|
||||
for point in points
|
||||
]
|
||||
|
||||
def _reset_cafe_position(self, direction: str):
|
||||
width = BOX_CAFE.width
|
||||
height = BOX_CAFE.height
|
||||
r = np.random.uniform(0.6, 0.8)
|
||||
vector_down = (width * r, height * r)
|
||||
vector_up = (width * r, -height * r)
|
||||
vector_left = (-width * r, 0)
|
||||
vector_right = (width * r, 0)
|
||||
random_r = (-5, -5, 5, 5)
|
||||
match direction:
|
||||
case 'init':
|
||||
self.device.pinch()
|
||||
self.device.swipe_vector(vector_down, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_up, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_up, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
case 'left':
|
||||
self.device.swipe_vector(vector_left, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_left, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
case 'right':
|
||||
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
self.device.swipe_vector(vector_right, box=BOX_CAFE.area, random_range=random_r, padding=5)
|
||||
|
||||
def _get_reward_num(self):
|
||||
ocr = Digit(OCR_CAFE)
|
||||
num = ocr.detect_and_ocr(self.device.image)
|
||||
if len(num) != 1:
|
||||
logger.warning(f'Invalid reward num: {num}')
|
||||
num = float(num[0].ocr_text.rstrip('%'))
|
||||
logger.attr('Reward', num)
|
||||
return num
|
||||
|
||||
def _handle_cafe(self, status):
|
||||
match status:
|
||||
case CafeStatus.STUDENT_LIST:
|
||||
self.appear_then_click(STUDENT_LIST)
|
||||
if not self.appear(STUDENT_LIST):
|
||||
return CafeStatus.OCR
|
||||
case CafeStatus.OCR:
|
||||
reward = self._get_reward_num()
|
||||
if reward == 0:
|
||||
return CafeStatus.GOT
|
||||
if self.appear_then_click(CHECK_REWARD):
|
||||
return CafeStatus.REWARD
|
||||
case CafeStatus.REWARD:
|
||||
if self.match_color(GOT_REWARD):
|
||||
self.device.click(GET_REWARD_CLOSE)
|
||||
return CafeStatus.GOT
|
||||
if not self.appear(GET_REWARD):
|
||||
return CafeStatus.OCR
|
||||
if self.match_color(GET_REWARD):
|
||||
self.device.click(GET_REWARD)
|
||||
case CafeStatus.GOT:
|
||||
logger.info('Cafe reward have been got')
|
||||
self.appear_then_click(GET_REWARD_CLOSE)
|
||||
return CafeStatus.CLICK
|
||||
case CafeStatus.CLICK:
|
||||
buttons = self._get_clickable_buttons(offset=(45, 10))
|
||||
self.click = len(buttons)
|
||||
logger.attr('Clickable', self.click)
|
||||
if not buttons:
|
||||
return CafeStatus.CHECK
|
||||
self.device.click(buttons[0])
|
||||
case CafeStatus.CHECK:
|
||||
buttons = self._get_clickable_buttons()
|
||||
if not self.is_adjust_on:
|
||||
if not buttons:
|
||||
return CafeStatus.FINISHED
|
||||
else:
|
||||
return CafeStatus.CLICK
|
||||
if not buttons:
|
||||
self.check += 1
|
||||
else:
|
||||
self.check = 0
|
||||
return CafeStatus.CLICK
|
||||
match self.check:
|
||||
case 1:
|
||||
self._reset_cafe_position('left')
|
||||
case 2:
|
||||
self._reset_cafe_position('right')
|
||||
case 3:
|
||||
self._reset_cafe_position('init')
|
||||
case 4:
|
||||
return CafeStatus.FINISHED
|
||||
case CafeStatus.FINISHED:
|
||||
return status
|
||||
case _:
|
||||
logger.warning(f'Invalid status: {status}')
|
||||
return status
|
||||
|
||||
def run(self):
|
||||
self.btn = CLICKABLE_TEMPLATE
|
||||
self.click = 0
|
||||
self.check = 0
|
||||
|
||||
is_reward_on = self.config.Cafe_Reward
|
||||
is_touch_on = self.config.Cafe_Touch
|
||||
self.is_adjust_on = self.config.Cafe_AutoAdjust
|
||||
|
||||
self.ui_ensure(page_cafe)
|
||||
|
||||
status = CafeStatus.STUDENT_LIST
|
||||
loading_timer = Timer(2).start()
|
||||
action_timer = Timer(1.5, count=1) # cant be too fast
|
||||
check_timer = Timer(1, count=1)
|
||||
is_list = False
|
||||
is_reset = False
|
||||
is_enable = is_reward_on or is_touch_on
|
||||
|
||||
while is_enable:
|
||||
self.device.screenshot()
|
||||
|
||||
if self.ui_additional():
|
||||
continue
|
||||
|
||||
if not loading_timer.reached():
|
||||
continue
|
||||
|
||||
if not is_list and status == CafeStatus.STUDENT_LIST and self.appear(STUDENT_LIST):
|
||||
is_list = True
|
||||
loading_timer = Timer(5).start()
|
||||
continue
|
||||
|
||||
if not is_reward_on and status == CafeStatus.OCR:
|
||||
logger.info('Skip reward')
|
||||
status = CafeStatus.CLICK
|
||||
continue
|
||||
|
||||
if not is_touch_on and status == CafeStatus.CLICK:
|
||||
logger.info('Skip touch')
|
||||
status = CafeStatus.FINISHED
|
||||
continue
|
||||
|
||||
if is_touch_on and not is_reset and status == CafeStatus.CLICK:
|
||||
self._reset_cafe_position('init')
|
||||
is_reset = True
|
||||
continue
|
||||
|
||||
if status == CafeStatus.CHECK and not check_timer.reached_and_reset():
|
||||
continue
|
||||
|
||||
if action_timer.reached_and_reset():
|
||||
status = self._handle_cafe(status)
|
||||
logger.attr('Status', status)
|
||||
|
||||
if status is CafeStatus.FINISHED:
|
||||
break
|
||||
|
||||
self.config.task_delay(server_update=True, minute=180)
|
||||
@@ -0,0 +1,15 @@
|
||||
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 ```
|
||||
|
||||
GET_REWARD_AP = ButtonWrapper(
|
||||
name='GET_REWARD_AP',
|
||||
jp=Button(
|
||||
file='./assets/jp/circle/GET_REWARD_AP.png',
|
||||
area=(540, 148, 623, 175),
|
||||
search=(520, 128, 643, 195),
|
||||
color=(198, 206, 213),
|
||||
button=(543, 457, 735, 524),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,56 @@
|
||||
from enum import Enum
|
||||
|
||||
from module.base.timer import Timer
|
||||
from module.logger import logger
|
||||
from tasks.base.page import page_circle
|
||||
from tasks.base.ui import UI
|
||||
from tasks.circle.assets.assets_circle import *
|
||||
|
||||
|
||||
class CircleStatus(Enum):
|
||||
"""
|
||||
Circle status
|
||||
"""
|
||||
REWARD = 0
|
||||
GOT = 1
|
||||
FINISHED = -1
|
||||
|
||||
|
||||
class Circle(UI):
|
||||
def _handle_circle(self, status):
|
||||
match status:
|
||||
case CircleStatus.REWARD:
|
||||
if self.appear_then_click(GET_REWARD_AP):
|
||||
logger.info("Get circle AP reward")
|
||||
return CircleStatus.FINISHED
|
||||
case CircleStatus.GOT:
|
||||
logger.info("Circle AP reward have been got")
|
||||
return CircleStatus.FINISHED
|
||||
case _:
|
||||
logger.warning(f"Invalid status: {status}")
|
||||
return status
|
||||
|
||||
def run(self):
|
||||
self.ui_ensure(page_circle)
|
||||
|
||||
status = CircleStatus.REWARD
|
||||
action_timer = Timer(0.5)
|
||||
ap_timer = Timer(2).start()
|
||||
|
||||
while 1:
|
||||
self.device.screenshot()
|
||||
|
||||
if self.ui_additional():
|
||||
continue
|
||||
|
||||
if ap_timer.reached() and status == CircleStatus.REWARD:
|
||||
status = CircleStatus.GOT
|
||||
|
||||
if action_timer.reached_and_reset():
|
||||
status = self._handle_circle(status)
|
||||
logger.attr('Status', status)
|
||||
|
||||
if status is CircleStatus.FINISHED:
|
||||
break
|
||||
|
||||
self.config.task_delay(server_update=True)
|
||||
@@ -0,0 +1,25 @@
|
||||
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_AP = ButtonWrapper(
|
||||
name='OCR_AP',
|
||||
share=Button(
|
||||
file='./assets/share/item/data/OCR_AP.png',
|
||||
area=(560, 11, 667, 37),
|
||||
search=(540, 0, 687, 57),
|
||||
color=(211, 216, 219),
|
||||
button=(560, 11, 667, 37),
|
||||
),
|
||||
)
|
||||
OCR_DATA = ButtonWrapper(
|
||||
name='OCR_DATA',
|
||||
share=Button(
|
||||
file='./assets/share/item/data/OCR_DATA.png',
|
||||
area=(768, 12, 1072, 37),
|
||||
search=(748, 0, 1092, 57),
|
||||
color=(212, 220, 224),
|
||||
button=(768, 12, 1072, 37),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
from module.base.timer import Timer
|
||||
from module.logger import logger
|
||||
from module.ocr.ocr import Digit, DigitCounter
|
||||
from tasks.base.page import page_work
|
||||
from tasks.base.ui import UI
|
||||
from tasks.item.assets.assets_item_data import *
|
||||
|
||||
|
||||
class DataUpdate(UI):
|
||||
def _get_data(self):
|
||||
"""
|
||||
Page:
|
||||
in: page_work
|
||||
"""
|
||||
ap = DigitCounter(OCR_AP).ocr_single_line(self.device.image)
|
||||
# Data for Credit and Pyroxene
|
||||
ocr = Digit(OCR_DATA)
|
||||
timeout = Timer(2, count=6).start()
|
||||
while 1:
|
||||
data = ocr.detect_and_ocr(self.device.image)
|
||||
if len(data) != 2:
|
||||
data = [data[0], data[-1]]
|
||||
logger.attr('Data', data)
|
||||
credit, pyroxene = [int(''.join([v for v in d.ocr_text if v.isdigit()])) for d in data]
|
||||
if credit > 0 or pyroxene > 0:
|
||||
break
|
||||
|
||||
logger.warning(f'Invalid credit and pyroxene: {data}')
|
||||
if timeout.reached():
|
||||
logger.warning('Get data timeout')
|
||||
break
|
||||
|
||||
logger.attr('Credit', credit)
|
||||
logger.attr('Pyroxene', pyroxene)
|
||||
with self.config.multi_set():
|
||||
self.config.stored.AP.set(ap[0], ap[2])
|
||||
self.config.stored.Credit.value = credit
|
||||
self.config.stored.Pyroxene.value = pyroxene
|
||||
|
||||
return ap, credit, pyroxene
|
||||
|
||||
def run(self):
|
||||
self.ui_ensure(page_work, acquire_lang_checked=False)
|
||||
|
||||
with self.config.multi_set():
|
||||
self._get_data()
|
||||
self.config.task_delay(server_update=True)
|
||||
@@ -0,0 +1,25 @@
|
||||
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 ```
|
||||
|
||||
LOGIN_CONFIRM = ButtonWrapper(
|
||||
name='LOGIN_CONFIRM',
|
||||
jp=Button(
|
||||
file='./assets/jp/login/LOGIN_CONFIRM.png',
|
||||
area=(36, 621, 98, 654),
|
||||
search=(16, 601, 118, 674),
|
||||
color=(211, 215, 220),
|
||||
button=(990, 354, 1232, 506),
|
||||
),
|
||||
)
|
||||
LOGIN_LOADING = ButtonWrapper(
|
||||
name='LOGIN_LOADING',
|
||||
jp=Button(
|
||||
file='./assets/jp/login/LOGIN_LOADING.png',
|
||||
area=(34, 682, 60, 707),
|
||||
search=(14, 662, 80, 720),
|
||||
color=(8, 66, 96),
|
||||
button=(34, 682, 60, 707),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,109 @@
|
||||
from module.base.timer import Timer
|
||||
from module.exception import GameNotRunningError
|
||||
from module.logger import logger
|
||||
from tasks.base.page import page_main
|
||||
from tasks.base.ui import UI
|
||||
# from tasks.login.assets.assets_login import LOGIN_CONFIRM, USER_AGREEMENT_ACCEPT, LOGIN_LOADING
|
||||
from tasks.login.assets.assets_login import LOGIN_CONFIRM, LOGIN_LOADING
|
||||
|
||||
|
||||
class Login(UI):
|
||||
def _handle_app_login(self):
|
||||
"""
|
||||
Pages:
|
||||
in: Any page
|
||||
out: page_main
|
||||
|
||||
Raises:
|
||||
GameStuckError:
|
||||
GameTooManyClickError:
|
||||
GameNotRunningError:
|
||||
"""
|
||||
def _page_main_twice_confirm():
|
||||
if self.ui_page_appear(page_main):
|
||||
timer = Timer(1).start()
|
||||
while 1:
|
||||
if timer.reached():
|
||||
self.device.screenshot()
|
||||
if self.ui_page_appear(page_main):
|
||||
logger.info('Login to main confirm')
|
||||
return True
|
||||
return False
|
||||
|
||||
logger.hr('App login')
|
||||
orientation_timer = Timer(5)
|
||||
startup_timer = Timer(5).start()
|
||||
app_timer = Timer(5).start()
|
||||
login_success = False
|
||||
|
||||
while 1:
|
||||
# Watch if game alive
|
||||
if app_timer.reached():
|
||||
if not self.device.app_is_running():
|
||||
logger.error('Game died during launch')
|
||||
raise GameNotRunningError('Game not running')
|
||||
app_timer.reset()
|
||||
# Watch device rotation
|
||||
if not login_success and orientation_timer.reached():
|
||||
# Screen may rotate after starting an app
|
||||
self.device.get_orientation()
|
||||
orientation_timer.reset()
|
||||
|
||||
self.device.screenshot()
|
||||
|
||||
# End
|
||||
# Game client requires at least 5s to start
|
||||
# The first few frames might be captured before app_stop(), ignore them
|
||||
if startup_timer.reached():
|
||||
if self.handle_daily_reward() or self.handle_daily_news():
|
||||
startup_timer.reset() # reuse startup timer as daily reward timer
|
||||
continue
|
||||
if _page_main_twice_confirm():
|
||||
break
|
||||
|
||||
# Watch resource downloading and loading
|
||||
if self.match_color(LOGIN_LOADING, interval=5, threshold=45):
|
||||
logger.info('Game resources downloading or loading')
|
||||
self.device.stuck_record_clear()
|
||||
|
||||
# Login
|
||||
if self.appear_then_click(LOGIN_CONFIRM):
|
||||
login_success = True
|
||||
continue
|
||||
# if self.appear_then_click(USER_AGREEMENT_ACCEPT):
|
||||
# continue
|
||||
# Additional
|
||||
# if self.handle_popup_single():
|
||||
# continue
|
||||
# if self.handle_popup_confirm():
|
||||
# continue
|
||||
if self.ui_additional():
|
||||
continue
|
||||
|
||||
return True
|
||||
|
||||
def handle_app_login(self):
|
||||
logger.info('handle_app_login')
|
||||
self.device.screenshot_interval_set(1.0)
|
||||
self.device.stuck_timer = Timer(300, count=300).start()
|
||||
try:
|
||||
self._handle_app_login()
|
||||
finally:
|
||||
self.device.screenshot_interval_set()
|
||||
self.device.stuck_timer = Timer(60, count=60).start()
|
||||
|
||||
def app_stop(self):
|
||||
logger.hr('App stop')
|
||||
self.device.app_stop()
|
||||
|
||||
def app_start(self):
|
||||
logger.hr('App start')
|
||||
self.device.app_start()
|
||||
self.handle_app_login()
|
||||
|
||||
def app_restart(self):
|
||||
logger.hr('App restart')
|
||||
self.device.app_stop()
|
||||
self.device.app_start()
|
||||
self.handle_app_login()
|
||||
self.config.task_delay(server_update=True)
|
||||
@@ -0,0 +1,25 @@
|
||||
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 ```
|
||||
|
||||
MAIL_RECEIVE = ButtonWrapper(
|
||||
name='MAIL_RECEIVE',
|
||||
jp=Button(
|
||||
file='./assets/jp/mail/MAIL_RECEIVE.png',
|
||||
area=(1086, 659, 1183, 682),
|
||||
search=(1066, 639, 1203, 702),
|
||||
color=(174, 151, 53),
|
||||
button=(1043, 640, 1226, 701),
|
||||
),
|
||||
)
|
||||
MAIL_RECEIVED = ButtonWrapper(
|
||||
name='MAIL_RECEIVED',
|
||||
jp=Button(
|
||||
file='./assets/jp/mail/MAIL_RECEIVED.png',
|
||||
area=(1085, 657, 1186, 685),
|
||||
search=(1065, 637, 1206, 705),
|
||||
color=(195, 196, 196),
|
||||
button=(1085, 657, 1186, 685),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
from module.base.timer import Timer
|
||||
from module.logger import logger
|
||||
from tasks.base.page import page_mail
|
||||
from tasks.base.ui import UI
|
||||
from tasks.mail.assets.assets_mail import *
|
||||
|
||||
|
||||
class Mail(UI):
|
||||
def run(self):
|
||||
self.ui_ensure(page_mail)
|
||||
action_timer = Timer(1).start()
|
||||
|
||||
while 1:
|
||||
self.device.screenshot()
|
||||
if self.ui_additional():
|
||||
continue
|
||||
if action_timer.reached_and_reset():
|
||||
if self.match_color(MAIL_RECEIVE):
|
||||
self.device.click(MAIL_RECEIVE)
|
||||
logger.info("Receive mail")
|
||||
continue
|
||||
if self.appear(MAIL_RECEIVED):
|
||||
logger.info("Mail have been received")
|
||||
break
|
||||
|
||||
self.config.task_delay(server_update=True)
|
||||
@@ -0,0 +1,15 @@
|
||||
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 ```
|
||||
|
||||
SCROLL = ButtonWrapper(
|
||||
name='SCROLL',
|
||||
jp=Button(
|
||||
file='./assets/jp/schedule/SCROLL.png',
|
||||
area=(742, 136, 1101, 671),
|
||||
search=(722, 116, 1121, 691),
|
||||
color=(198, 214, 210),
|
||||
button=(742, 136, 1101, 671),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,145 @@
|
||||
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 ```
|
||||
|
||||
CHALLENGE_LOSE = ButtonWrapper(
|
||||
name='CHALLENGE_LOSE',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/CHALLENGE_LOSE.png',
|
||||
area=(583, 195, 698, 224),
|
||||
search=(563, 175, 718, 244),
|
||||
color=(146, 158, 173),
|
||||
button=(548, 431, 738, 499),
|
||||
),
|
||||
)
|
||||
CHALLENGE_WIN = ButtonWrapper(
|
||||
name='CHALLENGE_WIN',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/CHALLENGE_WIN.png',
|
||||
area=(583, 131, 698, 160),
|
||||
search=(563, 111, 718, 180),
|
||||
color=(151, 165, 180),
|
||||
button=(549, 495, 737, 563),
|
||||
),
|
||||
)
|
||||
GET_REWARD_CREDIT = ButtonWrapper(
|
||||
name='GET_REWARD_CREDIT',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/GET_REWARD_CREDIT.png',
|
||||
area=(308, 360, 397, 415),
|
||||
search=(288, 340, 417, 435),
|
||||
color=(215, 193, 61),
|
||||
button=(308, 360, 397, 415),
|
||||
),
|
||||
)
|
||||
GET_REWARD_DAILY = ButtonWrapper(
|
||||
name='GET_REWARD_DAILY',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/GET_REWARD_DAILY.png',
|
||||
area=(307, 440, 395, 493),
|
||||
search=(287, 420, 415, 513),
|
||||
color=(214, 192, 60),
|
||||
button=(307, 440, 395, 493),
|
||||
),
|
||||
)
|
||||
GOT_REWARD_CREDIT = ButtonWrapper(
|
||||
name='GOT_REWARD_CREDIT',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/GOT_REWARD_CREDIT.png',
|
||||
area=(308, 358, 398, 416),
|
||||
search=(288, 338, 418, 436),
|
||||
color=(203, 205, 204),
|
||||
button=(308, 358, 398, 416),
|
||||
),
|
||||
)
|
||||
GOT_REWARD_DAILY = ButtonWrapper(
|
||||
name='GOT_REWARD_DAILY',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/GOT_REWARD_DAILY.png',
|
||||
area=(307, 439, 397, 494),
|
||||
search=(287, 419, 417, 514),
|
||||
color=(203, 203, 204),
|
||||
button=(307, 439, 397, 494),
|
||||
),
|
||||
)
|
||||
OCR_TICKET = ButtonWrapper(
|
||||
name='OCR_TICKET',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/OCR_TICKET.png',
|
||||
area=(206, 479, 240, 499),
|
||||
search=(186, 459, 260, 519),
|
||||
color=(200, 202, 204),
|
||||
button=(206, 479, 240, 499),
|
||||
),
|
||||
)
|
||||
PLAYER_SELECT_FIRST = ButtonWrapper(
|
||||
name='PLAYER_SELECT_FIRST',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/PLAYER_SELECT_FIRST.png',
|
||||
area=(467, 293, 496, 316),
|
||||
search=(447, 273, 516, 336),
|
||||
color=(191, 202, 207),
|
||||
button=(558, 179, 1118, 282),
|
||||
),
|
||||
)
|
||||
PLAYER_SELECT_SECOND = ButtonWrapper(
|
||||
name='PLAYER_SELECT_SECOND',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/PLAYER_SELECT_SECOND.png',
|
||||
area=(466, 451, 496, 475),
|
||||
search=(446, 431, 516, 495),
|
||||
color=(192, 203, 208),
|
||||
button=(554, 336, 1127, 443),
|
||||
),
|
||||
)
|
||||
PLAYER_SELECT_THIRD = ButtonWrapper(
|
||||
name='PLAYER_SELECT_THIRD',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/PLAYER_SELECT_THIRD.png',
|
||||
area=(466, 608, 496, 634),
|
||||
search=(446, 588, 516, 654),
|
||||
color=(194, 206, 211),
|
||||
button=(557, 495, 1120, 599),
|
||||
),
|
||||
)
|
||||
PREPARE_CHALLENGE = ButtonWrapper(
|
||||
name='PREPARE_CHALLENGE',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/PREPARE_CHALLENGE.png',
|
||||
area=(583, 83, 698, 111),
|
||||
search=(563, 63, 718, 131),
|
||||
color=(148, 161, 175),
|
||||
button=(561, 548, 723, 598),
|
||||
),
|
||||
)
|
||||
SKIP_OFF = ButtonWrapper(
|
||||
name='SKIP_OFF',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/SKIP_OFF.png',
|
||||
area=(1087, 585, 1127, 622),
|
||||
search=(1067, 565, 1147, 642),
|
||||
color=(96, 137, 171),
|
||||
button=(1088, 582, 1250, 626),
|
||||
),
|
||||
)
|
||||
SKIP_ON = ButtonWrapper(
|
||||
name='SKIP_ON',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/SKIP_ON.png',
|
||||
area=(1096, 591, 1121, 615),
|
||||
search=(1076, 571, 1141, 635),
|
||||
color=(99, 177, 212),
|
||||
button=(1089, 581, 1250, 626),
|
||||
),
|
||||
)
|
||||
START_CHALLENGE = ButtonWrapper(
|
||||
name='START_CHALLENGE',
|
||||
jp=Button(
|
||||
file='./assets/jp/tactical_challenge/START_CHALLENGE.png',
|
||||
area=(107, 10, 159, 37),
|
||||
search=(87, 0, 179, 57),
|
||||
color=(148, 161, 176),
|
||||
button=(1091, 635, 1246, 704),
|
||||
),
|
||||
)
|
||||
@@ -0,0 +1,157 @@
|
||||
import random
|
||||
from enum import Enum
|
||||
|
||||
from module.base.timer import Timer
|
||||
from module.logger import logger
|
||||
from module.ocr.ocr import DigitCounter
|
||||
from module.ui.switch import Switch
|
||||
from tasks.base.page import page_tactical_challenge
|
||||
from tasks.base.ui import UI
|
||||
from tasks.tactical_challenge.assets.assets_tactical_challenge import *
|
||||
|
||||
SWITCH_SKIP = Switch('Skip_switch')
|
||||
SWITCH_SKIP.add_state('on', SKIP_ON)
|
||||
SWITCH_SKIP.add_state('off', SKIP_OFF)
|
||||
|
||||
|
||||
class TCStatus(Enum):
|
||||
"""
|
||||
Tactical challenge status
|
||||
"""
|
||||
REWARD = 0
|
||||
OCR = 1
|
||||
SELECT = 2
|
||||
PREPARE = 3
|
||||
SKIP = 4
|
||||
START = 5
|
||||
RESULT = 6
|
||||
WIN = 7
|
||||
LOSE = 8
|
||||
FINAL = 9
|
||||
FINISHED = -1
|
||||
|
||||
|
||||
class TacticalChallenge(UI):
|
||||
select_players = (PLAYER_SELECT_FIRST, PLAYER_SELECT_SECOND, PLAYER_SELECT_THIRD)
|
||||
|
||||
def _get_ticket(self):
|
||||
"""
|
||||
Page:
|
||||
in: page_tactical_challenge
|
||||
"""
|
||||
ocr = DigitCounter(OCR_TICKET).ocr_single_line(self.device.image)
|
||||
# number of tickets remaining
|
||||
ticket, _, total = ocr
|
||||
if total == 0:
|
||||
logger.warning('Invalid ticket')
|
||||
return False, 5
|
||||
logger.attr('Ticket', ticket)
|
||||
|
||||
return True, ticket
|
||||
|
||||
def _get_reward(self):
|
||||
if self.match_color(GET_REWARD_DAILY):
|
||||
self.device.click(GET_REWARD_DAILY)
|
||||
logger.info('Get tc daily reward')
|
||||
return True
|
||||
if self.match_color(GET_REWARD_CREDIT):
|
||||
self.device.click(GET_REWARD_CREDIT)
|
||||
logger.info('Get tc credit reward')
|
||||
return True
|
||||
if self.match_color(GOT_REWARD_DAILY) and self.match_color(GOT_REWARD_CREDIT):
|
||||
logger.info('Both tc reward got')
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _set_skip(self):
|
||||
"""
|
||||
Set skip switch to on
|
||||
:returns: True if switch is set, False if switch not found
|
||||
"""
|
||||
if not SWITCH_SKIP.appear(main=self):
|
||||
logger.info('Skip switch not found')
|
||||
return False
|
||||
SWITCH_SKIP.set('on', main=self)
|
||||
|
||||
return True
|
||||
|
||||
def _player_select(self, select):
|
||||
if select:
|
||||
return random.choice(self.select_players)
|
||||
else:
|
||||
return self.select_players[select]
|
||||
|
||||
def _handle_challenge(self, status):
|
||||
match status:
|
||||
case TCStatus.REWARD:
|
||||
if self._get_reward():
|
||||
return TCStatus.OCR
|
||||
case TCStatus.OCR:
|
||||
is_valid, ticket = self._get_ticket()
|
||||
if not is_valid:
|
||||
return status
|
||||
if ticket == 0:
|
||||
return TCStatus.FINISHED
|
||||
return TCStatus.SELECT
|
||||
case TCStatus.SELECT:
|
||||
self.appear_then_click(self.select)
|
||||
if self.appear(PREPARE_CHALLENGE):
|
||||
return TCStatus.PREPARE
|
||||
case TCStatus.PREPARE:
|
||||
self.appear_then_click(PREPARE_CHALLENGE)
|
||||
if not self.appear(PREPARE_CHALLENGE):
|
||||
return TCStatus.SKIP
|
||||
case TCStatus.SKIP:
|
||||
if not self._set_skip():
|
||||
return TCStatus.SKIP
|
||||
return TCStatus.START
|
||||
case TCStatus.START:
|
||||
self.appear_then_click(START_CHALLENGE)
|
||||
if not self.appear(START_CHALLENGE):
|
||||
return TCStatus.RESULT
|
||||
case TCStatus.RESULT:
|
||||
if self.appear_then_click(CHALLENGE_WIN):
|
||||
return TCStatus.WIN
|
||||
if self.appear_then_click(CHALLENGE_LOSE):
|
||||
return TCStatus.LOSE
|
||||
case TCStatus.WIN | TCStatus.LOSE:
|
||||
if self.appear(CHALLENGE_WIN) or self.appear(CHALLENGE_LOSE):
|
||||
return TCStatus.RESULT
|
||||
is_valid, ticket = self._get_ticket()
|
||||
if not is_valid:
|
||||
return status
|
||||
if ticket == 0:
|
||||
return TCStatus.FINISHED
|
||||
return TCStatus.FINAL
|
||||
case TCStatus.FINAL | TCStatus.FINISHED:
|
||||
return status
|
||||
case _:
|
||||
logger.warning(f'Invalid status: {status}')
|
||||
return status
|
||||
|
||||
def run(self):
|
||||
self.ui_ensure(page_tactical_challenge)
|
||||
|
||||
self.select = self._player_select(self.config.TacticalChallenge_PlayerSelect)
|
||||
status = TCStatus.REWARD
|
||||
action_timer = Timer(1, count=1)
|
||||
# ensure reward can be clicked
|
||||
ui_timer = Timer(2).start()
|
||||
|
||||
while 1:
|
||||
self.device.screenshot()
|
||||
if self.ui_additional():
|
||||
continue
|
||||
if not ui_timer.reached():
|
||||
continue
|
||||
if action_timer.reached_and_reset():
|
||||
status = self._handle_challenge(status)
|
||||
logger.attr('Status', status.name)
|
||||
if status in (TCStatus.FINAL, TCStatus.FINISHED):
|
||||
break
|
||||
|
||||
if status is TCStatus.FINISHED:
|
||||
self.config.task_delay(server_update=True)
|
||||
else:
|
||||
self.config.task_delay(minute=1)
|
||||
Reference in New Issue
Block a user