Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion tasks/DailyTrifles/script_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
2 changes: 2 additions & 0 deletions tasks/GameUi/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
# 阴阳寮页面标志
Expand Down
2 changes: 1 addition & 1 deletion tasks/GameUi/default_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions tasks/GameUi/page/image_main.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Binary file added tasks/GameUi/page/page_check_mall_purple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions tasks/RichMan/mall/navbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
150 changes: 150 additions & 0 deletions tests/test_mall_appearance.py
Original file line number Diff line number Diff line change
@@ -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()
Loading