diff --git a/assets/cn/freebies/support_reward/PROFILE.png b/assets/cn/freebies/support_reward/PROFILE.png new file mode 100644 index 000000000..2c25e595d Binary files /dev/null and b/assets/cn/freebies/support_reward/PROFILE.png differ diff --git a/assets/en/freebies/support_reward/PROFILE.png b/assets/en/freebies/support_reward/PROFILE.png new file mode 100644 index 000000000..71b586e0b Binary files /dev/null and b/assets/en/freebies/support_reward/PROFILE.png differ diff --git a/assets/share/freebies/support_reward/CAN_GET_REWARD.png b/assets/share/freebies/support_reward/CAN_GET_REWARD.png new file mode 100644 index 000000000..3a8c3074a Binary files /dev/null and b/assets/share/freebies/support_reward/CAN_GET_REWARD.png differ diff --git a/assets/share/freebies/support_reward/CLICKING_REWARD.png b/assets/share/freebies/support_reward/CLICKING_REWARD.png new file mode 100644 index 000000000..e0b3c597a Binary files /dev/null and b/assets/share/freebies/support_reward/CLICKING_REWARD.png differ diff --git a/assets/share/freebies/support_reward/IN_PROFILE.png b/assets/share/freebies/support_reward/IN_PROFILE.png new file mode 100644 index 000000000..2e0aadee8 Binary files /dev/null and b/assets/share/freebies/support_reward/IN_PROFILE.png differ diff --git a/assets/share/freebies/support_reward/MENU_TO_PROFILE.png b/assets/share/freebies/support_reward/MENU_TO_PROFILE.png new file mode 100644 index 000000000..4716165d0 Binary files /dev/null and b/assets/share/freebies/support_reward/MENU_TO_PROFILE.png differ diff --git a/config/template.json b/config/template.json index d5d378a1a..075a78db3 100644 --- a/config/template.json +++ b/config/template.json @@ -155,6 +155,17 @@ "StallerJade": {} } }, + "Freebies": { + "Scheduler": { + "Enable": true, + "NextRun": "2020-01-01 00:00:00", + "Command": "Freebies", + "ServerUpdate": "04:00" + }, + "SupportReward": { + "Collect": true + } + }, "Rogue": { "Scheduler": { "Enable": false, diff --git a/module/base/button.py b/module/base/button.py index 6d6de85f0..0455f3867 100644 --- a/module/base/button.py +++ b/module/base/button.py @@ -147,10 +147,10 @@ class ButtonWrapper(Resource): self.name = name self.data_buttons = kwargs self._matched_button: t.Optional[Button] = None - self.resource_add(self.name) + self.resource_add(f'{name}:{next(self.iter_buttons(), None)}') def resource_release(self): - del_cached_property(self, 'assets') + del_cached_property(self, 'buttons') self._matched_button = None def __str__(self): diff --git a/module/base/resource.py b/module/base/resource.py index 40d930ed7..fd9d7cd58 100644 --- a/module/base/resource.py +++ b/module/base/resource.py @@ -1,11 +1,11 @@ import re -import module.config.server as server -from module.base.decorator import cached_property, del_cached_property +from module.base.decorator import cached_property -def get_assets_from_file(file, regex): +def get_assets_from_file(file): assets = set() + regex = re.compile(r"file='(.*?)'") with open(file, 'r', encoding='utf-8') as f: for row in f.readlines(): result = regex.search(row) @@ -20,11 +20,12 @@ class PreservedAssets: assets = set() assets |= get_assets_from_file( file='./tasks/base/assets/assets_base_page.py', - regex=re.compile(r'^([A-Za-z][A-Za-z0-9_]+) = ') ) assets |= get_assets_from_file( file='./tasks/base/assets/assets_base_popup.py', - regex=re.compile(r'^([A-Za-z][A-Za-z0-9_]+) = ') + ) + assets |= get_assets_from_file( + file='./tasks/base/assets/assets_base_main_page.py', ) return assets @@ -44,11 +45,13 @@ class Resource: @classmethod def is_loaded(cls, obj): - if hasattr(obj, '_image') and obj._image is None: - return False - elif hasattr(obj, 'image') and obj.image is None: - return False - return True + if hasattr(obj, '_image') and obj._image is not None: + return True + if hasattr(obj, 'image') and obj.image is not None: + return True + if hasattr(obj, 'buttons') and obj.buttons is not None: + return True + return False @classmethod def resource_show(cls): @@ -56,11 +59,16 @@ class Resource: logger.hr('Show resource') for key, obj in cls.instances.items(): if cls.is_loaded(obj): - continue - logger.info(f'{obj}: {key}') + logger.info(f'{obj}: {key}') def release_resources(next_task=''): + # Release all OCR models + # det models take 400MB + if not next_task: + from module.ocr.models import OCR_MODEL + OCR_MODEL.resource_release() + # Release assets cache # module.ui has about 80 assets and takes about 3MB # Alas has about 800 assets, but they are not all loaded. diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 24ea02e66..e29514b70 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1110,6 +1110,39 @@ } } }, + "Freebies": { + "Scheduler": { + "Enable": { + "type": "checkbox", + "value": true, + "option": [ + true, + false + ] + }, + "NextRun": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + }, + "Command": { + "type": "input", + "value": "Freebies", + "display": "hide" + }, + "ServerUpdate": { + "type": "input", + "value": "04:00", + "display": "hide" + } + }, + "SupportReward": { + "Collect": { + "type": "checkbox", + "value": true + } + } + }, "Rogue": { "Scheduler": { "Enable": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index f17335e5c..42d67065d 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -126,6 +126,8 @@ DungeonStorage: stored: StoredSimulatedUniverse order: 6 color: "#8fb5fe" +SupportReward: + Collect: true Weekly: Name: diff --git a/module/config/argument/default.yaml b/module/config/argument/default.yaml index 5292fee54..e7e1ea127 100644 --- a/module/config/argument/default.yaml +++ b/module/config/argument/default.yaml @@ -22,3 +22,6 @@ BattlePass: Assignment: Scheduler: Enable: true +Freebies: + Scheduler: + Enable: true diff --git a/module/config/argument/menu.json b/module/config/argument/menu.json index 91ecb58e4..2d57fb73b 100644 --- a/module/config/argument/menu.json +++ b/module/config/argument/menu.json @@ -16,7 +16,8 @@ "DailyQuest", "BattlePass", "Assignment", - "DataUpdate" + "DataUpdate", + "Freebies" ] } } \ No newline at end of file diff --git a/module/config/argument/task.yaml b/module/config/argument/task.yaml index e7bf3662c..d10e640b8 100644 --- a/module/config/argument/task.yaml +++ b/module/config/argument/task.yaml @@ -44,7 +44,9 @@ Daily: DataUpdate: - Scheduler - ItemStorage - + Freebies: + - Scheduler + - SupportReward # ==================== Rogue ==================== Rogue: diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 6960a0889..eff51cd67 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -61,6 +61,9 @@ class GeneratedConfig: DungeonStorage_EchoOfWar = {} DungeonStorage_SimulatedUniverse = {} + # Group `SupportReward` + SupportReward_Collect = True + # Group `Weekly` Weekly_Name = 'Echo_of_War_Divine_Seed' # Echo_of_War_Destruction_Beginning, Echo_of_War_End_of_the_Eternal_Freeze, Echo_of_War_Divine_Seed Weekly_Team = 1 # 1, 2, 3, 4, 5, 6 diff --git a/module/config/config_manual.py b/module/config/config_manual.py index 1d2cc8458..7f8c9904f 100644 --- a/module/config/config_manual.py +++ b/module/config/config_manual.py @@ -10,7 +10,8 @@ class ManualConfig: SCHEDULER_PRIORITY = """ Restart - > BattlePass > DailyQuest > Assignment > DataUpdate + > BattlePass > DailyQuest > Assignment + > Freebies > DataUpdate > Weekly > Dungeon """ diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 4bda469d1..d9cd94c96 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -46,6 +46,10 @@ "name": "Dashboard Upd", "help": "" }, + "Freebies": { + "name": "Freebies", + "help": "" + }, "Rogue": { "name": "Simulated Universe", "help": "Simulated Universe is in development, task will not be run" @@ -410,6 +414,16 @@ "help": "" } }, + "SupportReward": { + "_info": { + "name": "Support Reward", + "help": "" + }, + "Collect": { + "name": "Receive support reward", + "help": "" + } + }, "Weekly": { "_info": { "name": "Echo of War Settings", diff --git a/module/config/i18n/es-ES.json b/module/config/i18n/es-ES.json index 1c3e140cf..6b5e1c944 100644 --- a/module/config/i18n/es-ES.json +++ b/module/config/i18n/es-ES.json @@ -46,6 +46,10 @@ "name": "Actualizar panel", "help": "" }, + "Freebies": { + "name": "Freebies", + "help": "" + }, "Rogue": { "name": "Universo Simulado", "help": "El Universo Simulado está en desarrollo, y no se ejecutará" @@ -410,6 +414,16 @@ "help": "" } }, + "SupportReward": { + "_info": { + "name": "Support Reward", + "help": "" + }, + "Collect": { + "name": "Receive support reward", + "help": "" + } + }, "Weekly": { "_info": { "name": "Ajustes de Ecos de la guerra", diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 1738f634d..097abce6d 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -46,6 +46,10 @@ "name": "Task.DataUpdate.name", "help": "Task.DataUpdate.help" }, + "Freebies": { + "name": "他ギフト", + "help": "" + }, "Rogue": { "name": "Task.Rogue.name", "help": "Task.Rogue.help" @@ -410,6 +414,16 @@ "help": "DungeonStorage.SimulatedUniverse.help" } }, + "SupportReward": { + "_info": { + "name": "サポートボーナス", + "help": "" + }, + "Collect": { + "name": "收取サポートボーナス", + "help": "" + } + }, "Weekly": { "_info": { "name": "Weekly._info.name", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index f78f4936b..b5d71d7a5 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -46,6 +46,10 @@ "name": "仪表盘更新", "help": "" }, + "Freebies": { + "name": "白嫖奖励", + "help": "" + }, "Rogue": { "name": "模拟宇宙", "help": "模拟宇宙还在开发中,任务不会被运行" @@ -410,6 +414,16 @@ "help": "" } }, + "SupportReward": { + "_info": { + "name": "支援奖励", + "help": "" + }, + "Collect": { + "name": "领取支援奖励", + "help": "" + } + }, "Weekly": { "_info": { "name": "历战余响设置", diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 59b996030..9aa271e3f 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -46,6 +46,10 @@ "name": "儀表板更新", "help": "" }, + "Freebies": { + "name": "免費獎勵", + "help": "" + }, "Rogue": { "name": "模擬宇宙", "help": "模擬宇宙還在開發中,任務不會被運行" @@ -410,6 +414,16 @@ "help": "" } }, + "SupportReward": { + "_info": { + "name": "支援獎勵", + "help": "" + }, + "Collect": { + "name": "收取支援獎勵", + "help": "" + } + }, "Weekly": { "_info": { "name": "歷戰餘響設定", diff --git a/module/ocr/models.py b/module/ocr/models.py index f0cefa03e..c4f207c0a 100644 --- a/module/ocr/models.py +++ b/module/ocr/models.py @@ -1,6 +1,6 @@ from pponnxcr import TextSystem as TextSystem_ -from module.base.decorator import cached_property +from module.base.decorator import cached_property, del_cached_property from module.exception import ScriptError DIC_LANG_TO_MODEL = { @@ -56,6 +56,12 @@ class OcrModel: except AttributeError: raise ScriptError(f'OCR model under lang "{lang}" does not exists') + def resource_release(self): + del_cached_property(self, 'zhs') + del_cached_property(self, 'en') + del_cached_property(self, 'ja') + del_cached_property(self, 'zht') + @cached_property def zhs(self): return TextSystem('zhs') @@ -66,7 +72,7 @@ class OcrModel: @cached_property def ja(self): - return TextSystem('zht') + return TextSystem('ja') @cached_property def zht(self): diff --git a/src.py b/src.py index 66a302530..c706eb32c 100644 --- a/src.py +++ b/src.py @@ -46,6 +46,9 @@ class StarRailCopilot(AzurLaneAutoScript): from tasks.item.data_update import DataUpdate DataUpdate(config=self.config, device=self.device).run() + def freebies(self): + from tasks.freebies.freebies import Freebies + Freebies(config=self.config, device=self.device).run() if __name__ == '__main__': src = StarRailCopilot('src') diff --git a/tasks/base/popup.py b/tasks/base/popup.py index a74b0d1a4..074140bc4 100644 --- a/tasks/base/popup.py +++ b/tasks/base/popup.py @@ -1,18 +1,26 @@ from module.base.base import ModuleBase +from module.logger import logger from tasks.base.assets.assets_base_popup import * class PopupHandler(ModuleBase): - def handle_reward(self, interval=5) -> bool: + def handle_reward(self, interval=5, click_button=None) -> bool: """ Args: interval: + click_button: Set a button to click Returns: If handled. """ - if self.appear_then_click(GET_REWARD, interval=interval): - return True + if click_button is None: + if self.appear_then_click(GET_REWARD, interval=interval): + return True + else: + if self.appear(GET_REWARD, interval=interval): + logger.info(f'{GET_REWARD} -> {click_button}') + self.device.click(click_button) + return True return False diff --git a/tasks/dungeon/ui.py b/tasks/dungeon/ui.py index 5b5029928..91da95fd1 100644 --- a/tasks/dungeon/ui.py +++ b/tasks/dungeon/ui.py @@ -1,3 +1,5 @@ +import re + import numpy as np from module.base.base import ModuleBase @@ -70,6 +72,9 @@ class OcrDungeonList(Ocr): if self.lang == 'cn': result = result.replace('翼', '巽') # 巽风之形 result = result.replace('皖A0', '50').replace('皖', '') + # 燔灼之形•凝滞虚影 + result = result.replace('熠', '燔') + result = re.sub('^灼之形', '燔灼之形', result) return result @@ -411,7 +416,7 @@ class DungeonUI(UI): Examples: from tasks.dungeon.keywords import KEYWORDS_DUNGEON_LIST - self = DungeonUI('alas') + self = DungeonUI('src') self.device.screenshot() self.dungeon_tab_goto(KEYWORDS_DUNGEON_TAB.Survival_Index) self.dungeon_goto(KEYWORDS_DUNGEON_LIST.Calyx_Crimson_Harmony) diff --git a/tasks/forgotten_hall/ui.py b/tasks/forgotten_hall/ui.py index 1e51cb4f9..aa389111a 100644 --- a/tasks/forgotten_hall/ui.py +++ b/tasks/forgotten_hall/ui.py @@ -13,7 +13,7 @@ from tasks.base.assets.assets_base_page import FORGOTTEN_HALL_CHECK, MAP_EXIT from tasks.dungeon.keywords import DungeonList, KEYWORDS_DUNGEON_LIST, KEYWORDS_DUNGEON_TAB from tasks.dungeon.ui import DungeonUI from tasks.forgotten_hall.assets.assets_forgotten_hall_ui import * -from tasks.forgotten_hall.keywords import ForgottenHallStage +from tasks.forgotten_hall.keywords import ForgottenHallStage, KEYWORDS_FORGOTTEN_HALL_STAGE from tasks.forgotten_hall.team import ForgottenHallTeam from tasks.map.control.joystick import MapControlJoystick @@ -70,7 +70,11 @@ class DraggableStageList(DraggableList): while 1: result = super().insight_row(row, main=main, skip_first_screenshot=skip_first_screenshot) if not result: - return False + if row == KEYWORDS_FORGOTTEN_HALL_STAGE.Stage_1: + # Must have stage 1, retry if not found + continue + else: + return False if skip_first_screenshot: skip_first_screenshot = False diff --git a/tasks/freebies/assets/assets_freebies_support_reward.py b/tasks/freebies/assets/assets_freebies_support_reward.py new file mode 100644 index 000000000..a8e03e558 --- /dev/null +++ b/tasks/freebies/assets/assets_freebies_support_reward.py @@ -0,0 +1,62 @@ +from module.base.button import Button, ButtonWrapper + +# This file was auto-generated, do not modify it manually. To generate: +# ``` python -m dev_tools.button_extract ``` + +CAN_GET_REWARD = ButtonWrapper( + name='CAN_GET_REWARD', + share=Button( + file='./assets/share/freebies/support_reward/CAN_GET_REWARD.png', + area=(1055, 266, 1106, 310), + search=(1035, 246, 1126, 330), + color=(229, 186, 123), + button=(1055, 266, 1106, 310), + ), +) +CLICKING_REWARD = ButtonWrapper( + name='CLICKING_REWARD', + share=Button( + file='./assets/share/freebies/support_reward/CLICKING_REWARD.png', + area=(1066, 268, 1105, 307), + search=(1046, 248, 1125, 327), + color=(197, 161, 111), + button=(1066, 268, 1105, 307), + ), +) +IN_PROFILE = ButtonWrapper( + name='IN_PROFILE', + share=Button( + file='./assets/share/freebies/support_reward/IN_PROFILE.png', + area=(643, 106, 673, 131), + search=(623, 86, 693, 151), + color=(74, 67, 58), + button=(643, 106, 673, 131), + ), +) +MENU_TO_PROFILE = ButtonWrapper( + name='MENU_TO_PROFILE', + share=Button( + file='./assets/share/freebies/support_reward/MENU_TO_PROFILE.png', + area=(1123, 82, 1149, 97), + search=(1103, 62, 1169, 117), + color=(226, 226, 225), + button=(1123, 82, 1149, 97), + ), +) +PROFILE = ButtonWrapper( + name='PROFILE', + cn=Button( + file='./assets/cn/freebies/support_reward/PROFILE.png', + area=(890, 99, 1111, 131), + search=(870, 79, 1131, 151), + color=(205, 206, 206), + button=(890, 99, 1111, 131), + ), + en=Button( + file='./assets/en/freebies/support_reward/PROFILE.png', + area=(907, 102, 1092, 132), + search=(887, 82, 1112, 152), + color=(190, 189, 189), + button=(907, 102, 1092, 132), + ), +) diff --git a/tasks/freebies/freebies.py b/tasks/freebies/freebies.py new file mode 100644 index 000000000..d8128c047 --- /dev/null +++ b/tasks/freebies/freebies.py @@ -0,0 +1,14 @@ +from module.logger import logger +from module.base.base import ModuleBase +from tasks.freebies.support_reward import SupportReward + +class Freebies(ModuleBase): + def run(self): + """ + Run all freebie tasks + """ + if self.config.SupportReward_Collect: + logger.hr('Support Reward') + SupportReward(config=self.config, device=self.device).run() + + self.config.task_delay(server_update=True) \ No newline at end of file diff --git a/tasks/freebies/support_reward.py b/tasks/freebies/support_reward.py new file mode 100644 index 000000000..8c9d1ef7c --- /dev/null +++ b/tasks/freebies/support_reward.py @@ -0,0 +1,112 @@ +from module.base.timer import Timer +from module.logger import logger +from tasks.base.assets.assets_base_page import CLOSE, MENU_CHECK +from tasks.base.assets.assets_base_popup import GET_REWARD +from tasks.base.page import page_menu +from tasks.base.ui import UI +from tasks.freebies.assets.assets_freebies_support_reward import ( + CAN_GET_REWARD, + IN_PROFILE, + MENU_TO_PROFILE, + PROFILE +) + + +class SupportReward(UI): + + def run(self): + """ + Run get support reward task + """ + logger.hr('Support reward', level=1) + self.ui_ensure(page_menu) + + self._goto_profile() + self._get_reward() + self._goto_menu() + + def _goto_profile(self): + """ + Pages: + in: MENU + out: PROFILE + """ + skip_first_screenshot = False + logger.info('Going to profile') + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.appear(IN_PROFILE): + logger.info('Successfully in profile') + return True + + if self.appear_then_click(MENU_TO_PROFILE): + continue + + if self.appear_then_click(PROFILE): + continue + + def _get_reward(self, skip_first_screenshot=True): + """ + Pages: + in: PROFILE + out: GET_REWARD + """ + logger.info('Getting reward') + claimed = False + empty = Timer(0.3, count=1).start() + timeout = Timer(5).start() + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if not claimed and empty.reached(): + logger.info('No reward') + break + if self.appear(GET_REWARD): + logger.info('Got reward') + break + if timeout.reached(): + logger.warning('Get support reward timeout') + break + + if self.appear_then_click(CAN_GET_REWARD, similarity=0.70, interval=2): + claimed = True + timeout.reset() + continue + + def _goto_menu(self): + """ + Pages: + in: PROFILE or GET_REWARD + out: MENU + """ + skip_first_screenshot = False + logger.info('Going to menu') + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.appear(MENU_CHECK): + return True + + if self.appear(IN_PROFILE, interval=2): + logger.info(f'{IN_PROFILE} -> {CLOSE}') + self.device.click(CLOSE) + continue + if self.handle_reward(click_button=CAN_GET_REWARD): + # # Avoid clicking on some other buttons + continue + + +if __name__ == '__main__': + self = SupportReward('src') + self.device.screenshot() + self.run()