874 lines
34 KiB
Python
874 lines
34 KiB
Python
import time
|
|
from typing import Optional, Tuple
|
|
|
|
import cv2
|
|
|
|
from mower import __rootdir__ as __rootdir__
|
|
from mower.utils import config
|
|
from mower.utils import typealias as tp
|
|
from mower.utils.csleep import MowerExit
|
|
from mower.utils.image import bytes2img, cmatch, cropimg, loadres, thres2
|
|
from mower.utils.log import logger
|
|
from mower.utils.matcher import Matcher
|
|
from mower.utils.number import NumberRecognizer
|
|
from mower.utils.scene import Scene, SceneComment
|
|
from mower.utils.vector import clamp_scope, va
|
|
|
|
from .data import color, template_matching, template_matching_score
|
|
|
|
|
|
class Recognizer:
|
|
def __init__(self, image: Optional[tp.Image] = None) -> None:
|
|
self.w = 1920
|
|
self.h = 1080
|
|
self.num = NumberRecognizer()
|
|
self.clear()
|
|
self.loading_time = 0
|
|
self.LOADING_TIME_LIMIT = 5
|
|
if image is not None:
|
|
self._img = image
|
|
|
|
def clear(self):
|
|
self._img = None
|
|
self._gray = None
|
|
self._hsv = None
|
|
self._matcher = None
|
|
self.scene = Scene.UNDEFINED
|
|
self.animation_stop = False
|
|
|
|
@property
|
|
def img(self):
|
|
if self._img is None:
|
|
self.start()
|
|
return self._img
|
|
|
|
@property
|
|
def gray(self):
|
|
if self._gray is None:
|
|
self._gray = cv2.cvtColor(self.img, cv2.COLOR_RGB2GRAY)
|
|
return self._gray
|
|
|
|
@property
|
|
def hsv(self):
|
|
if self._hsv is None:
|
|
self._hsv = cv2.cvtColor(self.img, cv2.COLOR_RGB2HSV)
|
|
return self._hsv
|
|
|
|
@property
|
|
def matcher(self):
|
|
if self._matcher is None:
|
|
self._matcher = Matcher(self.gray)
|
|
return self._matcher
|
|
|
|
def start(self, screencap: Optional[bytes] = None) -> None:
|
|
"""init with screencap"""
|
|
|
|
retry_times = 5
|
|
while retry_times > 0:
|
|
try:
|
|
if screencap is not None:
|
|
self._img = bytes2img(screencap)
|
|
else:
|
|
self._img = config.device.screencap()
|
|
return
|
|
except cv2.error as e:
|
|
logger.warning(e)
|
|
retry_times -= 1
|
|
time.sleep(1)
|
|
continue
|
|
raise RuntimeError("init Recognizer failed")
|
|
|
|
def update(self) -> None:
|
|
if config.stop_mower.is_set():
|
|
raise MowerExit
|
|
self.clear()
|
|
|
|
def color(self, x: int, y: int) -> tp.Pixel:
|
|
"""get the color of the pixel"""
|
|
return self.img[y][x]
|
|
|
|
def detect_index_scene(self) -> bool:
|
|
res = loadres("index_nav", True)
|
|
img = cropimg(self.gray, ((25, 17), (456, 96)))
|
|
img = thres2(img, 240)
|
|
result = cv2.matchTemplate(img, res, cv2.TM_CCOEFF_NORMED)
|
|
result = result[0][0]
|
|
if result > 0.85:
|
|
logger.debug(result)
|
|
return True
|
|
return False
|
|
|
|
def detect_recruit_agent_scene(self) -> bool:
|
|
for p in [
|
|
"PIONEER",
|
|
"SPECIAL",
|
|
"SNIPER",
|
|
"CASTER",
|
|
"WARRIOR",
|
|
"TANK",
|
|
"MEDIC",
|
|
"SUPPORT",
|
|
]:
|
|
if self.find(f"recruit/profession/{p}", scope=((800, 745), (1140, 850))):
|
|
return True
|
|
return False
|
|
|
|
def check_current_focus(self):
|
|
if config.device.check_current_focus():
|
|
self.update()
|
|
|
|
def check_loading_time(self):
|
|
if self.scene == Scene.CONNECTING:
|
|
self.loading_time += 1
|
|
if self.loading_time > 1:
|
|
logger.debug(f"检测到连续等待{self.loading_time}次")
|
|
else:
|
|
self.loading_time = 0
|
|
if self.loading_time > self.LOADING_TIME_LIMIT:
|
|
logger.info(f"检测到连续等待{self.loading_time}次")
|
|
config.device.exit()
|
|
time.sleep(3)
|
|
self.check_current_focus()
|
|
|
|
def get_scene(self) -> int:
|
|
"""get the current scene in the game"""
|
|
if self.scene != Scene.UNDEFINED:
|
|
return self.scene
|
|
|
|
# 连接中,优先级最高
|
|
if self.find("connecting"):
|
|
self.scene = Scene.CONNECTING
|
|
|
|
# 平均色匹配
|
|
elif self.find("nav_bar"):
|
|
self.scene = Scene.NAVIGATION_BAR
|
|
elif self.find("sanity_charge"):
|
|
self.scene = Scene.SANITY_CHARGE
|
|
elif self.find("sanity_charge_dialog"):
|
|
self.scene = Scene.SANITY_CHARGE_DIALOG
|
|
elif self.find("confirm"):
|
|
self.scene = Scene.CONFIRM
|
|
elif self.find("order_label"):
|
|
self.scene = Scene.ORDER_LIST
|
|
elif self.find("drone"):
|
|
self.scene = Scene.DRONE_ACCELERATE
|
|
elif self.find("factory_collect"):
|
|
self.scene = Scene.FACTORY_ROOMS
|
|
elif self.find("mail/banner"):
|
|
self.scene = Scene.MAIL
|
|
elif self.find("navigation/record_restoration"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("choose_agent/support_status") or self.find(
|
|
"choose_agent/support_agent"
|
|
):
|
|
self.scene = Scene.OPERATOR_SUPPORT
|
|
elif self.find("fight/give_up"):
|
|
self.scene = Scene.OPERATOR_GIVEUP
|
|
elif self.find("fight/collection") or self.find("fight/collection_on"):
|
|
self.scene = Scene.OPERATOR_AGENT_SELECT
|
|
elif self.find("choose_agent/fast_select") or self.find(
|
|
"choose_agent/ope_start"
|
|
):
|
|
if self.find("rogue/action"):
|
|
self.scene = Scene.ROGUE_SELECT
|
|
else:
|
|
self.scene = Scene.OPERATOR_SELECT
|
|
elif self.find("fight/ban"):
|
|
self.scene = Scene.OPERATOR_BAN
|
|
elif self.find("ope_eliminate"):
|
|
self.scene = Scene.OPERATOR_ELIMINATE
|
|
elif self.find("ope_elimi_agency_panel"):
|
|
self.scene = Scene.OPERATOR_ELIMINATE_AGENCY
|
|
elif self.find("riic/report_title"):
|
|
self.scene = Scene.RIIC_REPORT
|
|
elif self.find("control_central_assistants"):
|
|
self.scene = Scene.CTRLCENTER_ASSISTANT
|
|
elif self.find("build_mode"):
|
|
self.scene = Scene.INFRA_MAIN
|
|
elif self.find("infra_todo"):
|
|
self.scene = Scene.INFRA_TODOLIST
|
|
elif self.find("infra_overview_rest") or self.find("infra_overview_work"):
|
|
self.scene = Scene.INFRA_ARRANGE
|
|
elif self.find("arrange_confirm"):
|
|
self.scene = Scene.INFRA_ARRANGE_CONFIRM
|
|
elif self.find("open_recruitment"):
|
|
self.scene = Scene.RECRUIT_MAIN
|
|
elif self.find("recruiting_instructions"):
|
|
self.scene = Scene.RECRUIT_TAGS
|
|
elif self.find("shop/trade_token_dialog"):
|
|
self.scene = Scene.SHOP_TRADE_TOKEN
|
|
elif self.find("shop/credit"):
|
|
self.scene = Scene.SHOP_CREDIT
|
|
elif self.find("shop/token"):
|
|
self.scene = Scene.SHOP_TOKEN
|
|
elif self.find("shop/recommend"):
|
|
self.scene = Scene.SHOP_OTHERS
|
|
elif self.find("shop/recommend_off"):
|
|
self.scene = Scene.SHOP_OTHERS
|
|
elif self.find("shop/cart"):
|
|
self.scene = Scene.SHOP_CREDIT_CONFIRM
|
|
elif self.find("shop/assist.jpg"):
|
|
self.scene = Scene.SHOP_ASSIST
|
|
elif self.find("I_know"):
|
|
self.scene = Scene.LOGIN_ANNOUNCE_NEW
|
|
elif self.find("login_logo") and (
|
|
self.find("hypergryph") or self.find("age_notice")
|
|
):
|
|
if self.find("login_awake"):
|
|
self.scene = Scene.LOGIN_QUICKLY
|
|
elif self.find("login_account"):
|
|
self.scene = Scene.LOGIN_MAIN
|
|
else:
|
|
self.scene = Scene.LOGIN_MAIN_NOENTRY
|
|
elif self.find("12cadpa"):
|
|
self.scene = Scene.LOGIN_START
|
|
elif self.find("login_bilibili"):
|
|
self.scene = Scene.LOGIN_BILIBILI
|
|
elif self.find("skip"):
|
|
self.scene = Scene.SKIP
|
|
elif self.find("login_connecting"):
|
|
self.scene = Scene.LOGIN_LOADING
|
|
elif self.find("choose_agent/riic/work_state") or self.find(
|
|
"choose_agent/riic/work_state_blue"
|
|
):
|
|
self.scene = Scene.RIIC_OPERATOR_SELECT
|
|
elif self.find("ope_recover_potion_on"):
|
|
self.scene = Scene.OPERATOR_RECOVER_POTION
|
|
elif self.find("ope_recover_originite_on", scope=((1530, 120), (1850, 190))):
|
|
self.scene = Scene.OPERATOR_RECOVER_ORIGINITE
|
|
elif self.find("double_confirm/main"):
|
|
if self.find("double_confirm/exit"):
|
|
self.scene = Scene.EXIT_GAME
|
|
elif self.find("double_confirm/friend"):
|
|
self.scene = Scene.BACK_TO_FRIEND_LIST
|
|
elif self.find("double_confirm/give_up"):
|
|
self.scene = Scene.OPERATOR_FAULT_CONFIRM
|
|
elif self.find("double_confirm/infrastructure"):
|
|
self.scene = Scene.LEAVE_INFRASTRUCTURE
|
|
elif self.find("double_confirm/recruit"):
|
|
self.scene = Scene.REFRESH_TAGS
|
|
elif self.find("double_confirm/network"):
|
|
self.scene = Scene.NETWORK_CHECK
|
|
elif self.find("double_confirm/voice"):
|
|
self.scene = Scene.DOWNLOAD_VOICE_RESOURCES
|
|
elif self.find("double_confirm/sss"):
|
|
self.scene = Scene.SSS_EXIT_CONFIRM
|
|
elif self.find("double_confirm/sss_abandon_drop"):
|
|
self.scene = Scene.SSS_ABANDON_DROP_IN_FIGHT
|
|
elif self.find("double_confirm/product_plan"):
|
|
self.scene = Scene.PRODUCT_SWITCHING_CONFIRM
|
|
elif self.find("double_confirm/rogue_recruit"):
|
|
self.scene = Scene.ROGUE_ABANDON_RECRUIT
|
|
elif self.find("double_confirm/explore"):
|
|
self.scene = Scene.ROGUE_ABANDON
|
|
elif self.find("double_confirm/refresh_shop"):
|
|
self.scene = Scene.ROGUE_REFRESH_SHOP
|
|
elif self.find("double_confirm/headhunting"):
|
|
self.scene = Scene.HEADHUNTING_FREE_CONFIRM
|
|
elif self.find("double_confirm/record.jpg"):
|
|
self.scene = Scene.BATTLE_RECORD
|
|
else:
|
|
self.scene = Scene.DOUBLE_CONFIRM
|
|
elif self.find("mission_trainee_on"):
|
|
self.scene = Scene.MISSION_TRAINEE
|
|
elif self.find("shop/spent_credit"):
|
|
self.scene = Scene.SHOP_UNLOCK_SCHEDULE
|
|
elif self.find("loading7"):
|
|
self.scene = Scene.LOADING
|
|
elif self.find("clue/daily"):
|
|
self.scene = Scene.CLUE_DAILY
|
|
elif self.find("clue/receive"):
|
|
self.scene = Scene.CLUE_RECEIVE
|
|
elif self.find("clue/give_away"):
|
|
self.scene = Scene.CLUE_GIVE_AWAY
|
|
elif self.find("clue/summary"):
|
|
self.scene = Scene.CLUE_SUMMARY
|
|
elif self.find("clue/filter_all"):
|
|
self.scene = Scene.CLUE_PLACE
|
|
elif self.find("clue/message_board_banner"):
|
|
self.scene = Scene.CLUE_MESSAGE_BOARD
|
|
elif self.find("clue/message_board_friend"):
|
|
self.scene = Scene.CLUE_MESSAGE_BOARD_FRIEND
|
|
elif self.find("clue/access_records.jpg"):
|
|
self.scene = Scene.CLUE_ACCESS_RECORD
|
|
elif self.find("upgrade"):
|
|
self.scene = Scene.UPGRADE
|
|
elif self.find("restore_all_sanity"):
|
|
self.scene = Scene.RESTORE_ALL_SANITY
|
|
elif self.find("operator/trust"):
|
|
self.scene = Scene.OPERATOR_DETAILS
|
|
elif self.find("depot/all") or self.find("depot/all_in"):
|
|
self.scene = Scene.DEPOT
|
|
elif self.find("rhodes") and (
|
|
self.find("operator/filter_hide") or self.find("operator/filter_show")
|
|
):
|
|
self.scene = Scene.OPERATOR_MANAGEMENT
|
|
elif self.find("pull_once"):
|
|
self.scene = Scene.HEADHUNTING
|
|
elif self.find("read_and_agree") or self.find("next_step"):
|
|
self.scene = Scene.AGREEMENT_UPDATE
|
|
elif self.find("notice"):
|
|
self.scene = Scene.NOTICE
|
|
elif self.find("sss/main"):
|
|
self.scene = Scene.SSS_MAIN
|
|
elif self.find("sss/start") or self.find("sss/start_ex"):
|
|
self.scene = Scene.SSS_START
|
|
elif self.find("sss/check_ex"):
|
|
self.scene = Scene.SSS_EC_EX
|
|
elif self.find("sss/ec"):
|
|
self.scene = Scene.SSS_EC
|
|
elif self.find("sss/choose_ec"):
|
|
self.scene = Scene.SSS_CHOOSE_EC
|
|
elif self.find("sss/device"):
|
|
self.scene = Scene.SSS_DEVICE
|
|
elif self.find("sss/squad"):
|
|
self.scene = Scene.SSS_SQUAD
|
|
elif self.find("sss/drop"):
|
|
self.scene = Scene.SSS_DROP_AGENT_BEFORE_FIGHT
|
|
elif self.find("sss/deploy") or self.find("sss/deploy_ex"):
|
|
self.scene = Scene.SSS_DEPLOY
|
|
elif self.find("sss/loading") or self.find("sss/loading_ex"):
|
|
self.scene = Scene.LOADING
|
|
elif self.find("sss/redeploy") or self.find("sss/redeploy_ex"):
|
|
self.scene = Scene.SSS_REDEPLOY
|
|
elif self.find("sss/terminated"):
|
|
self.scene = Scene.SSS_TERMINATED
|
|
elif self.find("sss/action") or self.find("sss/action_ex"):
|
|
self.scene = Scene.SSS_ACTION
|
|
elif self.find("sss/accomplished"):
|
|
self.scene = Scene.SSS_ACCOMPLISHED
|
|
elif self.find("sss/ope/use"):
|
|
self.scene = Scene.SSS_ELIMI_AGENCY
|
|
elif self.find("rogue/double_confirm/main"):
|
|
if self.find("rogue/double_confirm/this_node"):
|
|
self.scene = Scene.ROGUE_REFRESH_CONFIRM
|
|
elif self.find("rogue/double_confirm/no_more_tips"):
|
|
self.scene = Scene.ROGUE_CONFUSION_WARNING
|
|
else:
|
|
self.scene = Scene.ROGUE_DOUBLE_CONFIRM
|
|
elif self.find("rogue/Sarkaz_load/mental_load"):
|
|
self.scene = Scene.ROGUE_SARKAZ_LOAD
|
|
elif self.find("rogue/Sarkaz_load/lose"):
|
|
self.scene = Scene.ROGUE_SARKAZ_LIGHTEN_LOAD
|
|
elif self.find("rogue/close"):
|
|
self.scene = Scene.ROGUE_NOTICE
|
|
|
|
elif self.find("rogue/back"):
|
|
if self.find("rogue/select_team"):
|
|
self.scene = Scene.ROGUE_TEAM_SELECT
|
|
elif self.find("rogue/choose_support"):
|
|
self.scene = Scene.ROGUE_AWARD_BEFORE_EXPLORE
|
|
elif self.find("rogue/init_recruit"):
|
|
self.scene = Scene.ROGUE_INIT_RECRUIT
|
|
elif self.find("rogue/select_recruit_combination"):
|
|
self.scene = Scene.ROGUE_RECRUIT_SELECT
|
|
elif self.find("rogue/recruit/check_recruit"):
|
|
self.scene = Scene.ROGUE_RECRUIT_AGENT_SELECT
|
|
elif self.find("rogue/Sarkaz_dart"):
|
|
self.scene = Scene.ROGUE_NODE_LOADING
|
|
elif self.find("rogue/team"):
|
|
if self.find("rogue/go"):
|
|
self.scene = Scene.ROGUE_NODE_BEFORE_GO
|
|
elif self.find("rogue/hide_dart"):
|
|
self.scene = Scene.ROGUE_NODE_OPTION_SELECT
|
|
elif self.find("rogue/playing_tips"):
|
|
if self.find("rogue/shop/eccentric_businessman"):
|
|
if self.find("rogue/shop/invest_system"):
|
|
if self.find("rogue/shop/investment_portal"):
|
|
self.scene = Scene.ROGUE_INVESTMENT_PORTAL
|
|
else:
|
|
self.scene = Scene.ROGUE_INVESTMENT
|
|
else:
|
|
self.scene = Scene.ROGUE_SHOP
|
|
else:
|
|
self.scene = Scene.ROGUE_MAIN
|
|
else:
|
|
self.scene = Scene.UNKNOWN_ROGUE
|
|
else:
|
|
self.scene = Scene.UNKNOWN_ROGUE
|
|
elif self.find("rogue/view_details") and self.find("rogue/fold_up"):
|
|
self.scene = Scene.ROGUE_AGENT_DETAIL
|
|
elif self.find("navigation/score/previous"):
|
|
self.scene = Scene.TERMINAL_PREVIOUS
|
|
# elif self.find("sign_in/moon_festival/banner"):
|
|
# self.scene = Scene.MOON_FESTIVAL
|
|
elif self.find("sign_in/task/banner"):
|
|
self.scene = Scene.ACTIVITY_TASK
|
|
elif self.find("sign_in/shop/price_black"):
|
|
self.scene = Scene.ACTIVITY_SHOP_BUY
|
|
elif self.find("sign_in/shop/price_white"):
|
|
self.scene = Scene.ACTIVITY_SHOP_BUY
|
|
elif self.find("sign_in/shop/banner"):
|
|
self.scene = Scene.ACTIVITY_SHOP
|
|
elif self.find("sign_in/special_access/banner"):
|
|
self.scene = Scene.ACTIVITY_SPECIAL_ACCESS
|
|
elif self.find("rogue/theme_select"):
|
|
self.scene = Scene.ROGUE_THEME_SELECT
|
|
elif (
|
|
self.find("rogue/regular1")
|
|
or self.find("rogue/regular2")
|
|
or self.find("rogue/regular3")
|
|
or self.find("rogue/regular4")
|
|
):
|
|
self.scene = Scene.ROGUE_INDEX
|
|
elif self.find("rogue/view_data_back"):
|
|
self.scene = Scene.ROGUE_SETTLEMENT
|
|
elif self.find("rogue/level_up_check"):
|
|
self.scene = Scene.ROGUE_LEVEL_UP
|
|
elif self.find("rogue/monthly_commision"):
|
|
self.scene = Scene.ROGUE_MONTHLY_COMMISION
|
|
elif self.find("rogue/layer_loading/1") or self.find("rogue/layer_loading/2"):
|
|
self.scene = Scene.ROGUE_LAYER_LOADING
|
|
elif self.find("rogue/select_difficulty"):
|
|
self.scene = Scene.ROGUE_DIFFICULTY_SELECT
|
|
elif self.find("contract"):
|
|
self.scene = Scene.HEADHUNTING_RESULT
|
|
elif self.find("start_story"):
|
|
self.scene = Scene.STORY_STAGE
|
|
elif self.find("sf/exit_button"):
|
|
if self.find("sf/continue"):
|
|
self.scene = Scene.SF_CONTINUE
|
|
elif self.find("sf/properties"):
|
|
if self.find("sf/select"):
|
|
self.scene = Scene.SF_SELECT
|
|
elif self.find("sf/return"):
|
|
self.scene = Scene.SF_ACTIONS
|
|
elif self.find("sf/success"):
|
|
self.scene = Scene.SF_RESULT
|
|
elif self.find("sf/failure"):
|
|
self.scene = Scene.SF_RESULT
|
|
else:
|
|
self.scene = Scene.UNKNOWN
|
|
elif self.find("sf/inheritance"):
|
|
self.scene = Scene.SF_SELECT_TEAM
|
|
elif self.find("sf/continue_event"):
|
|
self.scene = Scene.SF_EVENT
|
|
elif self.find("sf/click_anywhere"):
|
|
self.scene = Scene.SF_CLICK_ANYWHERE
|
|
elif self.find("sf/team_pass"):
|
|
self.scene = Scene.SF_TEAM_PASS
|
|
elif self.find("sf/end"):
|
|
self.scene = Scene.SF_END
|
|
else:
|
|
self.scene = Scene.UNKNOWN
|
|
elif self.find("sf/exit"):
|
|
self.scene = Scene.SF_EXIT
|
|
elif self.find("guess/main"):
|
|
self.scene = Scene.GUESS_MAIN
|
|
elif self.find("guess/back"):
|
|
self.scene = Scene.GUESS_CHOOSE
|
|
elif self.find("guess/confirm"):
|
|
self.scene = Scene.GUESS_EXIT
|
|
elif self.find("guess/exit"):
|
|
self.scene = Scene.GUESS_FIGHT
|
|
elif self.find("guess/continue"):
|
|
self.scene = Scene.GUESS_SETTLEMENT
|
|
|
|
# 模板匹配
|
|
elif self.detect_index_scene():
|
|
if self.match3d("originite")[0] >= 0.9:
|
|
self.scene = Scene.INDEX_ORIGINITE
|
|
elif self.match3d("sanity")[0] >= 0.8:
|
|
self.scene = Scene.INDEX_SANITY
|
|
else:
|
|
self.scene = Scene.INDEX
|
|
elif self.find("materiel_ico"):
|
|
self.scene = Scene.MATERIEL
|
|
elif self.find("loading"):
|
|
self.scene = Scene.LOADING
|
|
elif self.find("loading2"):
|
|
self.scene = Scene.LOADING
|
|
elif self.find("loading3"):
|
|
self.scene = Scene.LOADING
|
|
elif self.find("loading4"):
|
|
self.scene = Scene.LOADING
|
|
elif self.find("ope_plan"):
|
|
self.scene = Scene.OPERATOR_BEFORE
|
|
elif self.find("navigation/activity/banner_normal"):
|
|
self.scene = Scene.ACTIVITY_NORMAL_CHOOSE_LEVEL
|
|
elif self.find("navigation/activity/banner_ex"):
|
|
self.scene = Scene.ACTIVITY_EX_CHOOSE_LEVEL
|
|
elif self.find("navigation/episode"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("navigation/collection/AP-1"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("navigation/collection/LS-1"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("navigation/collection/CA-1"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("navigation/collection/CE-1"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("navigation/collection/SK-1"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("navigation/collection/PR-A-1"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("navigation/collection/PR-B-1"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("navigation/collection/PR-C-1"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("navigation/collection/PR-D-1"):
|
|
self.scene = Scene.OPERATOR_CHOOSE_LEVEL
|
|
elif self.find("ope_agency_going"):
|
|
self.scene = Scene.OPERATOR_ONGOING
|
|
elif self.find("sss/abandon"):
|
|
self.scene = Scene.SSS_DROP_IN_FIGHT
|
|
elif self.find("story_skip_confirm_dialog"):
|
|
self.scene = Scene.STORY_SKIP
|
|
elif self.find("story_skip"):
|
|
self.scene = Scene.STORY
|
|
elif (
|
|
self.find("fight/gear")
|
|
or self.find("fight/pause_sign")
|
|
or self.find("fight/tower")
|
|
):
|
|
self.scene = Scene.OPERATOR_FIGHT
|
|
elif self.find("ope_finish"):
|
|
self.scene = Scene.OPERATOR_FINISH
|
|
elif self.find("fight/use"):
|
|
self.scene = Scene.OPERATOR_SUPPORT_AGENT
|
|
elif self.find("business_card"):
|
|
self.scene = Scene.BUSINESS_CARD
|
|
elif self.find("friend_list"):
|
|
self.scene = Scene.FRIEND_LIST
|
|
elif self.find("credit_visiting"):
|
|
self.scene = Scene.FRIEND_VISITING
|
|
elif (
|
|
self.find("arrange_check_in")
|
|
or self.find("arrange_check_in_on")
|
|
or self.find("meeting_arrange_check_in")
|
|
or self.find("meeting_arrange_check_in_on")
|
|
):
|
|
self.scene = Scene.INFRA_DETAILS
|
|
elif self.find("user") or self.find("user_on"):
|
|
self.scene = Scene.ACTIVITY_ROOM_DETAILS
|
|
elif self.find("ope_failed"):
|
|
self.scene = Scene.OPERATOR_FAILED
|
|
elif self.find("mission_daily_on"):
|
|
self.scene = Scene.MISSION_DAILY
|
|
elif self.find("mission_weekly_on"):
|
|
self.scene = Scene.MISSION_WEEKLY
|
|
elif self.detect_recruit_agent_scene():
|
|
if self.find("recruit/agent_token") or self.find(
|
|
"recruit/agent_token_first"
|
|
):
|
|
self.scene = Scene.RECRUIT_AGENT
|
|
else:
|
|
self.scene = Scene.ROGUE_AGENT
|
|
elif self.find("terminal/main"):
|
|
self.scene = Scene.TERMINAL_MAIN
|
|
elif self.find("terminal/score"):
|
|
self.scene = Scene.TERMINAL_SCORE
|
|
elif self.find("terminal/collection"):
|
|
self.scene = Scene.TERMINAL_COLLECTION
|
|
elif self.find("terminal/regular"):
|
|
self.scene = Scene.TERMINAL_REGULAR
|
|
elif self.find("terminal/longterm"):
|
|
self.scene = Scene.TERMINAL_LONGTERM
|
|
elif self.find("terminal/periodic"):
|
|
self.scene = Scene.TERMINAL_PERIODIC
|
|
elif self.find("sign_in/shop/entry"):
|
|
self.scene = Scene.ACTIVITY_MAIN
|
|
elif self.find("announcement_close"):
|
|
self.scene = Scene.ANNOUNCEMENT
|
|
elif self.find("choose_product_options"):
|
|
self.scene = Scene.CHOOSE_PRODUCT
|
|
elif self.find("order_switching_notice"):
|
|
self.scene = Scene.SWITCH_ORDER
|
|
elif self.find("clue"):
|
|
self.scene = Scene.INFRA_CONFIDENTIAL
|
|
elif self.find("sss/drop_EC"):
|
|
self.scene = Scene.SSS_DROP_EC_BEFORE_FIGHT
|
|
elif self.find("sss/operation_complete"):
|
|
self.scene = Scene.SSS_OPERATION_COMPLETE
|
|
elif self.find("rogue/recruit/use_support"):
|
|
self.scene = Scene.ROGUE_USE_SUPPORT
|
|
elif self.find("login_captcha"):
|
|
self.scene = Scene.LOGIN_CAPTCHA
|
|
|
|
# 没弄完的
|
|
# elif self.find("ope_elimi_finished"):
|
|
# self.scene = Scene.OPERATOR_ELIMINATE_FINISH
|
|
|
|
# 兜底
|
|
elif self.find("nav_button"):
|
|
self.scene = Scene.UNKNOWN_WITH_NAVBAR
|
|
else:
|
|
self.scene = Scene.UNKNOWN
|
|
self.check_current_focus()
|
|
|
|
logger.debug(f"Scene {self.scene}: {SceneComment[self.scene]}")
|
|
|
|
return self.scene
|
|
|
|
def find_ra_battle_exit(self) -> bool:
|
|
im = cv2.cvtColor(self.img, cv2.COLOR_RGB2HSV)
|
|
im = cv2.inRange(im, (29, 0, 0), (31, 255, 255))
|
|
score, scope = self.template_match(
|
|
"ra/battle_exit", ((75, 47), (165, 126)), cv2.TM_CCOEFF_NORMED
|
|
)
|
|
return scope if score > 0.8 else None
|
|
|
|
def detect_ra_adventure(self) -> bool:
|
|
img = cropimg(self.gray, ((385, 365), (475, 465)))
|
|
img = thres2(img, 250)
|
|
res = loadres("ra/adventure", True)
|
|
result = cv2.matchTemplate(img, res, cv2.TM_CCOEFF_NORMED)
|
|
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
|
logger.debug(f"{max_val=} {max_loc=}")
|
|
return max_val >= 0.9
|
|
|
|
def get_ra_scene(self) -> int:
|
|
"""
|
|
生息演算场景识别
|
|
"""
|
|
# 场景缓存
|
|
if self.scene != Scene.UNDEFINED:
|
|
return self.scene
|
|
|
|
# 连接中,优先级最高
|
|
if self.find("connecting"):
|
|
self.scene = Scene.CONNECTING
|
|
elif self.find("loading"):
|
|
self.scene = Scene.UNKNOWN
|
|
elif self.find("loading4"):
|
|
self.scene = Scene.UNKNOWN
|
|
|
|
# 奇遇
|
|
elif self.detect_ra_adventure():
|
|
self.scene = Scene.RA_ADVENTURE
|
|
|
|
# 快速跳过剧情对话
|
|
elif self.find("ra/guide_dialog"):
|
|
self.scene = Scene.RA_GUIDE_DIALOG
|
|
|
|
# 快速退出作战
|
|
elif self.find_ra_battle_exit():
|
|
self.scene = Scene.RA_BATTLE
|
|
elif self.find("ra/battle_exit_dialog"):
|
|
self.scene = Scene.RA_BATTLE_EXIT_CONFIRM
|
|
|
|
# 作战与分队
|
|
elif self.find("ra/squad_edit"):
|
|
self.scene = Scene.RA_SQUAD_EDIT
|
|
elif self.find("ra/start_action"):
|
|
if self.find("ra/action_points"):
|
|
self.scene = Scene.RA_BATTLE_ENTRANCE
|
|
else:
|
|
self.scene = Scene.RA_GUIDE_BATTLE_ENTRANCE
|
|
elif self.find("ra/get_item"):
|
|
self.scene = Scene.RA_GET_ITEM
|
|
elif self.find("ra/return_from_kitchen"):
|
|
self.scene = Scene.RA_KITCHEN
|
|
elif self.find("ra/squad_edit_confirm_dialog"):
|
|
self.scene = Scene.RA_SQUAD_EDIT_DIALOG
|
|
elif self.find("ra/enter_battle_confirm_dialog"):
|
|
self.scene = Scene.RA_SQUAD_ABNORMAL
|
|
elif self.find("ra/battle_complete"):
|
|
self.scene = Scene.RA_BATTLE_COMPLETE
|
|
|
|
# 结算界面
|
|
elif self.find("ra/day_complete"):
|
|
self.scene = Scene.RA_DAY_COMPLETE
|
|
elif self.find("ra/period_complete") and self.find("ra/click_anywhere"):
|
|
self.scene = Scene.RA_PERIOD_COMPLETE
|
|
|
|
# 森蚺图耶对话
|
|
elif self.find("ra/guide_entrance"):
|
|
self.scene = Scene.RA_GUIDE_ENTRANCE
|
|
|
|
# 存档操作
|
|
elif self.find("ra/delete_save_confirm_dialog"):
|
|
self.scene = Scene.RA_DELETE_SAVE_DIALOG
|
|
|
|
# 地图识别
|
|
elif self.find("ra/waste_time_button"):
|
|
self.scene = Scene.RA_DAY_DETAIL
|
|
elif self.find("ra/waste_time_dialog"):
|
|
self.scene = Scene.RA_WASTE_TIME_DIALOG
|
|
elif self.find("ra/map_back", thres=200) and self.color(1817, 333)[0] > 250:
|
|
self.scene = Scene.RA_MAP
|
|
|
|
# 一张便条
|
|
elif self.find("ra/notice"):
|
|
self.scene = Scene.RA_NOTICE
|
|
|
|
# 一张便条
|
|
elif self.find("ra/no_enough_drink"):
|
|
self.scene = Scene.RA_INSUFFICIENT_DRINK
|
|
|
|
# 从首页选择终端进入生息演算主页
|
|
elif self.find("terminal_longterm"):
|
|
self.scene = Scene.TERMINAL_LONGTERM
|
|
elif self.find("ra/main_title"):
|
|
self.scene = Scene.RA_MAIN
|
|
elif self.detect_index_scene():
|
|
self.scene = Scene.INDEX
|
|
elif self.find("terminal_main"):
|
|
self.scene = Scene.TERMINAL_MAIN
|
|
else:
|
|
self.scene = Scene.UNKNOWN
|
|
self.check_current_focus()
|
|
|
|
logger.debug(f"Scene: {self.scene}: {SceneComment[self.scene]}")
|
|
self.check_loading_time()
|
|
return self.scene
|
|
|
|
def get_train_scene(self) -> int:
|
|
"""
|
|
训练室场景识别
|
|
"""
|
|
# 场景缓存
|
|
if self.scene != Scene.UNDEFINED:
|
|
return self.scene
|
|
# 连接中,优先级最高
|
|
if self.find("connecting"):
|
|
self.scene = Scene.CONNECTING
|
|
elif self.find("infra_overview"):
|
|
self.scene = Scene.INFRA_MAIN
|
|
elif self.find("train_main"):
|
|
self.scene = Scene.TRAIN_MAIN
|
|
elif self.find("skill_collect_confirm", scope=((1142, 831), (1282, 932))):
|
|
self.scene = Scene.TRAIN_FINISH
|
|
elif self.find("training_support"):
|
|
self.scene = Scene.TRAIN_SKILL_SELECT
|
|
elif self.find("upgrade_failure"):
|
|
self.scene = Scene.TRAIN_SKILL_UPGRADE_ERROR
|
|
elif self.find("skill_confirm"):
|
|
self.scene = Scene.TRAIN_SKILL_UPGRADE
|
|
else:
|
|
self.scene = Scene.UNKNOWN
|
|
self.check_current_focus()
|
|
|
|
logger.debug(f"Scene: {self.scene}: {SceneComment[self.scene]}")
|
|
|
|
self.check_loading_time()
|
|
|
|
return self.scene
|
|
|
|
def find(
|
|
self,
|
|
res: tp.Res,
|
|
draw: bool = False,
|
|
scope: tp.Scope | None = None,
|
|
threshold: float = 0.9,
|
|
) -> tp.Scope | None:
|
|
"""找图
|
|
|
|
Args:
|
|
res (tp.Res): 元素名
|
|
draw (bool, optional): 画图,用于调试. Defaults to False.
|
|
scope (tp.Scope | None, optional): 匹配区域,只对特征匹配和预设为None的模板匹配有用. Defaults to None.
|
|
threshold (float, optional): 匹配阈值,只对特征匹配有用. Defaults to 0.9.
|
|
|
|
Returns:
|
|
tp.Scope | None: 匹配成功时返回元素所在的区域,失败时返回None
|
|
"""
|
|
if scope:
|
|
scope = clamp_scope(scope)
|
|
|
|
if res in color:
|
|
res_img = loadres(res)
|
|
h, w, _ = res_img.shape
|
|
|
|
pos_list = color[res]
|
|
if not isinstance(pos_list[0], tuple):
|
|
pos_list = [color[res]]
|
|
for pos in pos_list:
|
|
scope = pos, va(pos, (w, h))
|
|
img = cropimg(self.img, scope)
|
|
if cmatch(img, res_img, draw=draw):
|
|
score = cv2.matchTemplate(img, res_img, cv2.TM_CCOEFF_NORMED)[0][0]
|
|
threshold = (
|
|
template_matching_score[res]
|
|
if res in template_matching_score
|
|
else 0.9
|
|
)
|
|
if score >= threshold:
|
|
logger.debug(f"cmatch: {res=} {score=} {scope=}")
|
|
return scope
|
|
return None
|
|
|
|
if res in template_matching:
|
|
threshold = 0.9
|
|
if res in template_matching_score:
|
|
threshold = template_matching_score[res]
|
|
|
|
res_img = loadres(res, True)
|
|
h, w = res_img.shape
|
|
|
|
pos = template_matching[res] or scope
|
|
if isinstance(pos[0], (tuple, list)):
|
|
scope = pos
|
|
else:
|
|
scope = pos, va(pos, (w, h))
|
|
|
|
img = cropimg(self.gray, scope)
|
|
result = cv2.matchTemplate(img, res_img, cv2.TM_CCOEFF_NORMED)
|
|
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
|
|
|
if draw:
|
|
logger.debug(f"{max_val=}, pos={va(max_loc, scope[0])}")
|
|
|
|
if max_val >= threshold:
|
|
top_left = va(max_loc, scope[0])
|
|
scope = top_left, va(top_left, (w, h))
|
|
logger.debug(f"template matching: {res=} {max_val=} {scope=}")
|
|
return scope
|
|
return None
|
|
|
|
res_img = loadres(res, True)
|
|
score, scope = self.matcher.match(res_img, draw, scope)
|
|
return scope if score >= threshold else None
|
|
|
|
def template_match(
|
|
self,
|
|
res: str,
|
|
scope: Optional[tp.Scope] = None,
|
|
method: int = cv2.TM_CCOEFF_NORMED,
|
|
) -> Tuple[float, tp.Scope]:
|
|
logger.debug(f"{res=}")
|
|
|
|
template = loadres(res, True)
|
|
w, h = template.shape[::-1]
|
|
|
|
if scope:
|
|
x, y = scope[0]
|
|
img = cropimg(self.gray, scope)
|
|
else:
|
|
x, y = (0, 0)
|
|
img = self.gray
|
|
|
|
result = cv2.matchTemplate(img, template, method)
|
|
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
|
|
|
|
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
|
|
top_left = min_loc
|
|
score = min_val
|
|
else:
|
|
top_left = max_loc
|
|
score = max_val
|
|
|
|
p1 = (top_left[0] + x, top_left[1] + y)
|
|
p2 = (p1[0] + w, p1[1] + h)
|
|
|
|
scope = p1, p2
|
|
logger.debug(f"{score=} {scope=}")
|
|
return score, scope
|
|
|
|
def match(
|
|
self, res: tp.Res, draw: bool = False, scope: tp.Scope | None = None
|
|
) -> tuple[float, tp.Scope | None]:
|
|
logger.debug(f"{res=}")
|
|
return self.matcher.match(loadres(res, True), draw, scope)
|
|
|
|
def match2d(
|
|
self, res: tp.Res, draw: bool = False, scope: tp.Scope | None = None
|
|
) -> tuple[float, tp.Scope | None]:
|
|
logger.debug(f"{res=}")
|
|
return self.matcher.match2d(loadres(res, True), draw, scope)
|
|
|
|
def match3d(
|
|
self, res: tp.Res, draw: bool = False, scope: tp.Scope | None = None
|
|
) -> tuple[float, tp.Scope | None]:
|
|
logger.debug(f"{res=}")
|
|
return self.matcher.match3d(loadres(res, True), draw, scope)
|