mower-ng/mower/solvers/fight/battle_choose/choose_support.py
zhbaor c86e7ac651
All checks were successful
ci/woodpecker/push/check_format Pipeline was successful
场景图导航改写为solver
2025-01-30 10:14:54 +08:00

223 lines
7.9 KiB
Python

from typing import Literal
import cv2
from scipy.signal import argrelmin
from skimage.metrics import structural_similarity
from mower.data import agent_list
from mower.utils import config
from mower.utils import typealias as tp
from mower.utils.character_recognize import match_portrait
from mower.utils.graph import SceneGraphSolver
from mower.utils.image import cmatch, cropimg, loadres, thres2
from mower.utils.log import logger
from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver
profession_pos = {
"PIONEER": ((30, 150), (40, 160)),
"WARRIOR": ((30, 263), (40, 273)),
"TANK": ((30, 376), (40, 386)),
"SNIPER": ((30, 489), (40, 499)),
"CASTER": ((30, 602), (40, 612)),
"MEDIC": ((30, 715), (40, 725)),
"SUPPORT": ((30, 830), (40, 840)),
"SPECIAL": ((30, 944), (40, 954)),
}
skill_pos = (
((1237, 390), (1252, 405)),
((1409, 390), (1424, 405)),
((1582, 390), (1597, 405)),
)
class ChooseSupportSolver(BaseSolver):
solver_name = "选择助战干员"
def run(self, opers: list = [], mode: Literal["normal", "rogue"] = "normal"):
"""
Args:
opers: 包含name和skill,若传入多个干员会选择其中练度最高的
mode: 模式选择,normal为普通选助战,rogue为肉鸽选助战
"""
self.mode = mode
self.opers_skill = {}
if len(opers) == 0:
return True
for i in range(len(opers) - 1, -1, -1): # 改变group的结构
op = opers[i]
if "opers" in op:
opers += op["opers"]
del opers[i]
for op in opers:
self.opers_skill[op["name"]] = op["skill"]
logger.info(f"助战干员:{self.opers_skill}")
self.min_progression = (
config.conf.support.elite * 100 + config.conf.support.level
) # 最低练度要求
self.retry_times = config.conf.support.refresh # 刷新次数
self.profession = agent_list[opers[0]["name"]]["profession"] # 目标干员职业
self.matched = False # 是否匹配过干员
self.swiped = False # 是否滑动过
self.success = False # 是否成功选择到干员
self.skill = None # 选中干员需要的技能
super().run()
return self.success
def check_profession_focus(self) -> bool:
img = cropimg(config.recog.img, profession_pos[self.profession])
res = loadres("fight/profession_not_be_choosen")
return not cmatch(img, res, 70)
def get_oper_pos(self):
y = 908 if self.mode == "normal" else 917
if not self.swiped:
img = cropimg(config.recog.gray, ((0, y), (1920, y + 75)))
else:
img = cropimg(config.recog.gray, ((1600, y), (1920, y + 75)))
res = loadres("fight/choose", True)
result = cv2.matchTemplate(img, res, cv2.TM_SQDIFF_NORMED)[0]
threshold = 0.1
match = []
for i in argrelmin(result, order=100)[0]:
if result[i] < threshold:
if self.swiped:
match.append(((i + 1600, 500), (i + 1794, 625)))
else:
match.append(((i, 500), (i + 194, 625)))
logger.debug(match)
match_opers = dict(match_portrait(config.recog.gray, segment=match))
result = {}
for op in self.opers_skill:
if op in match_opers:
result[op] = match_opers[op]
return result
def get_agent_elite(self, scope: tp.Scope) -> int:
"获取干员精英等级"
img = cropimg(
config.recog.gray,
(
(scope[0][0] + 55, scope[0][1] + 140),
(scope[1][0] - 54, scope[1][1] + 80),
),
)
img = thres2(img, 200)
e1 = loadres("fight/elite1", True)
e2 = loadres("fight/elite2", True)
ssim1 = structural_similarity(img, e1)
ssim2 = structural_similarity(img, e2)
if ssim2 > 0.8:
return 2
elif ssim1 > 0.8:
return 1
else:
return 0
def number(self, scope: tp.Scope, height: int, thres: int) -> int:
"数字识别"
return config.recog.num.number_int("secret_front", scope, height, thres)
def get_agent_progression(self, scope: tp.Scope) -> int:
"获取干员练度"
elite = self.get_agent_elite(scope)
level = self.number(
(
(scope[0][0] + 125, scope[0][1] + 220),
(scope[1][0] - 19, scope[1][1] + 130),
),
24,
180,
)
logger.debug(f"elite: {elite}, level: {level}")
return elite * 100 + level
def check_agent(self, cur_opers: dict):
"检查干员是否符合要求"
if cur_opers is None:
return None
result = None
max_progression = self.min_progression - 1
for op in cur_opers:
progression = self.get_agent_progression(cur_opers[op])
if progression > max_progression:
max_progression = progression
result = cur_opers[op]
self.skill = self.opers_skill[op]
return result
def transition(self):
if (scene := self.scene()) == Scene.OPERATOR_SUPPORT:
if self.animation():
return
if self.retry_times < 0:
SceneGraphSolver().step(Scene.OPERATOR_SELECT)
return
if not self.check_profession_focus():
self.tap(profession_pos[self.profession])
return
if self.matched:
if self.swiped:
self.swipe_noinertia((800, 350), (150, 0))
self.swiped = False
if self.find("fight/refresh"):
for _ in range(10):
if pos := self.find("fight/refresh"):
self.tap(pos)
self.sleep(0.2)
else:
break
self.retry_times -= 1
self.matched = False
else:
self.sleep(0.5)
else:
if result := self.check_agent(self.get_oper_pos()):
self.ctap(result, 3)
elif not self.swiped:
self.swiped = True
self.swipe_noinertia((800, 350), (-150, 0))
self.sleep(0.4)
else:
self.matched = True
elif scene == Scene.OPERATOR_SUPPORT_AGENT:
if self.animation():
return
if not self.find(
"choose_agent/support_skill_be_choosen", scope=skill_pos[self.skill - 1]
):
self.tap(skill_pos[self.skill - 1])
else:
self.ctap("fight/use", 3)
self.success = True
elif scene == Scene.ROGUE_USE_SUPPORT:
self.tap("rogue/recruit/use_support")
self.success = True
elif scene == Scene.OPERATOR_SELECT:
if self.success:
return True
elif self.retry_times < 0:
logger.warning("选择助战干员失败,请降低练度要求或添加好友")
return True
else:
self.ctap((1660, 315))
elif scene == Scene.SSS_SQUAD:
if self.success:
return True
elif self.retry_times < 0:
logger.warning("选择助战干员失败,请降低练度要求或添加好友")
return True
else:
self.tap("sss/choose/choose_support")
elif scene == Scene.ROGUE_RECRUIT_AGENT_SELECT:
self.ctap("rogue/recruit/choose_support", 3)
elif scene in self.waiting_scene:
self.waiting_solver()
else:
return True