Refactor: Standardize the use of match_template_color()

This commit is contained in:
LmeSzinc 2024-12-09 16:09:34 +08:00
parent 164db6f4da
commit ba5b853cb9
22 changed files with 79 additions and 66 deletions

View File

@ -176,6 +176,38 @@ class ModuleBase:
return appear
def match_template_color(self, button, offset=(20, 20), interval=0, similarity=0.85, threshold=30):
"""
Args:
button (Button):
offset (bool, int):
interval (int, float): interval between two active events.
similarity (int, float): 0 to 1.
threshold (int, float): 0 to 255 if not use offset, smaller means more similar
Returns:
bool:
"""
button = self.ensure_button(button)
self.device.stuck_record_add(button)
if interval:
if button.name in self.interval_timer:
if self.interval_timer[button.name].limit != interval:
self.interval_timer[button.name] = Timer(interval)
else:
self.interval_timer[button.name] = Timer(interval)
if not self.interval_timer[button.name].reached():
return False
appear = button.match_template_color(
self.device.image, offset=offset, similarity=similarity, threshold=threshold)
if appear and interval:
self.interval_timer[button.name].reset()
return appear
def appear_then_click(self, button, screenshot=False, genre='items', offset=0, interval=0, similarity=0.85,
threshold=30):
button = self.ensure_button(button)

View File

@ -321,18 +321,26 @@ class Button(Resource):
self._button_offset = area_offset(self._button, offset[:2] + np.array(point))
return sim > similarity
def match_appear_on(self, image, threshold=30):
def match_template_color(self, image, offset=(20, 20), similarity=0.85, threshold=30):
"""
Template match first, color match then
Args:
image: Screenshot.
threshold: Default to 10.
offset (int, tuple): Detection area offset.
similarity (float): 0-1.
threshold (int): Default to 30.
Returns:
bool:
bool.
"""
diff = np.subtract(self.button, self._button)[:2]
area = area_offset(self.area, offset=diff)
return color_similar(color1=get_color(image, area), color2=self.color, threshold=threshold)
if self.match(image, offset=offset, similarity=similarity):
diff = np.subtract(self.button, self._button)[:2]
area = area_offset(self.area, offset=diff)
color = get_color(image, area)
return color_similar(color1=color, color2=self.color, threshold=threshold)
else:
return False
def crop(self, area, image=None, name=None):
"""

View File

@ -363,8 +363,7 @@ class RewardCommission(UI, InfoHandler):
raise GameStuckError('Triggered commission list flashing bug')
# Click
if (self.appear(COMMISSION_START, offset=(5, 20), interval=7)
and COMMISSION_START.match_appear_on(self.device.image)):
if self.match_template_color(COMMISSION_START, offset=(5, 20), interval=7):
self.device.click(COMMISSION_START)
self.interval_reset(COMMISSION_ADVICE)
comm_timer.reset()

View File

@ -183,8 +183,7 @@ class BuyFurniture(UI):
False if Failed buy
"""
self.enter_first_furniture_details_page()
if self.appear(DORM_FURNITURE_COUNTDOWN, offset=(20, 20)) \
and DORM_FURNITURE_COUNTDOWN.match_appear_on(self.device.image):
if self.match_template_color(DORM_FURNITURE_COUNTDOWN, offset=(20, 20)):
logger.info("There is a time-limited furniture available for buy")
if self.buy_furniture_once(self.config.BuyFurniture_BuyOption):

View File

@ -73,8 +73,7 @@ class BattlePass(Combat, UI):
if self.appear_then_click(REWARD_RECEIVE, offset=(20, 20), interval=3):
confirm_timer.reset()
continue
if self.appear(REWARD_RECEIVE_SP, offset=(20, 20), interval=3) \
and REWARD_RECEIVE_SP.match_appear_on(self.device.image, threshold=15):
if self.match_template_color(REWARD_RECEIVE_SP, offset=(20, 20), interval=3, threshold=15):
self.device.click(REWARD_RECEIVE_SP)
confirm_timer.reset()
continue

View File

@ -50,7 +50,7 @@ class LoginHandler(UI):
confirm_timer.reset()
# Login
if self.appear(LOGIN_CHECK, offset=(30, 30), interval=5) and LOGIN_CHECK.match_appear_on(self.device.image):
if self.match_template_color(LOGIN_CHECK, offset=(30, 30), interval=5):
self.device.click(LOGIN_CHECK)
if not login_success:
logger.info('Login success')

View File

@ -217,8 +217,7 @@ class StrategyHandler(InfoHandler):
in: STRATEGY_OPENED
out: STRATEGY_OPENED
"""
if (self.appear(MOB_MOVE_ENTER, offset=MOB_MOVE_OFFSET)
and MOB_MOVE_ENTER.match_appear_on(self.device.image)):
if self.match_template_color(MOB_MOVE_ENTER, offset=MOB_MOVE_OFFSET):
return True
else:
return False

View File

@ -269,8 +269,7 @@ class MapOperation(MysteryHandler, FleetPreparation, Retirement, FastForwardHand
return True
if mode == 'normal':
if self.appear(MAP_MODE_SWITCH_NORMAL, offset=(20, 20)) \
and MAP_MODE_SWITCH_NORMAL.match_appear_on(self.device.image):
if self.match_template_color(MAP_MODE_SWITCH_NORMAL, offset=(20, 20)):
logger.attr('MAP_MODE_SWITCH', 'normal')
return True
elif self.appear(MAP_MODE_SWITCH_HARD, offset=(20, 20), interval=2):
@ -281,8 +280,7 @@ class MapOperation(MysteryHandler, FleetPreparation, Retirement, FastForwardHand
else:
return False
elif mode == 'hard':
if self.appear(MAP_MODE_SWITCH_HARD, offset=(20, 20)) \
and MAP_MODE_SWITCH_HARD.match_appear_on(self.device.image):
if self.match_template_color(MAP_MODE_SWITCH_HARD, offset=(20, 20)):
logger.attr('MAP_MODE_SWITCH', 'hard')
return True
if self.appear(MAP_MODE_SWITCH_NORMAL, offset=(20, 20), interval=2):

View File

@ -69,8 +69,7 @@ class MeowfficerBase(UI):
self.device.screenshot()
# End
if self.appear(MEOWFFICER_CHECK, offset=(20, 20)) \
and MEOWFFICER_CHECK.match_appear_on(self.device.image):
if self.match_template_color(MEOWFFICER_CHECK, offset=(20, 20)):
break
else:
if click_timer.reached():

View File

@ -99,8 +99,7 @@ class MeowfficerBuy(MeowfficerBase):
continue
# End
if self.appear(MEOWFFICER_BUY_ENTER, offset=(20, 20)) \
and MEOWFFICER_BUY_ENTER.match_appear_on(self.device.image):
if self.match_template_color(MEOWFFICER_BUY_ENTER, offset=(20, 20)):
break
def meow_buy(self) -> bool:

View File

@ -75,8 +75,7 @@ class MeowfficerCollect(MeowfficerBase):
Returns:
bool
"""
if self.appear(MEOWFFICER_GET_CHECK, offset=(40, 40)) and MEOWFFICER_GET_CHECK.match_appear_on(
self.device.image):
if self.match_template_color(MEOWFFICER_GET_CHECK, offset=(40, 40)):
return True
if self.appear(MEOWFFICER_TRAIN_START, offset=(20, 20)):

View File

@ -64,8 +64,7 @@ class BeaconReward(Combat, UI):
else:
self.device.screenshot()
if self.appear(REWARD_RECEIVE, offset=(20, 20), interval=3) and REWARD_RECEIVE.match_appear_on(
self.device.image):
if self.match_template_color(REWARD_RECEIVE, offset=(20, 20), interval=3):
self.device.click(REWARD_RECEIVE)
confirm_timer.reset()
continue
@ -166,8 +165,7 @@ class DossierReward(Combat, UI):
else:
self.device.screenshot()
if self.appear(DOSSIER_REWARD_RECEIVE, offset=(20, 20), interval=3) and DOSSIER_REWARD_RECEIVE.match_appear_on(
self.device.image):
if self.match_template_color(DOSSIER_REWARD_RECEIVE, offset=(20, 20), interval=3):
self.device.click(DOSSIER_REWARD_RECEIVE)
confirm_timer.reset()
continue

View File

@ -369,7 +369,8 @@ class OSFleet(OSCamera, Combat, Fleet, OSAsh):
# Arrive
# Check colors, because screen goes black when something is unlocking.
if self.is_in_map() and IN_MAP.match_appear_on(self.device.image):
# A direct use of IN_MAP, basically `self.is_in_map() and IN_MAP.match_template_color()`
if self.match_template_color(IN_MAP, offset=(200, 5)):
self.update_os()
current = self.view.backend.homo_loca
logger.attr('homo_loca', current)

View File

@ -231,12 +231,10 @@ class OpsiAshBeacon(Meta):
else:
self.device.screenshot()
if self.appear(META_INNER_PAGE_DAMAGE, offset=(20, 20)) \
and META_INNER_PAGE_DAMAGE.match_appear_on(self.device.image):
if self.match_template_color(META_INNER_PAGE_DAMAGE, offset=(20, 20)):
logger.info('Already in meta damage page')
break
if self.appear(META_INNER_PAGE_NOT_DAMAGE, offset=(20, 20)) \
and META_INNER_PAGE_NOT_DAMAGE.match_appear_on(self.device.image):
if self.match_template_color(META_INNER_PAGE_NOT_DAMAGE, offset=(20, 20)):
logger.info('In meta details page, should switch to damage page')
self.appear_then_click(META_INNER_PAGE_NOT_DAMAGE, offset=(20, 20), interval=2)
continue

View File

@ -106,8 +106,7 @@ class ActionPointHandler(UI, MapEventHandler):
return self.appear(ACTION_POINT_USE, offset=(20, 20))
def is_current_ap_visible(self):
return self.appear(CURRENT_AP_CHECK, offset=(40, 5)) \
and CURRENT_AP_CHECK.match_appear_on(self.device.image, threshold=15)
return self.match_template_color(CURRENT_AP_CHECK, offset=(40, 5), threshold=15)
def action_point_use(self, skip_first_screenshot=True):
prev = self._action_point_current

View File

@ -10,7 +10,7 @@ class EnemySearchingHandler(EnemySearchingHandler_):
def is_in_map(self):
if IN_MAP.match_luma(self.device.image, offset=(200, 5)):
return True
if self.appear(MAP_GOTO_GLOBE_FOG, offset=(5, 5)) and MAP_GOTO_GLOBE_FOG.match_appear_on(self.device.image):
if self.match_template_color(MAP_GOTO_GLOBE_FOG, offset=(5, 5)):
return True
return False

View File

@ -244,14 +244,12 @@ class MapEventHandler(EnemySearchingHandler):
Returns:
bool: If clicked.
"""
if self.appear(AUTO_SEARCH_OS_MAP_OPTION_OFF, offset=(5, 120)) \
and AUTO_SEARCH_OS_MAP_OPTION_OFF.match_appear_on(self.device.image):
if self.match_template_color(AUTO_SEARCH_OS_MAP_OPTION_OFF, offset=(5, 120)):
if self.info_bar_count() >= 2:
self.device.screenshot_interval_set()
self.os_auto_search_quit(drop=drop)
raise CampaignEnd
if self.appear(AUTO_SEARCH_OS_MAP_OPTION_OFF_DISABLED, offset=(5, 120)) \
and AUTO_SEARCH_OS_MAP_OPTION_OFF_DISABLED.match_appear_on(self.device.image):
if self.match_template_color(AUTO_SEARCH_OS_MAP_OPTION_OFF_DISABLED, offset=(5, 120)):
if self.info_bar_count() >= 2:
self.device.screenshot_interval_set()
self.os_auto_search_quit(drop=drop)
@ -268,20 +266,17 @@ class MapEventHandler(EnemySearchingHandler):
if enable is None:
pass
elif enable:
if self.appear(AUTO_SEARCH_OS_MAP_OPTION_OFF, offset=(5, 120), interval=3) \
and AUTO_SEARCH_OS_MAP_OPTION_OFF.match_appear_on(self.device.image):
if self.match_template_color(AUTO_SEARCH_OS_MAP_OPTION_OFF, offset=(5, 120), interval=3):
self.device.click(AUTO_SEARCH_OS_MAP_OPTION_OFF)
self.interval_reset(AUTO_SEARCH_OS_MAP_OPTION_OFF_DISABLED)
return True
# Game client bugged sometimes, AUTO_SEARCH_OS_MAP_OPTION_OFF grayed out but still functional
if self.appear(AUTO_SEARCH_OS_MAP_OPTION_OFF_DISABLED, offset=(5, 120), interval=3) \
and AUTO_SEARCH_OS_MAP_OPTION_OFF_DISABLED.match_appear_on(self.device.image):
if self.match_template_color(AUTO_SEARCH_OS_MAP_OPTION_OFF_DISABLED, offset=(5, 120), interval=3):
self.device.click(AUTO_SEARCH_OS_MAP_OPTION_OFF_DISABLED)
self.interval_reset(AUTO_SEARCH_OS_MAP_OPTION_OFF)
return True
else:
if self.appear(AUTO_SEARCH_OS_MAP_OPTION_ON, offset=(5, 120), interval=3) \
and AUTO_SEARCH_OS_MAP_OPTION_ON.match_appear_on(self.device.image):
if self.match_template_color(AUTO_SEARCH_OS_MAP_OPTION_ON, offset=(5, 120), interval=3):
self.device.click(AUTO_SEARCH_OS_MAP_OPTION_ON)
return True

View File

@ -81,15 +81,13 @@ class MissionHandler(GlobeOperation, ZoneManager):
# End
if self.is_in_os_mission() \
and not self.appear(MISSION_FINISH, offset=(20, 20)) \
and not (self.appear(MISSION_CHECKOUT, offset=(20, 20))
and MISSION_CHECKOUT.match_appear_on(self.device.image)):
and not self.match_template_color(MISSION_CHECKOUT, offset=(20, 20)):
# No mission found, wait to confirm. Missions might not be loaded so fast.
if confirm_timer.reached():
logger.info('No OS mission found.')
break
elif self.is_in_os_mission() \
and (self.appear(MISSION_CHECKOUT, offset=(20, 20))
and MISSION_CHECKOUT.match_appear_on(self.device.image)):
and self.match_template_color(MISSION_CHECKOUT, offset=(20, 20)):
# Found one mission.
logger.info('Found at least one OS missions.')
break
@ -119,8 +117,7 @@ class MissionHandler(GlobeOperation, ZoneManager):
logger.info('Monthly BOSS mission found, checking missions bellow it')
checkout_offset = (-20, 100, 20, 150)
if not (self.appear(MISSION_CHECKOUT, offset=checkout_offset)
and MISSION_CHECKOUT.match_appear_on(self.device.image)):
if not self.match_template_color(MISSION_CHECKOUT, offset=checkout_offset):
# If not having enough items to claim a mission,
# there will still be MISSION_CHECKOUT, but button is transparent.
# So here needs to use both template matching and color detection.

View File

@ -25,8 +25,7 @@ class StrategicSearchHandler(MapEventHandler):
continue
if self.appear(AUTO_SEARCH_REWARD, offset=(50, 50)):
continue
if self.appear(STRATEGIC_SEARCH_MAP_OPTION_OFF, offset=(20, 20), interval=2) \
and STRATEGIC_SEARCH_MAP_OPTION_OFF.match_appear_on(self.device.image):
if self.match_template_color(STRATEGIC_SEARCH_MAP_OPTION_OFF, offset=(20, 20), interval=2):
self.device.click(STRATEGIC_SEARCH_MAP_OPTION_OFF)
continue

View File

@ -110,12 +110,9 @@ class Retirement(Enhancement, QuickRetireSettingHandler):
timeout.reset()
# Click
if self.appear(SHIP_CONFIRM, offset=(30, 30), interval=2):
if SHIP_CONFIRM.match_appear_on(self.device.image):
self.device.click(SHIP_CONFIRM)
continue
else:
self.interval_clear(SHIP_CONFIRM)
if self.match_template_color(SHIP_CONFIRM, offset=(30, 30), interval=2):
self.device.click(SHIP_CONFIRM)
continue
if self.appear(SHIP_CONFIRM_2, offset=(30, 30), interval=2):
if self.retire_keep_common_cv and not self._have_kept_cv:
self.keep_one_common_cv()
@ -259,7 +256,7 @@ class Retirement(Enhancement, QuickRetireSettingHandler):
if selected == 0:
break
self.device.screenshot()
if not (self.appear(SHIP_CONFIRM, offset=(30, 30)) and SHIP_CONFIRM.match_appear_on(self.device.image)):
if not self.match_template_color(SHIP_CONFIRM, offset=(30, 30)):
logger.warning('No ship selected, retrying')
continue

View File

@ -103,8 +103,7 @@ class Reward(UI):
for button in [MISSION_MULTI, MISSION_SINGLE]:
if not click_timer.reached():
continue
if self.appear(button, offset=(20, 200), interval=interval) \
and button.match_appear_on(self.device.image):
if self.match_template_color(button, offset=(20, 200), interval=interval):
self.device.click(button)
exit_timer.reset()
click_timer.reset()

View File

@ -40,8 +40,7 @@ class StorageUI(UI):
Returns:
bool, if in MATERIAL_CHECK, appear and match_appear_on
"""
return self.appear(MATERIAL_CHECK, offset=(20, 20), interval=interval) \
and MATERIAL_CHECK.match_appear_on(self.device.image)
return self.match_template_color(MATERIAL_CHECK, offset=(20, 20), interval=interval)
def _storage_enter_material(self, skip_first_screenshot=True):
"""