diff --git a/module/atom/image.py b/module/atom/image.py index b4787fdf90..3d4d52d1a0 100644 --- a/module/atom/image.py +++ b/module/atom/image.py @@ -8,7 +8,7 @@ from pathlib import Path from module.atom.RuleImageMallResourceMixin import RuleImageMallResourceMixin -from module.base.decorator import cached_property +from module.base.decorator import cached_property, del_cached_property from module.logger import logger from module.base.utils import is_approx_rectangle from module.base.utils.utils import random_normal_distribution_int @@ -233,6 +233,17 @@ def _sanitize_match_result(result: np.ndarray) -> np.ndarray: logger.error("match result contains nan/inf (solid-color region), treated as not matched") return np.nan_to_num(result, nan=-1.0, posinf=-1.0, neginf=-1.0) + def refresh(self) -> None: + self._image = None + self._kp = None + self._des = None + self._mask = None + self._mask_loaded = False + self._match_init = False + for cached_name in ('name', 'kp', 'des'): + del_cached_property(self, cached_name) + + def corp(self, image: np.array, roi: list = None) -> np.array: """ 截取图片 diff --git a/module/device/device.py b/module/device/device.py index a388cbc052..a57b38db42 100644 --- a/module/device/device.py +++ b/module/device/device.py @@ -12,7 +12,11 @@ from module.config.utils import get_server_next_update from module.device.app_control import AppControl from module.device.control import Control -from module.device.desktop_device import DesktopDevice +if IS_WINDOWS: + from module.device.desktop_device import DesktopDevice +else: + class DesktopDevice: + pass from module.device.platform2 import Platform from module.device.screenshot import Screenshot from module.exception import (GameNotRunningError, diff --git a/tasks/AreaBoss/script_task.py b/tasks/AreaBoss/script_task.py index 9d80b3bee3..5006e1328e 100644 --- a/tasks/AreaBoss/script_task.py +++ b/tasks/AreaBoss/script_task.py @@ -10,6 +10,8 @@ from module.atom.click import RuleClick from tasks.base_task import BaseTask from tasks.Component.GeneralBattle.general_battle import GeneralBattle +from tasks.Component.GeneralBattle.battle import Battle +from tasks.Component.GeneralBattle.battle_wait import battle_wait_strategy, battle_wait_options from tasks.GameUi.game_ui import GameUi from tasks.GameUi.page import page_area_boss, page_shikigami_records from tasks.Component.SwitchSoul.switch_soul import SwitchSoul @@ -21,7 +23,7 @@ from typing import List -class ScriptTask(GeneralBattle, GameUi, SwitchSoul, AreaBossAssets): +class ScriptTask(Battle, GeneralBattle, GameUi, SwitchSoul, AreaBossAssets): def _get_dynamic_boss_count(self) -> int: """ @@ -159,10 +161,10 @@ def boss_fight(self, battle: RuleImage, fileter_open: bool = True) -> bool: return False # 如果已经打过该BOSS,直接跳过不打了 - if self.is_group_ranked(): - logger.warning("There is no boss could be challenged") - self.ui_click_until_disappear(self.I_AB_CLOSE_RED, interval=3) - return True + # if self.is_group_ranked(): + # logger.warning("There is no boss could be challenged") + # self.ui_click_until_disappear(self.I_AB_CLOSE_RED, interval=3) + # return True # 根据 reward_floor 决定模式 if reward_floor in (AreaBossFloor.ONE, AreaBossFloor.TEN): @@ -193,8 +195,12 @@ def start_fight(self) -> bool: continue if not self.appear(self.I_AB_CLOSE_RED): # 如果这个红色的关闭不见了才可以进行继续 break - - return self.run_general_battle(self.config.area_boss.general_battle) + strategies, options = self.loadout_from_config(self.config.area_boss.general_battle) + options['completion'].check_imgs = [self.I_FIRE, self.I_FILTER] + self.loadout_show((strategies, options)) # 使用的是默认的策略: strategies + with battle_wait_options(**options): + win = self.battle_wait() + return win def setup_ultra(self, floor: AreaBossFloor = AreaBossFloor.ONE) -> bool: # 确定进入鬼王界面 @@ -493,6 +499,8 @@ def check_common_chars(self, bossName, name): return 1 else: return 0 # 如果交集的字符少于2个,可以根据需要返回其他值 + + if __name__ == '__main__': from module.config.config import Config from module.device.device import Device diff --git a/tasks/Component/Buy/assets.py b/tasks/Component/Buy/assets.py index 281c4ed2b6..a5c23a9d23 100644 --- a/tasks/Component/Buy/assets.py +++ b/tasks/Component/Buy/assets.py @@ -30,10 +30,14 @@ class BuyAssets: I_BUY_RMB = RuleImage(roi_front=(610,532,35,32), roi_back=(548,483,178,119), threshold=0.8, method="Template matching", file="./tasks/Component/Buy/buy/buy_buy_rmb.png") # 购买成功 I_BUY_SUCCESS = RuleImage(roi_front=(512,53,261,44), roi_back=(449,0,405,167), threshold=0.8, method="Template matching", file="./tasks/Component/Buy/buy/buy_buy_success.png") + # 确认按钮上的勾玉 + I_BUY_JADE = RuleImage(roi_front=(604,537,42,52), roi_back=(540,480,200,120), threshold=0.8, method="Template matching", file="./tasks/Component/Buy/buy/buy_buy_jade.png") # Ocr Rule Assets # Ocr-description O_BUY_NUMBER = RuleOcr(roi=(578,416,52,71), area=(578,416,52,71), mode="Digit", method="Default", keyword="", name="buy_number") + # 确认按钮上的单价, roi 运行时按勾玉图标位置设置 + O_BUY_PRICE = RuleOcr(roi=(646,537,60,52), area=(646,537,60,52), mode="Digit", method="Default", keyword="", name="buy_price") diff --git a/tasks/Component/Buy/buy.py b/tasks/Component/Buy/buy.py index a5f31ac91a..807c814a43 100644 --- a/tasks/Component/Buy/buy.py +++ b/tasks/Component/Buy/buy.py @@ -164,6 +164,171 @@ def buy_more(self, start_click: Union[RuleImage, RuleOcr, RuleClick], continue return True + def buy_one_or_more(self, start_click: Union[RuleImage, RuleOcr, RuleClick], + check_image: RuleImage, number: int = None, + max_price: int = None, money: int = None, remain: int = None) -> bool: + """ + 购买弹窗可能是单个购买窗口, 也可能是带数量选择(加号)的多数量购买窗口 + 先点开弹窗, 出现加号走 buy_more, 出现单个购买窗口走 buy_one + :param start_click: 开始点击 + :param check_image: 单个购买窗口确认时候的图片 + :param number: 多数量窗口的购买数量, 不指定就是拉满. 指定 max_price 时不用, 数量按 money 和 remain 算 + :param max_price: 单价上限, 不为 None 时先读确认按钮上的勾玉单价, 没有勾玉、读不到或超过上限就取消购买 + :param money: 当前余额, 指定 max_price 时用来算买得起几个 + :param remain: 剩余购买次数, 指定 max_price 时用来算购买数量 + :return: 超时没等到任何购买弹窗或取消购买返回 False + """ + timeout = Timer(10).start() + while 1: + self.screenshot() + + if self.appear(self.I_BUY_PLUS): + if max_price is not None: + return self._buy_with_price(start_click, check_image, True, max_price, money, remain) + if number == 1: + # buy_more 指定数量时会先点一次加号再 OCR, 买 1 个会变成买 2 个 + return self._buy_more_default(start_click) + logger.info('Buy window with number selector, use buy_more') + return self.buy_more(start_click, number) + if self.appear(check_image): + if max_price is not None: + return self._buy_with_price(start_click, check_image, False, max_price, money, remain) + logger.info('Single buy window, use buy_one') + return self.buy_one(start_click, check_image) + if timeout.reached(): + logger.warning('Wait buy window timeout') + return False + + if isinstance(start_click, RuleImage): + if self.appear_then_click(start_click, interval=1): + continue + elif isinstance(start_click, RuleOcr): + if self.ocr_appear_click(start_click, interval=1): + continue + elif isinstance(start_click, RuleClick): + if self.click(start_click, interval=1): + continue + + def _buy_with_price(self, start_click: Union[RuleImage, RuleOcr, RuleClick], check_image: RuleImage, + more: bool, max_price: int, money: int, remain: int) -> bool: + """ + 购买弹窗已打开, 读确认按钮上的勾玉单价, 不超过上限才买 + :param more: True 为多数量购买窗口, False 为单个购买窗口 + :return: 买到返回 True, 取消购买或达到购买上限返回 False + """ + price = self._buy_read_price() + if price is None or price > max_price: + logger.warning(f'Buy price {price} is unknown or over max price {max_price}, cancel buy') + self._buy_cancel(check_image) + return False + if not more: + logger.info(f'Single buy window, price {price}, buy number 1') + return self.buy_one(start_click, check_image) + if money is None or remain is None: + # 没传余额或剩余次数时只买 1 个 + number = 1 + else: + number = min(remain, money // price) + logger.info(f'Buy price {price}, money {money}, remain {remain}, buy number {number}') + if number < 1: + logger.warning('Money is not enough to buy one, cancel buy') + self._buy_cancel(check_image) + return False + if number == 1: + return self._buy_more_default(start_click) + if number == remain: + return self.buy_more(start_click, None) + # buy_more(start_click, number) 点加号后才读数量, 可能超过 number. + # 余额不够买满时每次只买 1 个, 调用方循环会重新读余额和剩余次数再买下一个 + logger.info(f'Money is not enough to buy all {remain}, buy one by one') + return self._buy_more_default(start_click) + + def _buy_read_price(self, min_price: int = 40) -> Union[int, None]: + """ + 读购买确认按钮上的勾玉单价: 先等勾玉图标出现, 再读图标右边的数字 + :param min_price: 单价下限, 低于它当作 OCR 读错(例如 90 少读一位成 9). 默认 40: 蓝票已知最低价 50, 留一点余量 + :return: 单价, 没有勾玉图标、读不到或低于下限返回 None + """ + jade_timer = Timer(2).start() + while 1: + if self.appear(self.I_BUY_JADE): + break + if jade_timer.reached(): + logger.warning('Jade icon is not found on buy button, maybe not paid by jade') + return None + self.screenshot() + x, y, w, h = self.I_BUY_JADE.roi_front + # 只取图标右边, 裁剪框碰到图标会把图标读成 1, 例如 90 读成 190 + self.O_BUY_PRICE.roi = [x + w, y, 60, h] + price = self.O_BUY_PRICE.ocr(self.device.image) + try: + price = int(price) + except (TypeError, ValueError): + price = 0 + if price == 0: + # Digit 读不到时返回 0 + logger.warning('OCR buy price failed') + return None + if price < min_price: + logger.warning(f'Buy price {price} is lower than min price {min_price}, treat as OCR error') + return None + return price + + def _buy_cancel(self, check_image: RuleImage) -> bool: + """ + 点取消关掉购买弹窗, 直到多数量窗口的加号和单个购买窗口的确认图都消失 + :return: 关掉返回 True, 超时返回 False + """ + timeout = Timer(10).start() + while 1: + self.screenshot() + if not self.appear(self.I_BUY_PLUS) and not self.appear(check_image): + return True + if timeout.reached(): + logger.warning('Cancel buy window timeout') + return False + if self.click(self.C_BUY_CANCEL, interval=1): + continue + + def _buy_more_default(self, start_click: Union[RuleImage, RuleOcr, RuleClick]) -> bool: + """ + 多数量购买窗口不调整数量, 默认数量为 1 时直接确认买 1 个 + :param start_click: 默认数量不是 1 时退回 buy_more(start_click, 1) 用 + :return: 买到返回 True, 达到购买上限返回 False + """ + ocr_timer = Timer(2).start() + while 1: + current = self.O_BUY_NUMBER.ocr(self.device.image) + if current == 1: + break + if ocr_timer.reached(): + logger.warning(f'Default buy number is {current}, fallback to buy_more') + return self.buy_more(start_click, 1) + self.screenshot() + logger.info('Buy window with number selector, buy one with default number') + while 1: + self.screenshot() + + if self.ui_reward_appear_click(): + while 1: + self.screenshot() + # 等待动画结束 + if not self.appear(self.I_UI_REWARD, threshold=0.6): + logger.info('Get reward success') + break + # 一直点击 + if self.ui_reward_appear_click(): + continue + return True + + if self.appear(self.I_UI_CONFIRM_SAMLL): + self.ui_click_until_disappear(self.I_UI_CONFIRM_SAMLL, interval=1) + logger.warning('Buy number limit') + return False + + if self.click(self.C_BUY_MORE, interval=2): + continue + def buy_check_money(self, target: RuleOcr, minimum: int): """ 检查钱是否足够 diff --git a/tasks/Component/Buy/buy/buy_buy_jade.png b/tasks/Component/Buy/buy/buy_buy_jade.png new file mode 100644 index 0000000000..4757eeab45 Binary files /dev/null and b/tasks/Component/Buy/buy/buy_buy_jade.png differ diff --git a/tasks/Component/Buy/buy/image.json b/tasks/Component/Buy/buy/image.json index 0356094aa3..18dd70df25 100644 --- a/tasks/Component/Buy/buy/image.json +++ b/tasks/Component/Buy/buy/image.json @@ -43,5 +43,14 @@ "method": "Template matching", "threshold": 0.8, "description": "购买成功" + }, + { + "itemName": "buy_jade", + "imageName": "buy_buy_jade.png", + "roiFront": "604,537,42,52", + "roiBack": "540,480,200,120", + "method": "Template matching", + "threshold": 0.8, + "description": "确认按钮上的勾玉" } ] \ No newline at end of file diff --git a/tasks/Component/Buy/buy/ocr.json b/tasks/Component/Buy/buy/ocr.json index b7eedcaf5b..0a1cb3df31 100644 --- a/tasks/Component/Buy/buy/ocr.json +++ b/tasks/Component/Buy/buy/ocr.json @@ -7,5 +7,14 @@ "method": "Default", "keyword": "", "description": "Ocr-description" + }, + { + "itemName": "buy_price", + "roiFront": "646,537,60,52", + "roiBack": "646,537,60,52", + "mode": "Digit", + "method": "Default", + "keyword": "", + "description": "确认按钮上的单价, roi 运行时按勾玉图标位置设置" } ] \ No newline at end of file diff --git a/tasks/Component/Costume/costume_base.py b/tasks/Component/Costume/costume_base.py index 4c59d5f8a2..6bd0365b80 100644 --- a/tasks/Component/Costume/costume_base.py +++ b/tasks/Component/Costume/costume_base.py @@ -125,6 +125,8 @@ def replace_img(self, asset_before_object.roi_back = asset_after.roi_back asset_before_object.threshold = asset_after.threshold asset_before_object.file = asset_after.file + # https://github.com/runhey/OnmyojiAutoScript/issues/1972 + asset_before_object.refresh() def check_costume_main(self, main_type: MainType): if main_type == MainType.COSTUME_MAIN: diff --git a/tasks/Component/GeneralBattle/battle_wait.py b/tasks/Component/GeneralBattle/battle_wait.py index f6189d3ff7..3de381b66c 100644 --- a/tasks/Component/GeneralBattle/battle_wait.py +++ b/tasks/Component/GeneralBattle/battle_wait.py @@ -1100,6 +1100,10 @@ def _nondefault(o): print('=' * 64) time.sleep(0.6) + def refresh_long_time(self) -> None: + self.device.stuck_record_add('BATTLE_STATUS_S') + self.device.click_record_clear() + # ------------------------------------------------------------------------------------------------------------------ # build in # ------------------------------------------------------------------------------------------------------------------ @@ -1107,8 +1111,7 @@ def _bw_setup_default(self, pub: PublicContext, pri: PrivateContext) -> HookSign self.C_REWARD_1.name = 'C_REWARD' self.C_REWARD_2.name = 'C_REWARD' self.C_REWARD_3.name = 'C_REWARD' - self.device.stuck_record_add('BATTLE_STATUS_S') - self.device.click_record_clear() + self.refresh_long_time() logger.info('Start battle process') return HookSignal.DONE @@ -1184,10 +1187,6 @@ def _bw_success_default(self, pub: PublicContext, pri: PrivateContext) -> HookSi while 1: self.screenshot() - # 发现御魂溢出,直接跳出,在interupt里面处理 - if self.appear(self.I_OVER_GHOST): - return HookSignal.CONTINUE - # 结算的时候御魂溢出提示,点击【知道了】 if self.appear(self.I_OVER_GHOST): self.click(self.I_OVER_GHOST) @@ -1248,6 +1247,7 @@ def _bw_prepare_default(self, pub: PublicContext, pri: PrivateContext) -> HookSi if pri_options.lock_team or self.is_in_real_battle(False): logger.info('Prepare stage finish') state.done = True + self.refresh_long_time() return runtime.hook_enabled_update( enable=('green', 'red', 'echo'), disable=('prepare', 'preset'), @@ -1290,7 +1290,6 @@ def _bw_preset_default(self, pub: PublicContext, pri: PrivateContext) -> HookSig return HookSignal.CONTINUE if not pri_options.preset_enable or state.done: return HookSignal.CONTINUE - logger.info("Preset is enable") # 点击预设按钮 while 1: @@ -1478,6 +1477,7 @@ def _bw_green_default(self, pub: PublicContext, pri: PrivateContext) -> HookSign # 点击绿标 self.device.click(x, y) state.done = True + self.refresh_long_time() return runtime.hook_enabled_update( enable=(), disable=('green',), @@ -1521,8 +1521,8 @@ def _bw_randomclick_default(self, pub: PublicContext, pri: PrivateContext) -> Ho # custom # ------------------------------------------------------------------------------------------------------------------ def _bw_success_soul(self, pub: PublicContext, pri: PrivateContext) -> HookSignal: - state = pri.per_battle - options = pri.options + state: PerBattleSuccess = pri.per_battle + options: OptionSuccessDefault = pri.options if not isinstance(state, PerBattleSuccess) or not isinstance(options, OptionSuccessDefault): raise TypeError("battle wait option 'success' must be an OptionSuccessDefault") if state.click_stage_1 is None: @@ -1565,7 +1565,7 @@ def _bw_success_soul(self, pub: PublicContext, pri: PrivateContext) -> HookSigna return HookSignal.CONTINUE @cached_property - def exclude_click_activity(self, areas: list[str] = None) -> RuleClickExclude: + def exclude_click_activity(self) -> RuleClickExclude: inputs = [] for area in ['C_END_MESSAGE_RIGHT_TOP', 'C_END_ACTIVITY_REWARD']: click = getattr(self, area, None) diff --git a/tasks/Component/GeneralBattle/general_battle.py b/tasks/Component/GeneralBattle/general_battle.py index f255580110..d708293d6c 100644 --- a/tasks/Component/GeneralBattle/general_battle.py +++ b/tasks/Component/GeneralBattle/general_battle.py @@ -228,6 +228,11 @@ def battle_wait(self, random_click_swipt_enable: bool) -> bool: logger.info("Get reward") while 1: self.screenshot() + + if self.appear(self.I_OVER_GHOST): + self.click(self.I_OVER_GHOST) + continue + # 如果出现领奖励 action_click = random.choice([self.C_REWARD_1, self.C_REWARD_2, self.C_REWARD_3]) if (self.appear_then_click(self.I_REWARD, action=action_click, interval=1.5) or diff --git a/tasks/DailyTrifles/script_task.py b/tasks/DailyTrifles/script_task.py index e2994c027f..bdea4e2fb9 100644 --- a/tasks/DailyTrifles/script_task.py +++ b/tasks/DailyTrifles/script_task.py @@ -216,7 +216,7 @@ def run_store(self): if self.config.daily_trifles.trifles_config.buy_sushi_count > 0: self.run_buy_sushi() - self.ui_click(self.I_UI_BACK_YELLOW, self.I_CHECK_MALL, interval=2.4, timeout=8) + self.goto_page(page_mall, skip_first_screenshot=False, timeout=8) self.goto_page(page_main) def run_store_sign(self): diff --git a/tasks/Dokan/script_task.py b/tasks/Dokan/script_task.py index e6063133e8..0c05c8c241 100644 --- a/tasks/Dokan/script_task.py +++ b/tasks/Dokan/script_task.py @@ -91,6 +91,15 @@ def run(self): if not in_dokan: out_dokan_timer.start() self.goto_dokan_scene() + in_dokan, current_scene = self.get_current_scene(True) + + # 进入道馆寻找界面后,在主循环前仅尝试一次开启道馆 + if (in_dokan and + current_scene == DokanScene.RYOU_DOKAN_SCENE_FINDING_DOKAN and + cfg.dokan_config.try_start_dokan): + count = self.update_remain_attack_count() + if count > 0: + self.creat_dokan() # 是否已击败馆主第一阵容 first_master_killed = False @@ -140,10 +149,6 @@ def run(self): if not try_start_dokan: is_dokan_activated = False break - # NOTE 只在周一尝试建立道馆 - if datetime.now().weekday() == 0: - self.creat_dokan() - # 寻找合适道馆,找不到直接退出 if not self.find_dokan(self.config.dokan.dokan_config.find_dokan_score): is_dokan_activated = False @@ -950,7 +955,7 @@ def next_run(self, skip_today=False, is_dokan_activated=False): # # 本来以为server为False(finish=True,success=False,server=False)就不会变成明天,谁知道还是变成明天 # # 逻辑太复杂,不如直接target,简单点 if self.config.dokan.attack_count_config.remain_attack_count == 1 and self.config.dokan.attack_count_config.daily_attack_count == 2: - self.set_next_run(task="Dokan", target=now + self.config.dokan.scheduler.failure_interval) + self.set_next_run(task="Dokan", target=now + self.config.dokan.scheduler.failure_interval, server=False) return # 其余情况当作成功 self.set_next_run(task="Dokan", finish=False, success=True, server=True) diff --git a/tasks/EternitySea/script_task.py b/tasks/EternitySea/script_task.py index f5d898dab9..a5a15fcfe2 100644 --- a/tasks/EternitySea/script_task.py +++ b/tasks/EternitySea/script_task.py @@ -36,6 +36,13 @@ def _two_teams_switch_sous(self, config): self.goto_page(page_shikigami_records) self.run_switch_soul_by_name(config.group_name, config.team_name) + def _prepare_eternity_sea(self): + self.goto_page(page_soul_zones) + self._enter_eternity_sea() + layer = self._task_config.eternity_sea_config.layer + self.check_layer(layer) + self.check_lock(self._task_config.general_battle_config.lock_team_enable) + def run(self) -> None: self._two_teams_switch_sous(self._task_config.switch_soul_config_1) self._two_teams_switch_sous(self._task_config.switch_soul_config_2) @@ -55,11 +62,7 @@ def run(self) -> None: def run_leader(self): logger.info('Start run leader') - self.goto_page(page_soul_zones) - self._enter_eternity_sea() - layer = self._task_config.eternity_sea_config.layer - self.check_layer(layer) - self.check_lock(self._task_config.general_battle_config.lock_team_enable) + self._prepare_eternity_sea() # 创建队伍 logger.info('Create team') while 1: @@ -177,9 +180,7 @@ def _back_to_soul_zones(self) -> None: def run_member(self): logger.info('Start run member') - # self.ui_goto(page_soul_zones) - # self.orochi_enter() - # self.check_lock(self.config.orochi.general_battle_config.lock_team_enable) + self._prepare_eternity_sea() # 进入战斗流程 self.device.stuck_record_add('BATTLE_STATUS_S') @@ -332,4 +333,4 @@ def check_lock(self, lock: bool = True) -> bool: c = Config("oas1") d = Device(c) t = ScriptTask(c, d) - t.screenshot() \ No newline at end of file + t.screenshot() diff --git a/tasks/GameUi/assets.py b/tasks/GameUi/assets.py index c5211c12f3..44138964ae 100644 --- a/tasks/GameUi/assets.py +++ b/tasks/GameUi/assets.py @@ -145,6 +145,8 @@ class GameUiAssets: I_CHECK_DAILY = RuleImage(roi_front=(28,515,58,62), roi_back=(1,487,154,145), threshold=0.8, method="Template matching", file="./tasks/GameUi/page/page_check_daily.png") # 商店页面标志 I_CHECK_MALL = RuleImage(roi_front=(231,455,131,78), roi_back=(201,432,185,121), threshold=0.8, method="Template matching", file="./tasks/GameUi/page/page_check_mall.png") + # 商店页面标志(紫色招牌) + I_CHECK_MALL_PURPLE = RuleImage(roi_front=(231,455,131,78), roi_back=(201,432,185,121), threshold=0.8, method="Template matching", file="./tasks/GameUi/page/page_check_mall_purple.png") # 商店推荐页面标志 I_CHECK_MALL_RECOMMEND = RuleImage(roi_front=(930,12,42,37), roi_back=(905,0,100,100), threshold=0.8, method="Template matching", file="./tasks/GameUi/page/page_check_mall_recommend.png") # 阴阳寮页面标志 diff --git a/tasks/GameUi/default_pages.py b/tasks/GameUi/default_pages.py index 2860d86581..b812840cec 100644 --- a/tasks/GameUi/default_pages.py +++ b/tasks/GameUi/default_pages.py @@ -95,7 +95,7 @@ def handle_login_page(task) -> bool: page_daily.connect(page_main, GlobalGameAssets.I_UI_BACK_YELLOW, key="page_daily->page_main") page_main.connect(page_daily, GameUiAssets.I_MAIN_GOTO_DAILY, key="page_main->page_daily") -page_mall = Page(GameUiAssets.I_CHECK_MALL, category="global") +page_mall = Page(any_of(GameUiAssets.I_CHECK_MALL, GameUiAssets.I_CHECK_MALL_PURPLE), category="global") page_mall.add_enter_failure_hooks(conditional_action(condition=GameUiAssets.I_CHECK_MAIN, action=RestartAssets.C_LOGIN_SCROLL_CLOSE_AREA)) page_mall.add_enter_success_hooks(GameUiAssets.I_AD_CLOSE_RED, GlobalGameAssets.I_UI_BACK_RED, GlobalGameAssets.I_UI_CANCEL_SAMLL) diff --git a/tasks/GameUi/page/image_main.json b/tasks/GameUi/page/image_main.json index ece4396bb8..935d51f9fa 100644 --- a/tasks/GameUi/page/image_main.json +++ b/tasks/GameUi/page/image_main.json @@ -170,6 +170,15 @@ "threshold": 0.8, "description": "商店页面标志" }, + { + "itemName": "check_mall_purple", + "imageName": "page_check_mall_purple.png", + "roiFront": "231,455,131,78", + "roiBack": "201,432,185,121", + "method": "Template matching", + "threshold": 0.8, + "description": "商店页面标志(紫色招牌)" + }, { "itemName": "check_mall_recommend", "imageName": "page_check_mall_recommend.png", diff --git a/tasks/GameUi/page/page_check_mall_purple.png b/tasks/GameUi/page/page_check_mall_purple.png new file mode 100644 index 0000000000..f6766029c2 Binary files /dev/null and b/tasks/GameUi/page/page_check_mall_purple.png differ diff --git a/tasks/GlobalGame/assets.py b/tasks/GlobalGame/assets.py index 106569e11c..1a2d23bc6f 100644 --- a/tasks/GlobalGame/assets.py +++ b/tasks/GlobalGame/assets.py @@ -12,17 +12,17 @@ class GlobalGameAssets: # Image Rule Assets # 接受 - I_G_ACCEPT = RuleImage(roi_front=(830,407,56,46), roi_back=(798,369,117,110), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_accept.png") + I_G_ACCEPT = RuleImage(roi_front=(846,471,56,46), roi_back=(810,448,120,192), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_accept.png") # 拒绝 - I_G_REJECT = RuleImage(roi_front=(832,508,53,51), roi_back=(808,480,108,101), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_reject.png") + I_G_REJECT = RuleImage(roi_front=(846,561,53,51), roi_back=(826,458,99,172), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_reject.png") # 勾玉 - I_G_JADE = RuleImage(roi_front=(661,466,43,46), roi_back=(661,466,43,46), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_jade.png") + I_G_JADE = RuleImage(roi_front=(677,510,43,46), roi_back=(549,483,202,111), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_jade.png") # 猫粮 - I_G_CAT_FOOD = RuleImage(roi_front=(565,463,59,65), roi_back=(565,463,59,65), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_cat_food.png") + I_G_CAT_FOOD = RuleImage(roi_front=(635,501,59,65), roi_back=(558,472,191,132), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_cat_food.png") # 狗粮 - I_G_DOG_FOOD = RuleImage(roi_front=(565,464,60,63), roi_back=(565,464,60,63), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_dog_food.png") + I_G_DOG_FOOD = RuleImage(roi_front=(643,505,60,63), roi_back=(541,485,234,115), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_dog_food.png") # 忽略 - I_G_IGNORE = RuleImage(roi_front=(773,116,21,21), roi_back=(759,102,48,48), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_ignore.png") + I_G_IGNORE = RuleImage(roi_front=(859,94,21,21), roi_back=(725,40,197,142), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_g_ignore.png") # 网络异常 I_NETWORK_ABNORMAL = RuleImage(roi_front=(583,330,221,67), roi_back=(583,330,221,67), threshold=0.8, method="Template matching", file="./tasks/GlobalGame/gg/gg_network_abnormal.png") # 已端口游戏服务器,需要重新连接,这种情况一般是断开很久了 diff --git a/tasks/GlobalGame/gg/gg_g_accept.png b/tasks/GlobalGame/gg/gg_g_accept.png index 2299dbd960..6b4ea5237f 100644 Binary files a/tasks/GlobalGame/gg/gg_g_accept.png and b/tasks/GlobalGame/gg/gg_g_accept.png differ diff --git a/tasks/GlobalGame/gg/gg_g_reject.png b/tasks/GlobalGame/gg/gg_g_reject.png index 2aa88c60b0..c35aff3439 100644 Binary files a/tasks/GlobalGame/gg/gg_g_reject.png and b/tasks/GlobalGame/gg/gg_g_reject.png differ diff --git a/tasks/GlobalGame/gg/image.json b/tasks/GlobalGame/gg/image.json index b223424be9..d76400b526 100644 --- a/tasks/GlobalGame/gg/image.json +++ b/tasks/GlobalGame/gg/image.json @@ -2,8 +2,8 @@ { "itemName": "g_accept", "imageName": "gg_g_accept.png", - "roiFront": "830,407,56,46", - "roiBack": "798,369,117,110", + "roiFront": "846,471,56,46", + "roiBack": "810,448,120,192", "method": "Template matching", "threshold": 0.8, "description": "接受" @@ -11,8 +11,8 @@ { "itemName": "g_reject", "imageName": "gg_g_reject.png", - "roiFront": "832,508,53,51", - "roiBack": "808,480,108,101", + "roiFront": "846,561,53,51", + "roiBack": "826,458,99,172", "method": "Template matching", "threshold": 0.8, "description": "拒绝" @@ -20,8 +20,8 @@ { "itemName": "g_jade", "imageName": "gg_g_jade.png", - "roiFront": "661,466,43,46", - "roiBack": "661,466,43,46", + "roiFront": "677,510,43,46", + "roiBack": "549,483,202,111", "method": "Template matching", "threshold": 0.8, "description": "勾玉" @@ -29,8 +29,8 @@ { "itemName": "g_cat_food", "imageName": "gg_g_cat_food.png", - "roiFront": "565,463,59,65", - "roiBack": "565,463,59,65", + "roiFront": "635,501,59,65", + "roiBack": "558,472,191,132", "method": "Template matching", "threshold": 0.8, "description": "猫粮" @@ -38,8 +38,8 @@ { "itemName": "g_dog_food", "imageName": "gg_g_dog_food.png", - "roiFront": "565,464,60,63", - "roiBack": "565,464,60,63", + "roiFront": "643,505,60,63", + "roiBack": "541,485,234,115", "method": "Template matching", "threshold": 0.8, "description": "狗粮" @@ -47,8 +47,8 @@ { "itemName": "g_ignore", "imageName": "gg_g_ignore.png", - "roiFront": "773,116,21,21", - "roiBack": "759,102,48,48", + "roiFront": "859,94,21,21", + "roiBack": "725,40,197,142", "method": "Template matching", "threshold": 0.8, "description": "忽略" diff --git a/tasks/MysteryShop/script_task.py b/tasks/MysteryShop/script_task.py index 115c220999..e37634e9f9 100644 --- a/tasks/MysteryShop/script_task.py +++ b/tasks/MysteryShop/script_task.py @@ -88,7 +88,8 @@ def run_shop(self, shop_config: ShopConfig = None): """ if shop_config.mystery_amulet: while self.buy_mall_one(buy_button=self.I_MS_BLUE, buy_check=self.I_MS_CHECK_BLUE, - money_ocr=self.O_MALL_RESOURCE_5, buy_money=85): + money_ocr=self.O_MALL_RESOURCE_5, buy_money=85, more_window=True, + max_price=85): pass if shop_config.black_daruma_scrap: while self.buy_mall_one(buy_button=self.I_MS_BLACK, buy_check=self.I_MS_CHECK_BLACK, diff --git a/tasks/RichMan/mall/friendship_points.py b/tasks/RichMan/mall/friendship_points.py index 2727060a7d..80735ccc63 100644 --- a/tasks/RichMan/mall/friendship_points.py +++ b/tasks/RichMan/mall/friendship_points.py @@ -32,13 +32,16 @@ def execute_friendship(self, con: FriendshipPointsConfig = None): self.buy_mall_more(buy_button=self.I_FS_BROKEN, remain_number=False, money_ocr=self.I_MALL_RESOURCE_FRIENDS.build_mall_resource_ocr(self.device.image), buy_number=con.broken_amulet, buy_max=99, buy_money=100) - def buy_mall_one(self, buy_button: RuleImage, buy_check: RuleImage, money_ocr: RuleOcr, buy_money: int): + def buy_mall_one(self, buy_button: RuleImage, buy_check: RuleImage, money_ocr: RuleOcr, buy_money: int, + more_window: bool = False, max_price: int = None): """ 针对只能买一个的 :param buy_button: :param buy_check: :param money_ocr: :param buy_money: 买这一个花多少 + :param more_window: 购买弹窗可能是多数量窗口(剩余次数大于1时), 为 True 时按弹窗类型选 buy_more 或 buy_one + :param max_price: 单价上限, 不为 None 时从购买弹窗读单价, 超过上限或读不到就不买, 数量按剩余次数和余额算 :return: """ logger.hr(buy_button.name, 3) @@ -62,6 +65,14 @@ def buy_mall_one(self, buy_button: RuleImage, buy_check: RuleImage, money_ocr: R logger.warning(f'No enough money {current_money}') return False # 点击购买 + if max_price is not None: + # buy_money 只是余额门槛, 不是单价. 单价从购买弹窗上读, 由 buy_one_or_more 按单价算数量 + logger.info(f'Money {current_money}, remain {_remain}, max price {max_price}') + return self.buy_one_or_more(buy_button, buy_check, max_price=max_price, + money=current_money, remain=_remain) + if more_window: + # 不读单价时只买 1 个, 由调用方循环重新检查剩余次数和余额再买 + return self.buy_one_or_more(buy_button, buy_check, 1) return self.buy_one(buy_button, buy_check) def buy_mall_more(self, buy_button: RuleImage, remain_number: bool, money_ocr: RuleOcr, diff --git a/tasks/RichMan/mall/navbar.py b/tasks/RichMan/mall/navbar.py index 713c9f69e4..aa725e54bc 100644 --- a/tasks/RichMan/mall/navbar.py +++ b/tasks/RichMan/mall/navbar.py @@ -6,7 +6,7 @@ from module.logger import logger -from tasks.GameUi.page import page_main, page_guild +from tasks.GameUi.page import page_main, page_guild, page_mall from tasks.GameUi.game_ui import GameUi from tasks.Component.Buy.buy import Buy from tasks.RichMan.assets import RichManAssets @@ -89,7 +89,7 @@ def back_mall(self): 返回商城 :return: """ - self.ui_click(self.I_UI_BACK_YELLOW, self.I_CHECK_MALL, interval=3, timeout=6) + self.goto_page(page_mall, skip_first_screenshot=False, timeout=6) def mall_resource(self, index: int) -> int: diff --git a/tasks/RichMan/mall/scales.py b/tasks/RichMan/mall/scales.py index d48f6179f6..0975c705ca 100644 --- a/tasks/RichMan/mall/scales.py +++ b/tasks/RichMan/mall/scales.py @@ -314,6 +314,19 @@ def _scales_sea(self, buy_number: int, buy_rule: str='auto'): if remain_number == 0: logger.warning(f'The remaining purchase quantity of xx is {remain_number}') return + # 游戏内开启自动选择御魂时,一次可以买完 + if self.config.rich_man.scales.enable_book_auto: + # 检查钱是否够买一个 + current_money = self.O_SCA_RES_SEA.ocr(self.device.image) + if not isinstance(current_money, int): + logger.warning('OCR error') + return + if current_money < 400: + logger.warning('Scales sea money is not enough') + return + self._scales_buy_more(self.I_SCA_PICTURE_BOOK) + time.sleep(0.5) + return if remain_number < buy_number: buy_number = remain_number logger.warning(f'Remaining purchase quantity is {remain_number}, buy_number is {buy_number}') @@ -340,16 +353,10 @@ def _scales_sea(self, buy_number: int, buy_rule: str='auto'): buy_res_number = buy_number if buy_cycles_number: for i in range(buy_cycles_number): - if self.config.rich_man.scales.enable_book_auto: - self._scales_buy_more(self.I_SCA_PICTURE_BOOK) - else: - self._scales_buy_sea_more(self.I_SCA_PICTURE_BOOK) + self._scales_buy_sea_more(self.I_SCA_PICTURE_BOOK) time.sleep(0.5) if buy_res_number and buy_res_number >= 2: - if self.config.rich_man.scales.enable_book_auto: - self._scales_buy_more(self.I_SCA_PICTURE_BOOK, buy_res_number) - else: - self._scales_buy_sea_more(self.I_SCA_PICTURE_BOOK, buy_res_number) + self._scales_buy_sea_more(self.I_SCA_PICTURE_BOOK, buy_res_number) time.sleep(0.5) diff --git a/tasks/WantedQuests/assets.py b/tasks/WantedQuests/assets.py index d91a593bc4..632d619257 100644 --- a/tasks/WantedQuests/assets.py +++ b/tasks/WantedQuests/assets.py @@ -21,11 +21,11 @@ class WantedQuestsAssets: # Image Rule Assets # description - I_WQ_INVITE_1 = RuleImage(roi_front=(137,361,39,47), roi_back=(108,338,100,100), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/invite_wq_invite_1.png") + I_WQ_INVITE_1 = RuleImage(roi_front=(103,397,37,38), roi_back=(79,366,146,104), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/invite_wq_invite_1.png") # description - I_WQ_INVITE_2 = RuleImage(roi_front=(462,361,37,47), roi_back=(435,336,100,100), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/invite_wq_invite_2.png") + I_WQ_INVITE_2 = RuleImage(roi_front=(386,399,31,35), roi_back=(355,368,157,108), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/invite_wq_invite_2.png") # description - I_WQ_INVITE_3 = RuleImage(roi_front=(754,366,39,42), roi_back=(728,339,100,100), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/invite_wq_invite_3.png") + I_WQ_INVITE_3 = RuleImage(roi_front=(671,402,37,37), roi_back=(640,368,176,105), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/invite_wq_invite_3.png") # description I_WQ_FRIEND_1 = RuleImage(roi_front=(240,185,180,80), roi_back=(240,185,180,80), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/invite_wq_friend_1.png") # description @@ -51,35 +51,35 @@ class WantedQuestsAssets: # 邀请跨服好友,已选中状态,高亮状态 I_WQ_INVITE_DIFF_SVR_HIGHLIGHT = RuleImage(roi_front=(260,70,120,90), roi_back=(260,70,120,90), threshold=0.9, method="Template matching", file="./tasks/WantedQuests/invite/wq_invite_diff_svr_highlight.png") # 从左到右第一个协作任务的类型 - I_WQ_COOPERATION_TYPE_GOLD_1 = RuleImage(roi_front=(195,505,180,90), roi_back=(195,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_gold.png") + I_WQ_COOPERATION_TYPE_GOLD_1 = RuleImage(roi_front=(127,527,180,90), roi_back=(127,527,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_gold.png") # 从左到右第一个协作任务的类型 - I_WQ_COOPERATION_TYPE_JADE_1 = RuleImage(roi_front=(195,505,180,90), roi_back=(195,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_jade.png") + I_WQ_COOPERATION_TYPE_JADE_1 = RuleImage(roi_front=(130,527,180,90), roi_back=(130,527,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_jade.png") # 从左到右第一个协作任务的类型为狗粮 - I_WQ_COOPERATION_TYPE_DOG_FOOD_1 = RuleImage(roi_front=(195,505,180,90), roi_back=(195,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_dog_food.png") + I_WQ_COOPERATION_TYPE_DOG_FOOD_1 = RuleImage(roi_front=(131,526,180,90), roi_back=(131,526,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_dog_food.png") # 从左到右第一个协作任务的类型为猫粮 - I_WQ_COOPERATION_TYPE_CAT_FOOD_1 = RuleImage(roi_front=(195,505,180,90), roi_back=(195,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_cat_food.png") + I_WQ_COOPERATION_TYPE_CAT_FOOD_1 = RuleImage(roi_front=(131,525,180,90), roi_back=(131,525,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_cat_food.png") # 从左到右第一个协作任务的类型 - I_WQ_COOPERATION_TYPE_SUSHI_1 = RuleImage(roi_front=(195,505,180,90), roi_back=(195,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_sushi.png") + I_WQ_COOPERATION_TYPE_SUSHI_1 = RuleImage(roi_front=(130,525,180,90), roi_back=(130,525,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_sushi.png") # 从左到右第二个协作任务的类型 - I_WQ_COOPERATION_TYPE_GOLD_2 = RuleImage(roi_front=(490,505,180,90), roi_back=(490,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_gold.png") + I_WQ_COOPERATION_TYPE_GOLD_2 = RuleImage(roi_front=(412,525,180,90), roi_back=(412,525,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_gold.png") # 从左到右第二个协作任务的类型 - I_WQ_COOPERATION_TYPE_JADE_2 = RuleImage(roi_front=(490,505,180,90), roi_back=(490,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_jade.png") + I_WQ_COOPERATION_TYPE_JADE_2 = RuleImage(roi_front=(413,525,180,90), roi_back=(413,525,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_jade.png") # 从左到右第二个协作任务的类型为狗粮 - I_WQ_COOPERATION_TYPE_DOG_FOOD_2 = RuleImage(roi_front=(490,505,180,90), roi_back=(490,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_dog_food.png") + I_WQ_COOPERATION_TYPE_DOG_FOOD_2 = RuleImage(roi_front=(413,527,179,89), roi_back=(413,527,179,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_dog_food.png") # 从左到右第二个协作任务的类型为猫粮 - I_WQ_COOPERATION_TYPE_CAT_FOOD_2 = RuleImage(roi_front=(490,505,180,90), roi_back=(490,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_cat_food.png") + I_WQ_COOPERATION_TYPE_CAT_FOOD_2 = RuleImage(roi_front=(412,525,180,90), roi_back=(412,525,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_cat_food.png") # 从左到右第二个协作任务的类型 - I_WQ_COOPERATION_TYPE_SUSHI_2 = RuleImage(roi_front=(490,505,180,90), roi_back=(490,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_sushi.png") + I_WQ_COOPERATION_TYPE_SUSHI_2 = RuleImage(roi_front=(413,526,180,90), roi_back=(413,526,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_sushi.png") # 从左到右第三个协作任务的类型 - I_WQ_COOPERATION_TYPE_GOLD_3 = RuleImage(roi_front=(790,505,180,90), roi_back=(790,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_gold.png") + I_WQ_COOPERATION_TYPE_GOLD_3 = RuleImage(roi_front=(693,524,180,90), roi_back=(693,524,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_gold.png") # 从左到右第三个协作任务的类型 - I_WQ_COOPERATION_TYPE_JADE_3 = RuleImage(roi_front=(790,505,180,90), roi_back=(790,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_jade.png") + I_WQ_COOPERATION_TYPE_JADE_3 = RuleImage(roi_front=(693,527,180,90), roi_back=(693,527,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_jade.png") # 从左到右第三个协作任务的类型为狗粮 - I_WQ_COOPERATION_TYPE_DOG_FOOD_3 = RuleImage(roi_front=(790,505,180,90), roi_back=(790,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_dog_food.png") + I_WQ_COOPERATION_TYPE_DOG_FOOD_3 = RuleImage(roi_front=(694,525,180,90), roi_back=(694,525,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_dog_food.png") # 从左到右第三个协作任务的类型为猫粮 - I_WQ_COOPERATION_TYPE_CAT_FOOD_3 = RuleImage(roi_front=(790,505,180,90), roi_back=(790,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_cat_food.png") + I_WQ_COOPERATION_TYPE_CAT_FOOD_3 = RuleImage(roi_front=(693,526,180,90), roi_back=(693,526,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_cat_food.png") # 从左到右第三个协作任务的类型 - I_WQ_COOPERATION_TYPE_SUSHI_3 = RuleImage(roi_front=(790,505,180,90), roi_back=(790,505,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_sushi.png") + I_WQ_COOPERATION_TYPE_SUSHI_3 = RuleImage(roi_front=(692,526,180,90), roi_back=(692,526,180,90), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/invite/wq_cooperation_type_sushi.png") # Ocr Rule Assets @@ -94,8 +94,6 @@ class WantedQuestsAssets: C_SECRET_CHAT = RuleClick(roi_front=(597,296,59,100), roi_back=(597,296,59,100), name="secret_chat") # 特殊的庭院需要点击,然后才能找到这个悬赏 C_SPECIAL_MAIN = RuleClick(roi_front=(409,572,32,30), roi_back=(404,569,41,35), name="special_main") - # 关闭 单个任务追踪界面 所需点击 的 空白区域 - C_WQ_TRACE_ONE_CLOSE = RuleClick(roi_front=(170,30,400,100), roi_back=(170,30,400,100), name="wq_trace_one_close") # Image Rule Assets @@ -104,38 +102,36 @@ class WantedQuestsAssets: # 勾号 I_WQ_DONE = RuleImage(roi_front=(248,183,37,39), roi_back=(63,134,624,401), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_wq_done.png") # 一键追踪 - I_TRACE_ENABLE = RuleImage(roi_front=(1097,588,101,70), roi_back=(1097,588,101,70), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_enable.png") + I_TRACE_ENABLE = RuleImage(roi_front=(1159,606,52,58), roi_back=(1075,518,198,196), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_enable.png") # 取消追踪 - I_TRACE_DISABLE = RuleImage(roi_front=(1091,586,108,70), roi_back=(1091,586,108,70), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_disable.png") + I_TRACE_DISABLE = RuleImage(roi_front=(1154,609,68,58), roi_back=(1065,495,215,225), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_disable.png") # 奖励宝箱 I_WQ_BOX = RuleImage(roi_front=(48,187,43,38), roi_back=(12,78,108,496), threshold=0.7, method="Template matching", file="./tasks/WantedQuests/wq/wq_wq_box.png") # 小号追踪 - I_TRACE_TRUE = RuleImage(roi_front=(173,187,28,29), roi_back=(173,187,28,29), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_true.png") + I_TRACE_TRUE = RuleImage(roi_front=(1148,571,77,69), roi_back=(1035,502,245,213), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_true.png") # 小号不追踪 - I_TRACE_FALSE = RuleImage(roi_front=(170,186,31,32), roi_back=(170,186,31,32), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_false.png") + I_TRACE_FALSE = RuleImage(roi_front=(1152,569,83,79), roi_back=(1051,481,229,239), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_false.png") # description - I_GOTO_1 = RuleImage(roi_front=(978,234,87,45), roi_back=(978,234,87,60), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_goto_1.png") + I_GOTO_1 = RuleImage(roi_front=(1075,287,62,29), roi_back=(1054,266,99,70), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_goto_1.png") # description - I_GOTO_2 = RuleImage(roi_front=(979,305,88,43), roi_back=(979,305,88,58), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_goto_2.png") + I_GOTO_2 = RuleImage(roi_front=(1074,363,60,31), roi_back=(1055,341,99,71), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_goto_2.png") # description - I_GOTO_3 = RuleImage(roi_front=(994,396,58,30), roi_back=(979,373,88,62), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_goto_3.png") + I_GOTO_3 = RuleImage(roi_front=(1074,439,56,29), roi_back=(1053,417,101,73), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_goto_3.png") # description - I_GOTO_4 = RuleImage(roi_front=(979,447,87,42), roi_back=(979,447,87,57), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_goto_4.png") - # 判断是否还有任务 - I_WQ_CHECK_TASK = RuleImage(roi_front=(110,154,21,125), roi_back=(73,122,69,459), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_wq_check_task.png") + I_GOTO_4 = RuleImage(roi_front=(1078,516,55,27), roi_back=(1054,488,100,76), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_goto_4.png") # 庭院悬赏任务界面 单个任务点开后 左上角是否追踪 - I_WQ_TRACE_ONE_ENABLE = RuleImage(roi_front=(160,175,120,60), roi_back=(160,175,120,60), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_one_enable.png") + I_WQ_TRACE_ONE_ENABLE = RuleImage(roi_front=(1150,570,79,75), roi_back=(1047,500,233,220), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_one_enable.png") # 庭院悬赏任务界面 单个任务点开后 左上角是否追踪 - I_WQ_TRACE_ONE_DISABLE = RuleImage(roi_front=(160,175,120,60), roi_back=(160,175,120,60), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_one_disable.png") + I_WQ_TRACE_ONE_DISABLE = RuleImage(roi_front=(1156,570,77,67), roi_back=(1055,488,225,232), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_one_disable.png") # 庭院悬赏任务界面 单个任务点开后的界面 是否打开的 标志 - I_WQ_TRACE_ONE_CHECK_OPENED = RuleImage(roi_front=(510,175,210,60), roi_back=(510,175,210,60), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_one_check_opened.png") + I_WQ_TRACE_ONE_CHECK_OPENED = RuleImage(roi_front=(778,166,52,29), roi_back=(582,147,431,110), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_one_check_opened.png") # 庭院悬赏任务界面 单个任务点开后的界面 是否为现世任务标志 - I_WQ_TRACE_ONE_REALWORLD = RuleImage(roi_front=(850,470,180,180), roi_back=(850,470,180,180), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_one_realworld.png") + I_WQ_TRACE_ONE_REALWORLD = RuleImage(roi_front=(1173,579,51,54), roi_back=(1037,517,243,203), threshold=0.8, method="Template matching", file="./tasks/WantedQuests/wq/wq_trace_one_realworld.png") # Ocr Rule Assets # 挑战券的数量 - O_WQ_NUMBER = RuleOcr(roi=(569,13,50,32), area=(569,13,50,32), mode="Digit", method="Default", keyword="", name="wq_number") + O_WQ_NUMBER = RuleOcr(roi=(541,14,97,33), area=(541,14,97,33), mode="Digit", method="Default", keyword="", name="wq_number") # 悬赏封印 O_WQ_WANTED = RuleOcr(roi=(15,116,85,401), area=(15,116,85,401), mode="Full", method="Default", keyword="", name="wq_wanted") # Ocr-description @@ -151,23 +147,23 @@ class WantedQuestsAssets: # 备用识别2 O_WQ_NUM_UNKNOWN_2 = RuleOcr(roi=(34,406,65,24), area=(34,406,65,24), mode="Digit", method="Default", keyword="", name="wq_num_unknown_2") # Ocr-description - O_WQ_TYPE_1 = RuleOcr(roi=(544,248,55,36), area=(544,248,55,36), mode="Single", method="Default", keyword="", name="wq_type_1") + O_WQ_TYPE_1 = RuleOcr(roi=(645,283,55,36), area=(645,283,55,36), mode="Single", method="Default", keyword="", name="wq_type_1") # Ocr-description - O_WQ_TYPE_2 = RuleOcr(roi=(544,321,55,30), area=(544,321,55,30), mode="Single", method="Default", keyword="", name="wq_type_2") + O_WQ_TYPE_2 = RuleOcr(roi=(647,361,55,30), area=(647,361,55,30), mode="Single", method="Default", keyword="", name="wq_type_2") # Ocr-description - O_WQ_TYPE_3 = RuleOcr(roi=(544,392,54,30), area=(544,392,54,30), mode="Single", method="Default", keyword="", name="wq_type_3") + O_WQ_TYPE_3 = RuleOcr(roi=(648,437,54,30), area=(648,437,54,30), mode="Single", method="Default", keyword="", name="wq_type_3") # Ocr-description - O_WQ_TYPE_4 = RuleOcr(roi=(545,462,52,32), area=(545,462,52,32), mode="Single", method="Default", keyword="", name="wq_type_4") + O_WQ_TYPE_4 = RuleOcr(roi=(647,512,52,32), area=(647,512,52,32), mode="Single", method="Default", keyword="", name="wq_type_4") # Ocr-description - O_WQ_INFO_1 = RuleOcr(roi=(609,246,309,45), area=(609,246,309,45), mode="Single", method="Default", keyword="", name="wq_info_1") + O_WQ_INFO_1 = RuleOcr(roi=(710,279,309,45), area=(710,279,309,45), mode="Single", method="Default", keyword="", name="wq_info_1") # Ocr-description - O_WQ_INFO_2 = RuleOcr(roi=(612,318,298,41), area=(612,318,298,41), mode="Single", method="Default", keyword="", name="wq_info_2") + O_WQ_INFO_2 = RuleOcr(roi=(712,358,298,41), area=(712,358,298,41), mode="Single", method="Default", keyword="", name="wq_info_2") # Ocr-description - O_WQ_INFO_3 = RuleOcr(roi=(612,386,305,44), area=(612,386,305,44), mode="Single", method="Default", keyword="", name="wq_info_3") + O_WQ_INFO_3 = RuleOcr(roi=(710,430,305,44), area=(710,429,305,44), mode="Single", method="Default", keyword="", name="wq_info_3") # Ocr-description - O_WQ_INFO_4 = RuleOcr(roi=(613,456,315,44), area=(613,456,315,44), mode="Single", method="Default", keyword="", name="wq_info_4") + O_WQ_INFO_4 = RuleOcr(roi=(711,506,315,44), area=(711,507,315,44), mode="Single", method="Default", keyword="", name="wq_info_4") # 怪物名称,如酒吞,阎魔等 - O_WQ_MONSTER_TYPE = RuleOcr(roi=(165,215,45,175), area=(165,215,45,175), mode="Full", method="Default", keyword="", name="wq_monster_type") + O_WQ_MONSTER_TYPE = RuleOcr(roi=(634,154,483,52), area=(634,154,483,52), mode="Full", method="Default", keyword="", name="wq_monster_type") # O_WQ_TEXT_ALL = RuleOcr(roi=(12,119,88,393), area=(12,119,88,393), mode="Full", method="Default", keyword="", name="wq_text_all") diff --git a/tasks/WantedQuests/invite/image.json b/tasks/WantedQuests/invite/image.json index 8fd2a2eb1d..d4534093c7 100644 --- a/tasks/WantedQuests/invite/image.json +++ b/tasks/WantedQuests/invite/image.json @@ -2,8 +2,8 @@ { "itemName": "wq_invite_1", "imageName": "invite_wq_invite_1.png", - "roiFront": "137,361,39,47", - "roiBack": "108,338,100,100", + "roiFront": "103,397,37,38", + "roiBack": "79,366,146,104", "method": "Template matching", "threshold": 0.8, "description": "description" @@ -11,8 +11,8 @@ { "itemName": "wq_invite_2", "imageName": "invite_wq_invite_2.png", - "roiFront": "462,361,37,47", - "roiBack": "435,336,100,100", + "roiFront": "386,399,31,35", + "roiBack": "355,368,157,108", "method": "Template matching", "threshold": 0.8, "description": "description" @@ -20,8 +20,8 @@ { "itemName": "wq_invite_3", "imageName": "invite_wq_invite_3.png", - "roiFront": "754,366,39,42", - "roiBack": "728,339,100,100", + "roiFront": "671,402,37,37", + "roiBack": "640,368,176,105", "method": "Template matching", "threshold": 0.8, "description": "description" @@ -137,8 +137,8 @@ { "itemName": "wq_cooperation_type_gold_1", "imageName": "wq_cooperation_type_gold.png", - "roiFront": "195,505,180,90", - "roiBack": "195,505,180,90", + "roiFront": "127,527,180,90", + "roiBack": "127,527,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第一个协作任务的类型" @@ -146,8 +146,8 @@ { "itemName": "wq_cooperation_type_jade_1", "imageName": "wq_cooperation_type_jade.png", - "roiFront": "195,505,180,90", - "roiBack": "195,505,180,90", + "roiFront": "130,527,180,90", + "roiBack": "130,527,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第一个协作任务的类型" @@ -155,8 +155,8 @@ { "itemName": "wq_cooperation_type_dog_food_1", "imageName": "wq_cooperation_type_dog_food.png", - "roiFront": "195,505,180,90", - "roiBack": "195,505,180,90", + "roiFront": "131,526,180,90", + "roiBack": "131,526,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第一个协作任务的类型为狗粮" @@ -164,8 +164,8 @@ { "itemName": "wq_cooperation_type_cat_food_1", "imageName": "wq_cooperation_type_cat_food.png", - "roiFront": "195,505,180,90", - "roiBack": "195,505,180,90", + "roiFront": "131,525,180,90", + "roiBack": "131,525,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第一个协作任务的类型为猫粮" @@ -173,8 +173,8 @@ { "itemName": "wq_cooperation_type_sushi_1", "imageName": "wq_cooperation_type_sushi.png", - "roiFront": "195,505,180,90", - "roiBack": "195,505,180,90", + "roiFront": "130,525,180,90", + "roiBack": "130,525,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第一个协作任务的类型" @@ -182,8 +182,8 @@ { "itemName": "wq_cooperation_type_gold_2", "imageName": "wq_cooperation_type_gold.png", - "roiFront": "490,505,180,90", - "roiBack": "490,505,180,90", + "roiFront": "412,525,180,90", + "roiBack": "412,525,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第二个协作任务的类型" @@ -191,8 +191,8 @@ { "itemName": "wq_cooperation_type_jade_2", "imageName": "wq_cooperation_type_jade.png", - "roiFront": "490,505,180,90", - "roiBack": "490,505,180,90", + "roiFront": "413,525,180,90", + "roiBack": "413,525,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第二个协作任务的类型" @@ -200,8 +200,8 @@ { "itemName": "wq_cooperation_type_dog_food_2", "imageName": "wq_cooperation_type_dog_food.png", - "roiFront": "490,505,180,90", - "roiBack": "490,505,180,90", + "roiFront": "413,527,179,89", + "roiBack": "413,527,179,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第二个协作任务的类型为狗粮" @@ -209,8 +209,8 @@ { "itemName": "wq_cooperation_type_cat_food_2", "imageName": "wq_cooperation_type_cat_food.png", - "roiFront": "490,505,180,90", - "roiBack": "490,505,180,90", + "roiFront": "412,525,180,90", + "roiBack": "412,525,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第二个协作任务的类型为猫粮" @@ -218,8 +218,8 @@ { "itemName": "wq_cooperation_type_sushi_2", "imageName": "wq_cooperation_type_sushi.png", - "roiFront": "490,505,180,90", - "roiBack": "490,505,180,90", + "roiFront": "413,526,180,90", + "roiBack": "413,526,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第二个协作任务的类型" @@ -227,8 +227,8 @@ { "itemName": "wq_cooperation_type_gold_3", "imageName": "wq_cooperation_type_gold.png", - "roiFront": "790,505,180,90", - "roiBack": "790,505,180,90", + "roiFront": "693,524,180,90", + "roiBack": "693,524,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第三个协作任务的类型" @@ -236,8 +236,8 @@ { "itemName": "wq_cooperation_type_jade_3", "imageName": "wq_cooperation_type_jade.png", - "roiFront": "790,505,180,90", - "roiBack": "790,505,180,90", + "roiFront": "693,527,180,90", + "roiBack": "693,527,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第三个协作任务的类型" @@ -245,8 +245,8 @@ { "itemName": "wq_cooperation_type_dog_food_3", "imageName": "wq_cooperation_type_dog_food.png", - "roiFront": "790,505,180,90", - "roiBack": "790,505,180,90", + "roiFront": "694,525,180,90", + "roiBack": "694,525,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第三个协作任务的类型为狗粮" @@ -254,8 +254,8 @@ { "itemName": "wq_cooperation_type_cat_food_3", "imageName": "wq_cooperation_type_cat_food.png", - "roiFront": "790,505,180,90", - "roiBack": "790,505,180,90", + "roiFront": "693,526,180,90", + "roiBack": "693,526,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第三个协作任务的类型为猫粮" @@ -263,8 +263,8 @@ { "itemName": "wq_cooperation_type_sushi_3", "imageName": "wq_cooperation_type_sushi.png", - "roiFront": "790,505,180,90", - "roiBack": "790,505,180,90", + "roiFront": "692,526,180,90", + "roiBack": "692,526,180,90", "method": "Template matching", "threshold": 0.8, "description": "从左到右第三个协作任务的类型" diff --git a/tasks/WantedQuests/invite/invite_wq_invite_1.png b/tasks/WantedQuests/invite/invite_wq_invite_1.png index 0f55d4f2a3..9dd249a9ec 100644 Binary files a/tasks/WantedQuests/invite/invite_wq_invite_1.png and b/tasks/WantedQuests/invite/invite_wq_invite_1.png differ diff --git a/tasks/WantedQuests/invite/invite_wq_invite_2.png b/tasks/WantedQuests/invite/invite_wq_invite_2.png index 73f1733907..5e82c405fd 100644 Binary files a/tasks/WantedQuests/invite/invite_wq_invite_2.png and b/tasks/WantedQuests/invite/invite_wq_invite_2.png differ diff --git a/tasks/WantedQuests/invite/invite_wq_invite_3.png b/tasks/WantedQuests/invite/invite_wq_invite_3.png index b9756f417a..65a26efad9 100644 Binary files a/tasks/WantedQuests/invite/invite_wq_invite_3.png and b/tasks/WantedQuests/invite/invite_wq_invite_3.png differ diff --git a/tasks/WantedQuests/script_task.py b/tasks/WantedQuests/script_task.py index 1763440361..ad7b243cc2 100644 --- a/tasks/WantedQuests/script_task.py +++ b/tasks/WantedQuests/script_task.py @@ -225,7 +225,7 @@ def trace_one(self, btn: RuleImage): # 防止点击后界面来不及刷新 sleep(1.5) # 关闭单个任务的追踪界面 - self.ui_click_until_smt_disappear(self.C_WQ_TRACE_ONE_CLOSE, stop=self.I_WQ_TRACE_ONE_CHECK_OPENED, + self.ui_click_until_smt_disappear(self.I_UI_BACK_RED, stop=self.I_WQ_TRACE_ONE_CHECK_OPENED, interval=1.5) def execute_mission(self, ocr, num_want: int, num_challenge: int): @@ -308,7 +308,8 @@ def extract_info(index: int) -> tuple or None: return False # 跳过不想打的 monster_name = self.O_WQ_MONSTER_TYPE.detect_text(self.device.image) - if monster_name in self.unwanted_boss_name_list: + # 识别串可能带前缀动词(如需要击败/击杀), 用子串包含匹配而非整串相等 + if any(name in monster_name for name in self.unwanted_boss_name_list): # logger.warning(f'unwanted {monster_name}') self.ui_click(self.I_TRACE_TRUE, self.I_TRACE_FALSE) diff --git a/tasks/WantedQuests/wq/click.json b/tasks/WantedQuests/wq/click.json index e16094c573..4efec77f6f 100644 --- a/tasks/WantedQuests/wq/click.json +++ b/tasks/WantedQuests/wq/click.json @@ -10,11 +10,5 @@ "roiFront": "409,572,32,30", "roiBack": "404,569,41,35", "description": "特殊的庭院需要点击,然后才能找到这个悬赏" - }, - { - "itemName": "wq_trace_one_close", - "roiFront": "170,30,400,100", - "roiBack": "170,30,400,100", - "description": "关闭 单个任务追踪界面 所需点击 的 空白区域" } ] \ No newline at end of file diff --git a/tasks/WantedQuests/wq/image.json b/tasks/WantedQuests/wq/image.json index 45fc829dcf..6e5b801e52 100644 --- a/tasks/WantedQuests/wq/image.json +++ b/tasks/WantedQuests/wq/image.json @@ -20,8 +20,8 @@ { "itemName": "trace_enable", "imageName": "wq_trace_enable.png", - "roiFront": "1097,588,101,70", - "roiBack": "1097,588,101,70", + "roiFront": "1159,606,52,58", + "roiBack": "1075,518,198,196", "method": "Template matching", "threshold": 0.8, "description": "一键追踪" @@ -29,8 +29,8 @@ { "itemName": "trace_disable", "imageName": "wq_trace_disable.png", - "roiFront": "1091,586,108,70", - "roiBack": "1091,586,108,70", + "roiFront": "1154,609,68,58", + "roiBack": "1065,495,215,225", "method": "Template matching", "threshold": 0.8, "description": "取消追踪" @@ -47,8 +47,8 @@ { "itemName": "trace_true", "imageName": "wq_trace_true.png", - "roiFront": "173,187,28,29", - "roiBack": "173,187,28,29", + "roiFront": "1148,571,77,69", + "roiBack": "1035,502,245,213", "method": "Template matching", "threshold": 0.8, "description": "小号追踪" @@ -56,8 +56,8 @@ { "itemName": "trace_false", "imageName": "wq_trace_false.png", - "roiFront": "170,186,31,32", - "roiBack": "170,186,31,32", + "roiFront": "1152,569,83,79", + "roiBack": "1051,481,229,239", "method": "Template matching", "threshold": 0.8, "description": "小号不追踪" @@ -65,8 +65,8 @@ { "itemName": "goto_1", "imageName": "wq_goto_1.png", - "roiFront": "978,234,87,45", - "roiBack": "978,234,87,60", + "roiFront": "1075,287,62,29", + "roiBack": "1054,266,99,70", "method": "Template matching", "threshold": 0.8, "description": "description" @@ -74,8 +74,8 @@ { "itemName": "goto_2", "imageName": "wq_goto_2.png", - "roiFront": "979,305,88,43", - "roiBack": "979,305,88,58", + "roiFront": "1074,363,60,31", + "roiBack": "1055,341,99,71", "method": "Template matching", "threshold": 0.8, "description": "description" @@ -83,8 +83,8 @@ { "itemName": "goto_3", "imageName": "wq_goto_3.png", - "roiFront": "994,396,58,30", - "roiBack": "979,373,88,62", + "roiFront": "1074,439,56,29", + "roiBack": "1053,417,101,73", "method": "Template matching", "threshold": 0.8, "description": "description" @@ -92,26 +92,17 @@ { "itemName": "goto_4", "imageName": "wq_goto_4.png", - "roiFront": "979,447,87,42", - "roiBack": "979,447,87,57", + "roiFront": "1078,516,55,27", + "roiBack": "1054,488,100,76", "method": "Template matching", "threshold": 0.8, "description": "description" }, - { - "itemName": "wq_check_task", - "imageName": "wq_wq_check_task.png", - "roiFront": "110,154,21,125", - "roiBack": "73,122,69,459", - "method": "Template matching", - "threshold": 0.8, - "description": "判断是否还有任务" - }, { "itemName": "wq_trace_one_enable", "imageName": "wq_trace_one_enable.png", - "roiFront": "160,175,120,60", - "roiBack": "160,175,120,60", + "roiFront": "1150,570,79,75", + "roiBack": "1047,500,233,220", "method": "Template matching", "threshold": 0.8, "description": "庭院悬赏任务界面 单个任务点开后 左上角是否追踪" @@ -119,8 +110,8 @@ { "itemName": "wq_trace_one_disable", "imageName": "wq_trace_one_disable.png", - "roiFront": "160,175,120,60", - "roiBack": "160,175,120,60", + "roiFront": "1156,570,77,67", + "roiBack": "1055,488,225,232", "method": "Template matching", "threshold": 0.8, "description": "庭院悬赏任务界面 单个任务点开后 左上角是否追踪" @@ -128,8 +119,8 @@ { "itemName": "wq_trace_one_check_opened", "imageName": "wq_trace_one_check_opened.png", - "roiFront": "510,175,210,60", - "roiBack": "510,175,210,60", + "roiFront": "778,166,52,29", + "roiBack": "582,147,431,110", "method": "Template matching", "threshold": 0.8, "description": "庭院悬赏任务界面 单个任务点开后的界面 是否打开的 标志" @@ -137,8 +128,8 @@ { "itemName": "wq_trace_one_realworld", "imageName": "wq_trace_one_realworld.png", - "roiFront": "850,470,180,180", - "roiBack": "850,470,180,180", + "roiFront": "1173,579,51,54", + "roiBack": "1037,517,243,203", "method": "Template matching", "threshold": 0.8, "description": "庭院悬赏任务界面 单个任务点开后的界面 是否为现世任务标志" diff --git a/tasks/WantedQuests/wq/ocr.json b/tasks/WantedQuests/wq/ocr.json index 13bc1decfd..2433d10080 100644 --- a/tasks/WantedQuests/wq/ocr.json +++ b/tasks/WantedQuests/wq/ocr.json @@ -1,8 +1,8 @@ [ { "itemName": "wq_number", - "roiFront": "569,13,50,32", - "roiBack": "569,13,50,32", + "roiFront": "541,14,97,33", + "roiBack": "541,14,97,33", "mode": "Digit", "method": "Default", "keyword": "", @@ -73,8 +73,8 @@ }, { "itemName": "wq_type_1", - "roiFront": "544,248,55,36", - "roiBack": "544,248,55,36", + "roiFront": "645,283,55,36", + "roiBack": "645,283,55,36", "mode": "Single", "method": "Default", "keyword": "", @@ -82,8 +82,8 @@ }, { "itemName": "wq_type_2", - "roiFront": "544,321,55,30", - "roiBack": "544,321,55,30", + "roiFront": "647,361,55,30", + "roiBack": "647,361,55,30", "mode": "Single", "method": "Default", "keyword": "", @@ -91,8 +91,8 @@ }, { "itemName": "wq_type_3", - "roiFront": "544,392,54,30", - "roiBack": "544,392,54,30", + "roiFront": "648,437,54,30", + "roiBack": "648,437,54,30", "mode": "Single", "method": "Default", "keyword": "", @@ -100,8 +100,8 @@ }, { "itemName": "wq_type_4", - "roiFront": "545,462,52,32", - "roiBack": "545,462,52,32", + "roiFront": "647,512,52,32", + "roiBack": "647,512,52,32", "mode": "Single", "method": "Default", "keyword": "", @@ -109,8 +109,8 @@ }, { "itemName": "wq_info_1", - "roiFront": "609,246,309,45", - "roiBack": "609,246,309,45", + "roiFront": "710,279,309,45", + "roiBack": "710,279,309,45", "mode": "Single", "method": "Default", "keyword": "", @@ -118,8 +118,8 @@ }, { "itemName": "wq_info_2", - "roiFront": "612,318,298,41", - "roiBack": "612,318,298,41", + "roiFront": "712,358,298,41", + "roiBack": "712,358,298,41", "mode": "Single", "method": "Default", "keyword": "", @@ -127,8 +127,8 @@ }, { "itemName": "wq_info_3", - "roiFront": "612,386,305,44", - "roiBack": "612,386,305,44", + "roiFront": "710,430,305,44", + "roiBack": "710,429,305,44", "mode": "Single", "method": "Default", "keyword": "", @@ -136,8 +136,8 @@ }, { "itemName": "wq_info_4", - "roiFront": "613,456,315,44", - "roiBack": "613,456,315,44", + "roiFront": "711,506,315,44", + "roiBack": "711,507,315,44", "mode": "Single", "method": "Default", "keyword": "", @@ -145,8 +145,8 @@ }, { "itemName": "wq_monster_type", - "roiFront": "165,215,45,175", - "roiBack": "165,215,45,175", + "roiFront": "634,154,483,52", + "roiBack": "634,154,483,52", "mode": "Full", "method": "Default", "keyword": "", diff --git a/tasks/WantedQuests/wq/wq_goto_1.png b/tasks/WantedQuests/wq/wq_goto_1.png index 55df6f050e..32252e42e8 100644 Binary files a/tasks/WantedQuests/wq/wq_goto_1.png and b/tasks/WantedQuests/wq/wq_goto_1.png differ diff --git a/tasks/WantedQuests/wq/wq_goto_2.png b/tasks/WantedQuests/wq/wq_goto_2.png index 878729547a..0893c46d07 100644 Binary files a/tasks/WantedQuests/wq/wq_goto_2.png and b/tasks/WantedQuests/wq/wq_goto_2.png differ diff --git a/tasks/WantedQuests/wq/wq_goto_3.png b/tasks/WantedQuests/wq/wq_goto_3.png index 9bdc8083cb..7c85bc9b82 100644 Binary files a/tasks/WantedQuests/wq/wq_goto_3.png and b/tasks/WantedQuests/wq/wq_goto_3.png differ diff --git a/tasks/WantedQuests/wq/wq_goto_4.png b/tasks/WantedQuests/wq/wq_goto_4.png index b21abdf1c6..a146df0020 100644 Binary files a/tasks/WantedQuests/wq/wq_goto_4.png and b/tasks/WantedQuests/wq/wq_goto_4.png differ diff --git a/tasks/WantedQuests/wq/wq_tarce_disenable.png b/tasks/WantedQuests/wq/wq_tarce_disenable.png deleted file mode 100644 index de4810ec6e..0000000000 Binary files a/tasks/WantedQuests/wq/wq_tarce_disenable.png and /dev/null differ diff --git a/tasks/WantedQuests/wq/wq_trace_disable.png b/tasks/WantedQuests/wq/wq_trace_disable.png index de4810ec6e..71012a4ebf 100644 Binary files a/tasks/WantedQuests/wq/wq_trace_disable.png and b/tasks/WantedQuests/wq/wq_trace_disable.png differ diff --git a/tasks/WantedQuests/wq/wq_trace_enable.png b/tasks/WantedQuests/wq/wq_trace_enable.png index df6ec27dfb..eda7463688 100644 Binary files a/tasks/WantedQuests/wq/wq_trace_enable.png and b/tasks/WantedQuests/wq/wq_trace_enable.png differ diff --git a/tasks/WantedQuests/wq/wq_trace_false.png b/tasks/WantedQuests/wq/wq_trace_false.png index 823d6b7a83..60db48bddb 100644 Binary files a/tasks/WantedQuests/wq/wq_trace_false.png and b/tasks/WantedQuests/wq/wq_trace_false.png differ diff --git a/tasks/WantedQuests/wq/wq_trace_one_check_opened.png b/tasks/WantedQuests/wq/wq_trace_one_check_opened.png index b8a137803f..dfb745eaf9 100644 Binary files a/tasks/WantedQuests/wq/wq_trace_one_check_opened.png and b/tasks/WantedQuests/wq/wq_trace_one_check_opened.png differ diff --git a/tasks/WantedQuests/wq/wq_trace_one_disable.png b/tasks/WantedQuests/wq/wq_trace_one_disable.png index 138ed73939..a4d2dc896e 100644 Binary files a/tasks/WantedQuests/wq/wq_trace_one_disable.png and b/tasks/WantedQuests/wq/wq_trace_one_disable.png differ diff --git a/tasks/WantedQuests/wq/wq_trace_one_enable.png b/tasks/WantedQuests/wq/wq_trace_one_enable.png index b7c833dc3b..32fd4c29b0 100644 Binary files a/tasks/WantedQuests/wq/wq_trace_one_enable.png and b/tasks/WantedQuests/wq/wq_trace_one_enable.png differ diff --git a/tasks/WantedQuests/wq/wq_trace_one_realworld.png b/tasks/WantedQuests/wq/wq_trace_one_realworld.png index dc2cd93b65..73b3b5a2dd 100644 Binary files a/tasks/WantedQuests/wq/wq_trace_one_realworld.png and b/tasks/WantedQuests/wq/wq_trace_one_realworld.png differ diff --git a/tasks/WantedQuests/wq/wq_trace_true.png b/tasks/WantedQuests/wq/wq_trace_true.png index 1de3b3ae0c..07907bd36d 100644 Binary files a/tasks/WantedQuests/wq/wq_trace_true.png and b/tasks/WantedQuests/wq/wq_trace_true.png differ diff --git a/tasks/WantedQuests/wq/wq_wq_check_task.png b/tasks/WantedQuests/wq/wq_wq_check_task.png deleted file mode 100644 index 0b8a63e391..0000000000 Binary files a/tasks/WantedQuests/wq/wq_wq_check_task.png and /dev/null differ diff --git a/tests/test_mall_appearance.py b/tests/test_mall_appearance.py new file mode 100644 index 0000000000..f91bc90e0a --- /dev/null +++ b/tests/test_mall_appearance.py @@ -0,0 +1,150 @@ +"""Check both mall appearances and return paths without a device or OCR service.""" + +import unittest +from types import SimpleNamespace +from unittest.mock import patch + +import numpy as np + +from module.exception import GamePageUnknownError +from tasks.DailyTrifles.assets import DailyTriflesAssets +from tasks.DailyTrifles.page import page_store_gift_room +from tasks.DailyTrifles.script_task import ScriptTask +from tasks.GameUi.assets import GameUiAssets +from tasks.GameUi.game_ui import GameUi +from tasks.GameUi.page import page_main, page_mall, page_mall_recommend +from tasks.GameUi.session import NavigatorSession +from tasks.GlobalGame.assets import GlobalGameAssets +from tasks.RichMan.mall.navbar import MallNavbar + + +def frame_with(*rules): + """Build a synthetic frame using the unchanged asset positions.""" + frame = np.zeros((720, 1280, 3), dtype=np.uint8) + for rule in rules: + template = rule.image + x, y = rule.roi_front[:2] + height, width = template.shape[:2] + frame[y:y + height, x:x + width] = template + return frame + + +class OfflineMallUi(GameUi): + def __init__(self, mall_rule, state="mall"): + # Skip real Config/Device setup; keep the production matcher and navigator. + back = GlobalGameAssets.I_UI_BACK_YELLOW + self.frames = { + "mall": frame_with(mall_rule, back), + "main": frame_with(GameUiAssets.I_CHECK_MAIN, GameUiAssets.I_MAIN_GOTO_MALL), + "recommend": frame_with(GameUiAssets.I_CHECK_MALL_RECOMMEND, back), + "gift": frame_with(DailyTriflesAssets.I_GIFT_RECOMMEND, back), + "room": frame_with(back), + "blank": frame_with(), + } + self.state = state + self.after_sign = "gift" + self.clicks = [] + self.screenshot_count = 0 + self.interval_timer = {} + self.config = SimpleNamespace( + daily_trifles=SimpleNamespace( + trifles_config=SimpleNamespace(store_sign=True, buy_sushi_count=-1), + ), + script=SimpleNamespace(device=SimpleNamespace(control_method="minitouch")), + ) + self.device = SimpleNamespace( + image=self.frames[state], + app_lost_login=lambda: False, + app_is_running=lambda: True, + get_orientation=lambda: None, + ) + self.navigator = NavigatorSession(task_category="daily_trifles") + self.navigator.bootstrap([page_main, page_mall, page_mall_recommend, page_store_gift_room]) + + def screenshot(self): + self.screenshot_count += 1 + if self.screenshot_count > 100: + raise AssertionError("Mall navigation kept retrying") + self.device.image = self.frames[self.state] + return self.device.image + + def appear_then_click(self, target, **kwargs): + if not self.appear(target): + return False + self.clicks.append(target) + if target is self.I_UI_BACK_YELLOW: + self.state = "main" if self.state == "mall" else "mall" + elif target is self.I_MAIN_GOTO_MALL: + self.state = "recommend" + else: + raise AssertionError(f"Unexpected click: {target}") + return True + + def run_store_sign(self): + # Leave the cached mall screenshot stale to check that return takes a new one. + self.state = self.after_sign + + +class MallAppearanceTests(unittest.TestCase): + variants = (GameUiAssets.I_CHECK_MALL, GameUiAssets.I_CHECK_MALL_PURPLE) + + def setUp(self): + for target in ("tasks.GameUi.navigator.Timer.wait", "tasks.GameUi.navigator.time.sleep"): + patcher = patch(target) + patcher.start() + self.addCleanup(patcher.stop) + + def test_both_appearances_are_recognized_but_non_mall_is_not(self): + for rule in self.variants: + with self.subTest(rule=rule.name): + task = OfflineMallUi(rule) + self.assertTrue(task.confirm_page(page_mall)) + task.state = "main" + self.assertFalse(task.confirm_page(page_mall, skip_first_screenshot=False)) + + def test_entering_from_main_stops_at_either_mall_appearance(self): + for rule in self.variants: + with self.subTest(rule=rule.name): + task = OfflineMallUi(rule, state="main") + self.assertTrue(task.goto_page(page_mall)) + self.assertEqual(task.state, "mall") + self.assertEqual(task.clicks, [task.I_MAIN_GOTO_MALL, task.I_UI_BACK_YELLOW]) + + def test_return_does_not_click_back_when_already_in_mall(self): + for rule in self.variants: + with self.subTest(rule=rule.name): + task = OfflineMallUi(rule) + MallNavbar.back_mall(task) + self.assertEqual(task.state, "mall") + self.assertEqual(task.clicks, []) + + def test_daily_trifles_returns_from_gift_room_then_exits_to_main(self): + for rule in self.variants: + with self.subTest(rule=rule.name): + task = OfflineMallUi(rule) + ScriptTask.run_store(task) + self.assertEqual(task.state, "main") + self.assertEqual(task.clicks, [task.I_UI_BACK_YELLOW] * 2) + + def test_rich_man_returns_from_an_unregistered_shop_room(self): + for rule in self.variants: + with self.subTest(rule=rule.name): + task = OfflineMallUi(rule, state="room") + MallNavbar.back_mall(task) + self.assertEqual(task.state, "mall") + self.assertEqual(task.clicks, [task.I_UI_BACK_YELLOW]) + + def test_unrecognized_return_page_raises_instead_of_finishing(self): + for caller in (ScriptTask.run_store, MallNavbar.back_mall): + with self.subTest(caller=caller.__qualname__): + task = OfflineMallUi(self.variants[1], state="mall" if caller is ScriptTask.run_store else "blank") + task.after_sign = "blank" + with patch("tasks.GameUi.navigator.Timer") as timer: + timer.return_value.start.return_value.reached.return_value = True + with self.assertRaises(GamePageUnknownError): + caller(task) + self.assertEqual(task.clicks, []) + + +if __name__ == "__main__": + unittest.main()