diff --git a/assets/share/item/synthesize/SYNTHESIZE_INSUFFICIENT.png b/assets/share/item/synthesize/SYNTHESIZE_INSUFFICIENT.png new file mode 100644 index 000000000..004bc15c9 Binary files /dev/null and b/assets/share/item/synthesize/SYNTHESIZE_INSUFFICIENT.png differ diff --git a/module/base/decorator.py b/module/base/decorator.py index f4d693735..e507282aa 100644 --- a/module/base/decorator.py +++ b/module/base/decorator.py @@ -123,6 +123,18 @@ def has_cached_property(obj, name): return name in obj.__dict__ +def set_cached_property(obj, name, value): + """ + Set a cached property. + + Args: + obj: + name (str): + value: + """ + obj.__dict__[name] = value + + def function_drop(rate=0.5, default=None): """ Drop function calls to simulate random emulator stuck, for testing purpose. diff --git a/module/device/connection.py b/module/device/connection.py index 2e19fd286..9be7fbe0f 100644 --- a/module/device/connection.py +++ b/module/device/connection.py @@ -1016,7 +1016,24 @@ class Connection(ConnectionAttr): # Set server # logger.info('Server changed, release resources') # set_server(self.package) + return else: + if self.config.is_cloud_game: + packages = [p for p in packages if p in server_.VALID_CLOUD_PACKAGE] + if len(packages) == 1: + logger.info('Auto package detection found only one package, using it') + self.package = packages[0] + if set_config: + self.config.Emulator_PackageName = server_.to_server(self.package) + return + else: + packages = [p for p in packages if p in server_.VALID_PACKAGE] + if len(packages) == 1: + logger.info('Auto package detection found only one package, using it') + self.package = packages[0] + if set_config: + self.config.Emulator_PackageName = server_.to_server(self.package) + return logger.critical( f'Multiple Star Rail packages found, auto package detection cannot decide which to choose, ' 'please copy one of the available devices listed above to Alas.Emulator.PackageName') diff --git a/module/webui/widgets.py b/module/webui/widgets.py index 7fb24730c..dad26022a 100644 --- a/module/webui/widgets.py +++ b/module/webui/widgets.py @@ -413,10 +413,16 @@ def put_arg_planner(kwargs: T_Output_Kwargs) -> Output | None: if isinstance(total, dict): total = tuple(total.values()) - row = put_scope(f"arg_stored-stored-value-{name}", [ - put_text(f"{progress:.2f}%{eta}").style("--dashboard-bold--"), - put_text(f"{value} / {total}").style("--dashboard-time--"), - ]) + if eta: + row = put_scope(f"arg_stored-stored-value-{name}", [ + put_text(f"{progress:.2f}%{eta}").style("--dashboard-bold--"), + put_text(f"{value} / {total}").style("--dashboard-time--"), + ]) + else: + row = put_scope(f"arg_stored-stored-value-{name}", [ + put_text(f"{progress:.2f}%").style("--dashboard-value--"), + put_text(f"{value} / {total}").style("--dashboard-time--"), + ]) return put_scope( f"arg_container-planner-{name}", diff --git a/tasks/assignment/assignment.py b/tasks/assignment/assignment.py index 8a76ded7d..ef537dfaf 100644 --- a/tasks/assignment/assignment.py +++ b/tasks/assignment/assignment.py @@ -74,6 +74,11 @@ class Assignment(AssignmentClaim, SynthesizeUI): delay = min(self.dispatched.values()) logger.info(f'Delay assignment check to {str(delay)}') self.config.task_delay(target=delay) + # Align server update + update = get_server_next_update(self.config.Scheduler_ServerUpdate) + if update - delay < timedelta(hours=4): + logger.info('Approaching next day, delay to server update instead') + self.config.task_delay(target=update) else: # ValueError: min() arg is an empty sequence logger.error('Empty dispatched list, delay 2 hours instead') diff --git a/tasks/combat/obtain.py b/tasks/combat/obtain.py index b73b5f8e6..cfd6df70f 100644 --- a/tasks/combat/obtain.py +++ b/tasks/combat/obtain.py @@ -253,7 +253,13 @@ class CombatObtain(PlannerMixin): ItemAmount: Arrow_of_the_Beast_Hunter, 85 """ self.planner.load_obtained_amount(items) - self.planner_write() + with self.config.multi_set(): + self.planner_write() + # Sync to dashboard + for item in items: + if item.item.name == 'Credit': + self.config.stored.Credit.value = item.value + return items def obtained_is_full(self, dungeon: DungeonList | None, wave_done=0, obtain_get=True) -> bool: diff --git a/tasks/dungeon/dungeon.py b/tasks/dungeon/dungeon.py index cc4ed9959..490ede9f0 100644 --- a/tasks/dungeon/dungeon.py +++ b/tasks/dungeon/dungeon.py @@ -1,3 +1,4 @@ +from module.base.decorator import set_cached_property from module.base.utils import area_offset from module.logger import logger from tasks.battle_pass.keywords import KEYWORDS_BATTLE_PASS_QUEST @@ -191,6 +192,7 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat): def run(self): self.config.update_battle_pass_quests() self.config.update_daily_quests() + self.check_synthesize() self.called_daily_support = False self.achieved_daily_quest = False self.achieved_weekly_quest = False @@ -309,6 +311,7 @@ class Dungeon(DungeonStamina, DungeonEvent, Combat): def check_synthesize(self): logger.info('Check synthesize') synthesize = Synthesize(config=self.config, device=self.device, task=self.config.task.command) + set_cached_property(synthesize, 'planner', self.planner) if synthesize.synthesize_needed(): synthesize.synthesize_planner() diff --git a/tasks/dungeon/weekly.py b/tasks/dungeon/weekly.py index f187c0b3c..5b0465245 100644 --- a/tasks/dungeon/weekly.py +++ b/tasks/dungeon/weekly.py @@ -55,6 +55,7 @@ class WeeklyDungeon(Dungeon): planner = self.planner.get_weekly() if planner is not None: dungeon = planner + self.is_doing_planner = True logger.attr('DungeonWeekly', dungeon) # UI switches @@ -81,6 +82,7 @@ class WeeklyDungeon(Dungeon): # Combat count = self.dungeon_run(dungeon, wave_limit=min(remain, 3)) + self.is_doing_planner = False logger.attr('achieved_daily_quest', self.achieved_daily_quest) logger.attr('achieved_weekly_quest', self.achieved_weekly_quest) diff --git a/tasks/item/assets/assets_item_synthesize.py b/tasks/item/assets/assets_item_synthesize.py index e6cdabcdb..a9c4bf67c 100644 --- a/tasks/item/assets/assets_item_synthesize.py +++ b/tasks/item/assets/assets_item_synthesize.py @@ -63,6 +63,16 @@ SYNTHESIZE_CONFIRM = ButtonWrapper( button=(730, 641, 998, 675), ), ) +SYNTHESIZE_INSUFFICIENT = ButtonWrapper( + name='SYNTHESIZE_INSUFFICIENT', + share=Button( + file='./assets/share/item/synthesize/SYNTHESIZE_INSUFFICIENT.png', + area=(510, 564, 1220, 594), + search=(490, 544, 1240, 614), + color=(177, 102, 95), + button=(510, 564, 1220, 594), + ), +) SYNTHESIZE_INVENTORY = ButtonWrapper( name='SYNTHESIZE_INVENTORY', share=Button( diff --git a/tasks/item/data_update.py b/tasks/item/data_update.py index 71177328f..8423f74b8 100644 --- a/tasks/item/data_update.py +++ b/tasks/item/data_update.py @@ -7,6 +7,7 @@ from tasks.base.page import page_item from tasks.item.assets.assets_item_data import OCR_DATA from tasks.item.keywords import KEYWORDS_ITEM_TAB from tasks.item.ui import ItemUI +from tasks.planner.model import PlannerMixin class DataDigit(Digit): @@ -16,7 +17,7 @@ class DataDigit(Digit): return super().after_process(result) -class DataUpdate(ItemUI): +class DataUpdate(ItemUI, PlannerMixin): def _get_data(self): """ Page: @@ -53,3 +54,9 @@ class DataUpdate(ItemUI): self.config.stored.Credit.value = credit self.config.stored.StallerJade.value = jade self.config.task_delay(server_update=True) + # Sync to planner + require = self.config.cross_get('Dungeon.Planner.Item_Credit.total', default=0) + if require: + self.config.cross_set('Dungeon.Planner.Item_Credit.value', credit) + self.config.cross_set('Dungeon.Planner.Item_Credit.time', self.config.stored.Credit.time) + self.planner_write() diff --git a/tasks/item/inventory.py b/tasks/item/inventory.py index 3744bfb41..82ab69184 100644 --- a/tasks/item/inventory.py +++ b/tasks/item/inventory.py @@ -94,6 +94,12 @@ class InventoryManager: if count == 1: return mids elif count == 2: + # Only one row, [173.5 175. ] + mid_diff_mean = np.mean(mid_diff_range) + diff = max(mids) - min(mids) + if diff < mid_diff_mean * 0.3: + return np.mean(mids).reshape((1,)) + # Double rows return mids # print(mids) encourage = self.COINCIDENT_POINT_ENCOURAGE_DISTANCE ** 2 @@ -179,6 +185,8 @@ class InventoryManager: area = self.inventory.area x_list = np.unique(np.sort(points[:, 0])) y_list = np.unique(np.sort(points[:, 1])) + # print(x_list) + # print(y_list) x_list = self.mid_cleanse( x_list, mid_diff_range=(self.GRID_DELTA[0] - 3, self.GRID_DELTA[0] + 3), @@ -189,6 +197,8 @@ class InventoryManager: mid_diff_range=(self.GRID_DELTA[1] - 3, self.GRID_DELTA[1] + 3), edge_range=(area[1], area[3]) ) + # print(x_list) + # print(y_list) def is_near_existing(p): diff = np.linalg.norm(points - p, axis=1) diff --git a/tasks/item/synthesize.py b/tasks/item/synthesize.py index 60f0ff3c6..8a4ab1805 100644 --- a/tasks/item/synthesize.py +++ b/tasks/item/synthesize.py @@ -387,10 +387,14 @@ class Synthesize(CombatObtain, ItemUI): out: page_synthesize, SYNTHESIZE_CONFIRM """ logger.hr('Synthesize confirm') + self.interval_clear([SYNTHESIZE_CONFIRM, page_synthesize.check_button]) def appear_confirm(): return self.image_color_count(SYNTHESIZE_CONFIRM, color=(226, 229, 232), threshold=221, count=1000) + def appear_insufficient(): + return self.image_color_count(SYNTHESIZE_INSUFFICIENT, color=(172, 95, 87), threshold=221, count=5000) + # SYNTHESIZE_CONFIRM -> reward_appear while 1: if skip_first_screenshot: @@ -404,6 +408,7 @@ class Synthesize(CombatObtain, ItemUI): break # Click if self.handle_popup_confirm(): + self.interval_reset(page_synthesize.check_button) continue if appear_confirm() and self.ui_page_appear(page_synthesize, interval=2): self.device.click(SYNTHESIZE_CONFIRM) @@ -421,10 +426,15 @@ class Synthesize(CombatObtain, ItemUI): if appear_confirm(): logger.info('Synthesize end') break + if appear_insufficient(): + logger.info('Synthesize end, item insufficient') + break # Click if self.handle_reward(click_button=SYNTHESIZE_MINUS): continue + self.interval_clear([SYNTHESIZE_CONFIRM, page_synthesize.check_button]) + def synthesize_exit(self, skip_first_screenshot=True): """ Pages: diff --git a/tasks/planner/model.py b/tasks/planner/model.py index ba14f3d4b..bf29b9bdc 100644 --- a/tasks/planner/model.py +++ b/tasks/planner/model.py @@ -652,6 +652,15 @@ class PlannerMixin(UI): if add: planner.add_planner_result(self.planner) + # Load from dashboard + try: + row = planner.rows['Credit'] + value = self.config.stored.Credit.value + if value: + row.value = value + except KeyError: + pass + self.planner_write(planner) @cached_property