Compare commits

...

4 commits

Author SHA1 Message Date
4c3eaef108 Merge branch 'clue'
All checks were successful
ci/woodpecker/push/check_format Pipeline was successful
2025-06-29 20:29:12 +08:00
6d3640aea0 线索改进 2025-06-29 20:28:54 +08:00
fe6a59b0f6 线索数量获取场景修复
All checks were successful
ci/woodpecker/push/check_format Pipeline was successful
2025-06-29 15:20:15 +08:00
706fa6a4c0 线索相关Solver使用TransitionOn新写法
All checks were successful
ci/woodpecker/push/check_format Pipeline was successful
2025-06-29 15:06:32 +08:00
13 changed files with 356 additions and 286 deletions

BIN
mower/resources/clue/filter_all_on.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -1,51 +1,53 @@
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from datetime import timedelta
from mower.utils.log import logger
from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver
from mower.utils.solver import BaseSolver, TransitionOn
from .utils import clue_cls
from .utils import ClueMainSolver, clue_cls
class DailySolver(BaseSolver, BaseMixin):
class DailySolver(BaseSolver):
solver_name = "每日线索领取"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
def run(self) -> None:
self.success = False
super().run()
def transition(self) -> bool:
if (
scene := self.scene()
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
self.tap((500, 1000))
elif scene == Scene.INFRA_CONFIDENTIAL:
if self.animation():
return
if self.success:
return True
# 检查是否领过线索
daily_scope = ((1822, 208), (1886, 243))
if self.find("clue/badge_new", scope=daily_scope):
self.tap((1800, 270))
else:
return True
elif scene == Scene.CLUE_DAILY:
exit_pos = (1484, 152)
if self.find("clue/icon_notification"):
logger.info("当前获得线索数已经达到最大值")
self.tap(exit_pos)
self.success = True
elif clue := clue_cls("daily"):
logger.info(f"领取今日线索({clue}号)")
self.tap("clue/button_get")
else:
# 今日线索已领取,点X退出
self.tap(exit_pos)
self.success = True
elif scene == Scene.CLUE_MESSAGE_BOARD:
self.cback(3, id="infra_back")
elif scene in self.waiting_scene:
self.waiting_solver()
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self):
if self.success:
return True
if self.animation():
return
# 检查是否领过线索
daily_scope = ((1822, 208), (1886, 243))
if self.find("clue/badge_new", scope=daily_scope):
self.tap((1800, 270))
else:
EnterRoomSolver().run("meeting", detail=False)
return True
@TransitionOn(Scene.CLUE_DAILY)
def _(self):
exit_pos = (1484, 152)
if self.find("clue/icon_notification"):
logger.info("当前获得线索数已经达到最大值")
self.tap(exit_pos)
self.success = True
elif clue := clue_cls("daily"):
logger.info(f"领取今日线索({clue}号)")
self.tap("clue/button_get")
else:
# 今日线索已领取,点X退出
self.tap(exit_pos)
self.success = True
@TransitionOn(BaseSolver.waiting_scene)
def _(self):
self.waiting_solver()
@TransitionOn()
def _(self):
ClueMainSolver().run()

View file

@ -1,13 +1,18 @@
from datetime import timedelta
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config
from mower.utils.log import logger
from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver
from mower.utils.solver import BaseSolver, TransitionOn
from .utils import MeetingSolver
class GetClueCountSolver(BaseSolver, BaseMixin):
solver_name = "线索数量"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
def run(self) -> int:
self.res = -1
@ -16,16 +21,19 @@ class GetClueCountSolver(BaseSolver, BaseMixin):
return self.res
raise ValueError("未找到线索数量")
def transition(self) -> bool:
if self.find("meeting_arrange_check_in"):
self.res = self.read_screen(
config.recog.img, limit=10, cord=((476, 985), (519, 1020))
)
logger.info(f"当前拥有线索数量为{self.res}")
return True
elif (scene := self.scene()) == Scene.INFRA_CONFIDENTIAL:
self.cback(3, id="infra_back")
elif scene in self.waiting_scene:
self.waiting_solver()
else:
EnterRoomSolver().run("meeting", detail=False)
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
MeetingSolver().run()
self.res = self.read_screen(
config.recog.img, limit=10, cord=((476, 985), (519, 1020))
)
logger.info(f"当前拥有线索数量为{self.res}")
return True
@TransitionOn(BaseSolver.waiting_scene)
def _(self):
self.waiting_solver()
@TransitionOn()
def _(self):
MeetingSolver().run()

View file

@ -1,18 +1,20 @@
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from datetime import timedelta
from mower.utils import config
from mower.utils.image import cmatch, crop2content, cropimg, loadres, thres2
from mower.utils.log import logger
from mower.utils.rapidocr import ocr_rec
from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver
from mower.utils.solver import BaseSolver, TransitionOn
from mower.utils.vector import va
from .utils import clue_cls, clue_scope
from .utils import ClueMainSolver, clue_cls, clue_scope
class GiveAwaySolver(BaseSolver, BaseMixin):
class GiveAwaySolver(BaseSolver):
solver_name = "传递线索"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
def run(self, clue_count) -> None:
self.clue_count = clue_count
@ -20,50 +22,48 @@ class GiveAwaySolver(BaseSolver, BaseMixin):
return
super().run()
def transition(self) -> bool:
if (
scene := self.scene()
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
self.tap((500, 1000))
elif scene == Scene.INFRA_CONFIDENTIAL:
self.ctap((1799, 578))
elif scene == Scene.CLUE_GIVE_AWAY:
if not (config.conf.leifeng_mode or self.clue_count > 9):
self.tap((1868, 54))
return True
if self.animation():
return
if (c := clue_cls("give_away")) is None:
self.tap((1868, 54))
return True
if config.recog.gray[230][600] < 90:
self.ctap(clue_scope["give_away"], 5)
return
if self.find("clue/icon_notification"):
return
if self.detect_product_complete():
return
orange = False
for i in range(4):
scope = (1285 + (c - 1) * 65, 137 + i * 225)
bg = loadres("clue/give_away_digit_bg")
h, w, _ = bg.shape
scope = scope, va(scope, (w, h))
img = cropimg(config.recog.img, scope)
if cmatch(img, bg):
orange = True
break
if not orange:
i = 0
name_top_left = (880, 125 + 225 * i)
name_scope = (name_top_left, va(name_top_left, (390, 34)))
name = cropimg(config.recog.gray, name_scope)
name = thres2(name, 250)
name = crop2content(name)
name = ocr_rec(name)
logger.info(f"{name}送一张线索{c}")
self.tap((1808, 200 + i * 255))
elif scene in self.waiting_scene:
self.waiting_solver()
else:
EnterRoomSolver().run("meeting", detail=False)
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self):
self.ctap((1799, 578))
@TransitionOn(Scene.CLUE_GIVE_AWAY)
def _(self):
if not (config.conf.leifeng_mode or self.clue_count > 9):
return True
if self.animation():
return
if (c := clue_cls("give_away")) is None:
return True
if config.recog.gray[230][600] < 90:
self.ctap(clue_scope["give_away"], 5)
return
if self.find("product_complete"):
return
orange = False
for i in range(4):
scope = (1285 + (c - 1) * 65, 137 + i * 225)
bg = loadres("clue/give_away_digit_bg")
h, w, _ = bg.shape
scope = scope, va(scope, (w, h))
img = cropimg(config.recog.img, scope)
if cmatch(img, bg):
orange = True
break
if not orange:
i = 0
name_top_left = (880, 125 + 225 * i)
name_scope = (name_top_left, va(name_top_left, (390, 34)))
name = cropimg(config.recog.gray, name_scope)
name = thres2(name, 250)
name = crop2content(name)
name = ocr_rec(name)
logger.info(f"{name}送一张线索{c}")
self.tap((1808, 200 + i * 255))
@TransitionOn(BaseSolver.waiting_scene)
def _(self):
self.waiting_solver()
@TransitionOn()
def _(self):
ClueMainSolver().run()

View file

@ -1,42 +1,42 @@
from datetime import timedelta
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config
from mower.utils.scene import Scene
from mower.utils.solver import BaseSolver
from mower.utils.solver import BaseSolver, TransitionOn
from .utils import MeetingSolver
class MessageBoard(BaseSolver, BaseMixin):
class MessageBoard(BaseSolver):
solver_name = "留言板"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
def transition(self):
if (
scene := self.scene()
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
if pos := self.find("clue/title_party"):
self.tap(pos)
return
score, scope = config.recog.match("clue/message_board_entry")
if score > 0.8:
self.ctap(scope)
return
self.tap("clue/interact")
elif scene == Scene.CLUE_MESSAGE_BOARD:
if self.animation():
return
if pos := self.find("clue/message_board_collect"):
self.tap(pos)
return
return True
elif scene in [
Scene.INFRA_CONFIDENTIAL,
Scene.CLUE_MESSAGE_BOARD_FRIEND,
Scene.CLUE_ACCESS_RECORD,
]:
self.cback(3, id="infra_back")
elif scene in self.waiting_scene:
self.waiting_solver()
else:
EnterRoomSolver().run("meeting", detail=False)
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
MeetingSolver().run()
if pos := self.find("clue/title_party"):
self.tap(pos)
return
score, scope = config.recog.match("clue/message_board_entry")
if score > 0.8:
self.ctap(scope)
return
self.tap("clue/interact")
@TransitionOn(Scene.CLUE_MESSAGE_BOARD)
def _(self):
if self.animation():
return
if pos := self.find("clue/message_board_collect"):
self.tap(pos)
return
return True
@TransitionOn(BaseSolver.waiting_scene)
def _(self):
self.waiting_solver()
@TransitionOn()
def _(self):
MeetingSolver().run()

View file

@ -1,12 +1,13 @@
from datetime import timedelta
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config
from mower.utils.log import logger
from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver, TransitionOn
from .utils import MeetingSolver
class PartyTimeSolver(BaseSolver, BaseMixin):
solver_name = "线索交流结束时间"
@ -15,9 +16,7 @@ class PartyTimeSolver(BaseSolver, BaseMixin):
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
if self.detect_room() != "meeting":
EnterRoomSolver().run("meeting", detail=False)
return
MeetingSolver().run()
if not self.find("clue/party_on"):
config.party_time = None
logger.info("线索交流未开启")
@ -28,7 +27,6 @@ class PartyTimeSolver(BaseSolver, BaseMixin):
return True
if pos := self.find("clue/show_party_details"):
self.tap(pos)
return
@TransitionOn(BaseSolver.waiting_scene)
def _(self):
@ -36,8 +34,4 @@ class PartyTimeSolver(BaseSolver, BaseMixin):
@TransitionOn()
def _(self):
EnterRoomSolver().run("meeting", detail=False)
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self):
self.cback(3, id="infra_back")
MeetingSolver().run()

View file

@ -1,19 +1,19 @@
from datetime import timedelta
import cv2
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config
from mower.utils.image import crop2content, cropimg, loadres, thres2
from mower.utils.log import logger
from mower.utils.rapidocr import ocr_rec
from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver
from mower.utils.solver import BaseSolver, TransitionOn
from mower.utils.vector import va
from .utils import (
ClueMainSolver,
clue_cls,
clue_scope,
exit_pos,
is_orange,
main_dots,
main_scope,
@ -27,8 +27,10 @@ filter_receive = (1900, 45)
filter_self = (1610, 70)
class PlaceSolver(BaseSolver, BaseMixin):
class PlaceSolver(BaseSolver):
solver_name = "放置线索"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
def run(self) -> None:
self.clue_status = {}
@ -49,104 +51,101 @@ class PlaceSolver(BaseSolver, BaseMixin):
return cl, st
return None, None
def transition(self) -> bool:
if (
scene := self.scene()
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
self.tap((500, 1000))
elif scene == Scene.INFRA_CONFIDENTIAL:
if self.animation():
return
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self):
if self.animation():
return
if unlock_pos := self.detect_unlock():
self.tap(unlock_pos)
return
for i in range(1, 8):
if is_orange(self.get_color(main_dots[i]).tolist()):
self.clue_status[i] = "available"
elif clue_cls(i):
hsv = config.recog.hsv
if 160 < hsv[main_time[i][1]][main_time[i][0]][0] < 180:
self.clue_status[i] = "friend"
else:
self.clue_status[i] = "self"
else:
self.clue_status[i] = None
cl, st = self.place_index()
if st in ["available", "self", "available_self_only"]:
self.ctap(main_scope[cl], 1)
return
return True
@TransitionOn(Scene.CLUE_PLACE)
def _(self):
cl, st = self.place_index()
if cl is None:
if unlock_pos := self.detect_unlock():
self.tap(unlock_pos)
return
for i in range(1, 8):
if is_orange(self.get_color(main_dots[i]).tolist()):
self.clue_status[i] = "available"
elif clue_cls(i):
hsv = config.recog.hsv
if 160 < hsv[main_time[i][1]][main_time[i][0]][0] < 180:
self.clue_status[i] = "friend"
else:
self.clue_status[i] = "self"
return True
if self.get_color((1328 + 77 * cl, 114))[0] < 150:
# 右上角 1-7
self.tap(clue_scope[cl])
return
receive = st in ["available", "self"]
filter_pos = filter_receive if receive else filter_self
if not all(self.get_color(filter_pos) > [252] * 3):
self.tap(filter_pos)
return
self.clue_list = []
for cp in clue_pos:
clue_img = cropimg(config.recog.img, tl2p(cp))
res = loadres(f"clue/{cl}")
result = cv2.matchTemplate(clue_img, res, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
if max_val > tm_thres:
name_scope = (va(cp, (274, 99)), va(cp, (580, 134)))
name_img = cropimg(config.recog.gray, name_scope)
name = ocr_rec(name_img)
time_scope = (va(cp, (45, 222)), va(cp, (168, 255)))
time_hsv = cropimg(config.recog.hsv, time_scope)
if 165 < time_hsv[0][0][0] < 175:
time_img = thres2(cropimg(config.recog.gray, time_scope), 180)
time_img = crop2content(time_img)
time = ocr_rec(time_img)
else:
self.clue_status[i] = None
cl, st = self.place_index()
if st in ["available", "self", "available_self_only"]:
self.ctap(main_scope[cl], 1)
return
time = None
self.clue_list.append({"name": name, "time": time, "scope": tl2p(cp)})
else:
return True
elif scene == Scene.CLUE_PLACE:
cl, st = self.place_index()
if cl is None:
if unlock_pos := self.detect_unlock():
self.tap(unlock_pos)
return
else:
self.tap(exit_pos)
return True
if self.get_color((1328 + 77 * cl, 114))[0] < 150:
# 右上角 1-7
self.tap(clue_scope[cl])
return
receive = st in ["available", "self"]
filter_pos = filter_receive if receive else filter_self
if not all(self.get_color(filter_pos) > [252] * 3):
self.tap(filter_pos)
return
self.clue_list = []
for cp in clue_pos:
clue_img = cropimg(config.recog.img, tl2p(cp))
res = loadres(f"clue/{cl}")
result = cv2.matchTemplate(clue_img, res, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
if max_val > tm_thres:
name_scope = (va(cp, (274, 99)), va(cp, (580, 134)))
name_img = cropimg(config.recog.gray, name_scope)
name = ocr_rec(name_img)
time_scope = (va(cp, (45, 222)), va(cp, (168, 255)))
time_hsv = cropimg(config.recog.hsv, time_scope)
if 165 < time_hsv[0][0][0] < 175:
time_img = thres2(cropimg(config.recog.gray, time_scope), 180)
time_img = crop2content(time_img)
time = ocr_rec(time_img)
else:
time = None
self.clue_list.append(
{"name": name, "time": time, "scope": tl2p(cp)}
)
else:
break
if self.clue_list:
list_name = "接收库" if receive else "自有库"
logger.info(f"{cl}号线索{list_name}{self.clue_list}")
selected = None
for c in self.clue_list:
if c["time"]:
selected = c
break
if self.clue_list:
list_name = "接收库" if receive else "自有库"
logger.info(f"{cl}号线索{list_name}{self.clue_list}")
selected = None
for c in self.clue_list:
if c["time"]:
selected = c
break
selected = selected or self.clue_list[0]
self.tap(selected["scope"])
if self.clue_status[cl] == "available":
self.clue_status[cl] = "friend"
elif self.clue_status[cl] == "available_self_only":
self.clue_status[cl] = "self_only"
elif self.clue_status[cl] == "self":
self.clue_status[cl] = "friend"
else:
self.clue_status[cl] = None
selected = selected or self.clue_list[0]
self.tap(selected["scope"])
if self.clue_status[cl] == "available":
self.clue_status[cl] = "friend"
elif self.clue_status[cl] == "available_self_only":
self.clue_status[cl] = "self_only"
elif self.clue_status[cl] == "self":
self.clue_status[cl] = "friend"
else:
if self.clue_status[cl] == "available":
self.clue_status[cl] = "available_self_only"
elif self.clue_status[cl] == "available_self_only":
self.clue_status[cl] = None
elif self.clue_status[cl] == "self":
self.clue_status[cl] = "self_only"
else:
self.clue_status[cl] = None
elif scene in self.waiting_scene:
self.waiting_solver()
self.clue_status[cl] = None
else:
EnterRoomSolver().run("meeting", detail=False)
if self.clue_status[cl] == "available":
self.clue_status[cl] = "available_self_only"
elif self.clue_status[cl] == "available_self_only":
self.clue_status[cl] = None
elif self.clue_status[cl] == "self":
self.clue_status[cl] = "self_only"
else:
self.clue_status[cl] = None
@TransitionOn(BaseSolver.waiting_scene)
def _(self):
self.waiting_solver()
@TransitionOn()
def _(self):
ClueMainSolver().run()

View file

@ -1,45 +1,44 @@
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from datetime import timedelta
from mower.utils import config
from mower.utils.image import cropimg
from mower.utils.log import logger
from mower.utils.rapidocr import ocr_rec
from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver
from mower.utils.solver import BaseSolver, TransitionOn
from .utils import clue_cls, exit_pos
from .utils import ClueMainSolver, clue_cls
class ReceiveSolver(BaseSolver, BaseMixin):
class ReceiveSolver(BaseSolver):
solver_name = "接收好友线索"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
def run(self):
super().run()
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self):
receive_scope = ((1822, 365), (1886, 400))
if self.find("clue/badge_new", scope=receive_scope):
self.ctap((1800, 430))
return
return True
def transition(self) -> bool:
if (
scene := self.scene()
) == Scene.INFRA_DETAILS and self.detect_room() == "meeting":
self.tap((500, 1000))
elif scene == Scene.INFRA_CONFIDENTIAL:
receive_scope = ((1822, 365), (1886, 400))
if self.find("clue/badge_new", scope=receive_scope):
self.ctap((1800, 430))
else:
return True
elif scene == Scene.CLUE_RECEIVE:
if self.find("infra_complete/信用", scope=((1440, 130), (1920, 193))):
self.sleep()
return
if clue := clue_cls("receive"):
name_scope = ((1580, 220), (1880, 255))
name_img = cropimg(config.recog.gray, name_scope)
name = ocr_rec(name_img) or "好友"
logger.info(f"接收{name}{clue}号线索")
self.tap(name_scope)
else:
self.tap(exit_pos)
elif scene in self.waiting_scene:
self.waiting_solver()
else:
EnterRoomSolver().run("meeting", detail=False)
@TransitionOn(Scene.CLUE_RECEIVE)
def _(self):
if self.find("product_complete"):
return
if (clue := clue_cls("receive")) is None:
return True
name_scope = ((1580, 220), (1880, 255))
name_img = cropimg(config.recog.gray, name_scope)
name = ocr_rec(name_img) or "好友"
logger.info(f"接收{name}{clue}号线索")
self.tap(name_scope)
@TransitionOn(BaseSolver.waiting_scene)
def _(self):
self.waiting_solver()
@TransitionOn()
def _(self):
ClueMainSolver().run()

View file

@ -1,7 +1,15 @@
import cv2
from datetime import timedelta
import cv2
import networkx as nx
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.utils import config
from mower.utils.graph.utils import DG
from mower.utils.image import cropimg, loadres
from mower.utils.recognize import Scene
from mower.utils.solver import BaseSolver, TransitionOn
from mower.utils.vector import va
clue_size = (162, 216)
@ -45,7 +53,6 @@ for i in range(1, 8):
main_time[i] = va(clue_top_left[i], main_time_offset)
main_scope[i] = tl2p(va(clue_top_left[i], main_offset))
tm_thres = 0.6
exit_pos = (1239, 144)
def clue_cls(scope):
@ -58,3 +65,53 @@ def clue_cls(scope):
if max_val > tm_thres:
return i
return None
class MeetingSolver(BaseSolver, BaseMixin):
solver_name = "会客室"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
@TransitionOn(Scene.INFRA_MAIN)
def _(self):
EnterRoomSolver().run("meeting", detail=False)
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
if self.detect_room() != "meeting":
self.scene_graph_step(Scene.INFRA_MAIN)
return
if self.find("meeting_arrange_check_in"):
return True
self.tap((960, 540))
@TransitionOn()
def _(self):
try:
nx.shortest_path(DG, self.scene(), Scene.INFRA_DETAILS)
self.scene_graph_step(Scene.INFRA_DETAILS)
except nx.NetworkXNoPath:
self.scene_graph_step(Scene.INFRA_MAIN)
class ClueMainSolver(BaseSolver):
solver_name = "主界面"
solver_max_duration = timedelta(minutes=2)
solver_default_scene = None
@TransitionOn(Scene.INFRA_DETAILS)
def _(self):
MeetingSolver().run()
self.tap((500, 1000))
@TransitionOn(Scene.INFRA_CONFIDENTIAL)
def _(self):
return True
@TransitionOn()
def _(self):
try:
nx.shortest_path(DG, self.scene(), Scene.INFRA_CONFIDENTIAL)
self.scene_graph_step(Scene.INFRA_CONFIDENTIAL)
except nx.NetworkXNoPath:
self.scene_graph_step(Scene.INFRA_MAIN)

View file

@ -18,10 +18,10 @@ def todo_complete(solver: BaseSolver):
@edge(Scene.ACTIVITY_ROOM_DETAILS, Scene.INFRA_MAIN)
@edge(Scene.CTRLCENTER_ASSISTANT, Scene.INFRA_MAIN)
@edge(Scene.CLUE_DAILY, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_RECEIVE, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_RECEIVE, Scene.INFRA_DETAILS)
@edge(Scene.CLUE_GIVE_AWAY, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_SUMMARY, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_PLACE, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_PLACE, Scene.INFRA_DETAILS)
@edge(Scene.CLUE_MESSAGE_BOARD, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_MESSAGE_BOARD_FRIEND, Scene.CLUE_MESSAGE_BOARD)
@edge(Scene.CLUE_ACCESS_RECORD, Scene.CLUE_MESSAGE_BOARD)
@ -33,6 +33,12 @@ def infra_back(solver: BaseSolver):
solver.cback(3, id="infra_back")
@edge(Scene.CLUE_RECEIVE, Scene.INFRA_CONFIDENTIAL)
@edge(Scene.CLUE_PLACE, Scene.INFRA_CONFIDENTIAL)
def clue_receive_back(solver: BaseSolver):
solver.tap((960, 1000))
@edge(Scene.RIIC_OPERATOR_SELECT, Scene.INFRA_DETAILS)
def riic_operator_select(solver: BaseSolver):
solver.ctap("confirm_blue", 3)

View file

@ -283,7 +283,7 @@ class Recognizer:
self.scene = Scene.CLUE_GIVE_AWAY
elif self.find("clue/summary"):
self.scene = Scene.CLUE_SUMMARY
elif self.find("clue/filter_all"):
elif self.find("clue/filter_all") or self.find("clue/filter_all_on"):
self.scene = Scene.CLUE_PLACE
elif self.find("clue/message_board_banner"):
self.scene = Scene.CLUE_MESSAGE_BOARD

View file

@ -18,6 +18,7 @@ color = {
"clue/access_records.jpg": (843, 279),
"clue/daily": (526, 623),
"clue/filter_all": (1297, 99),
"clue/filter_all_on": (1299, 100),
"clue/give_away": (25, 18),
"clue/message_board_banner": (19, 1006),
"clue/message_board_collect": (1657, 876),

View file

@ -69,6 +69,7 @@ Res = Literal[
"clue/button_unlock",
"clue/daily",
"clue/filter_all",
"clue/filter_all_on",
"clue/give_away",
"clue/give_away_digit_bg",
"clue/icon_notification",