diff --git a/assets/cn/freebies/MAIL_SELECT_ALL.png b/assets/cn/freebies/MAIL_SELECT_ALL.png new file mode 100644 index 000000000..f4bf046f8 Binary files /dev/null and b/assets/cn/freebies/MAIL_SELECT_ALL.png differ diff --git a/assets/cn/template/TEMPLATE_SIREN_AmagiMasked.gif b/assets/cn/template/TEMPLATE_SIREN_AmagiMasked.gif new file mode 100644 index 000000000..0b167b20d Binary files /dev/null and b/assets/cn/template/TEMPLATE_SIREN_AmagiMasked.gif differ diff --git a/assets/cn/template/TEMPLATE_STAGE_CLEAR_20240725.gif b/assets/cn/template/TEMPLATE_STAGE_CLEAR_20240725.gif new file mode 100644 index 000000000..6c8a15347 Binary files /dev/null and b/assets/cn/template/TEMPLATE_STAGE_CLEAR_20240725.gif differ diff --git a/assets/cn/template/TEMPLATE_STAGE_CLEAR_20240725.png b/assets/cn/template/TEMPLATE_STAGE_CLEAR_20240725.png deleted file mode 100644 index af27d9a11..000000000 Binary files a/assets/cn/template/TEMPLATE_STAGE_CLEAR_20240725.png and /dev/null differ diff --git a/assets/en/freebies/MAIL_SELECT_ALL.png b/assets/en/freebies/MAIL_SELECT_ALL.png new file mode 100644 index 000000000..f4bf046f8 Binary files /dev/null and b/assets/en/freebies/MAIL_SELECT_ALL.png differ diff --git a/assets/en/template/TEMPLATE_SIREN_AmagiMasked.gif b/assets/en/template/TEMPLATE_SIREN_AmagiMasked.gif new file mode 100644 index 000000000..0b167b20d Binary files /dev/null and b/assets/en/template/TEMPLATE_SIREN_AmagiMasked.gif differ diff --git a/assets/en/template/TEMPLATE_STAGE_CLEAR_20240725.gif b/assets/en/template/TEMPLATE_STAGE_CLEAR_20240725.gif new file mode 100644 index 000000000..6c8a15347 Binary files /dev/null and b/assets/en/template/TEMPLATE_STAGE_CLEAR_20240725.gif differ diff --git a/assets/en/template/TEMPLATE_STAGE_CLEAR_20240725.png b/assets/en/template/TEMPLATE_STAGE_CLEAR_20240725.png deleted file mode 100644 index af27d9a11..000000000 Binary files a/assets/en/template/TEMPLATE_STAGE_CLEAR_20240725.png and /dev/null differ diff --git a/assets/jp/freebies/MAIL_SELECT_ALL.png b/assets/jp/freebies/MAIL_SELECT_ALL.png new file mode 100644 index 000000000..5152ddf98 Binary files /dev/null and b/assets/jp/freebies/MAIL_SELECT_ALL.png differ diff --git a/assets/jp/template/TEMPLATE_SIREN_AmagiMasked.gif b/assets/jp/template/TEMPLATE_SIREN_AmagiMasked.gif new file mode 100644 index 000000000..0b167b20d Binary files /dev/null and b/assets/jp/template/TEMPLATE_SIREN_AmagiMasked.gif differ diff --git a/assets/jp/template/TEMPLATE_STAGE_CLEAR_20240725.gif b/assets/jp/template/TEMPLATE_STAGE_CLEAR_20240725.gif new file mode 100644 index 000000000..6c8a15347 Binary files /dev/null and b/assets/jp/template/TEMPLATE_STAGE_CLEAR_20240725.gif differ diff --git a/assets/jp/template/TEMPLATE_STAGE_CLEAR_20240725.png b/assets/jp/template/TEMPLATE_STAGE_CLEAR_20240725.png deleted file mode 100644 index af27d9a11..000000000 Binary files a/assets/jp/template/TEMPLATE_STAGE_CLEAR_20240725.png and /dev/null differ diff --git a/assets/jp/ui_white/MAIL_CHECK.png b/assets/jp/ui_white/MAIL_CHECK.png new file mode 100644 index 000000000..6f87f646c Binary files /dev/null and b/assets/jp/ui_white/MAIL_CHECK.png differ diff --git a/assets/tw/freebies/MAIL_SELECT_ALL.png b/assets/tw/freebies/MAIL_SELECT_ALL.png new file mode 100644 index 000000000..f4bf046f8 Binary files /dev/null and b/assets/tw/freebies/MAIL_SELECT_ALL.png differ diff --git a/assets/tw/template/TEMPLATE_SIREN_AmagiMasked.gif b/assets/tw/template/TEMPLATE_SIREN_AmagiMasked.gif new file mode 100644 index 000000000..0b167b20d Binary files /dev/null and b/assets/tw/template/TEMPLATE_SIREN_AmagiMasked.gif differ diff --git a/assets/tw/template/TEMPLATE_STAGE_CLEAR_20240725.gif b/assets/tw/template/TEMPLATE_STAGE_CLEAR_20240725.gif new file mode 100644 index 000000000..6c8a15347 Binary files /dev/null and b/assets/tw/template/TEMPLATE_STAGE_CLEAR_20240725.gif differ diff --git a/assets/tw/template/TEMPLATE_STAGE_CLEAR_20240725.png b/assets/tw/template/TEMPLATE_STAGE_CLEAR_20240725.png deleted file mode 100644 index af27d9a11..000000000 Binary files a/assets/tw/template/TEMPLATE_STAGE_CLEAR_20240725.png and /dev/null differ diff --git a/campaign/Readme.md b/campaign/Readme.md index 499f0110d..2d6484a2a 100644 --- a/campaign/Readme.md +++ b/campaign/Readme.md @@ -203,3 +203,6 @@ To add a new event, add a new row in here, and run `python -m module.config.conf | 20240711 | event 20211229 cn | Tower of Transcendence Rerun | - | - | -  | 復刻逆轉彩虹之塔 | | 20240718 | event 20220526 cn | Pledge of the Radiant Court Rerun | 复刻泠誓光庭 | Pledge of the Radiant Court Rerun | 復刻诚閃の剣 搖光の城 | - | | 20240725 | event 20240725 cn | Interlude of Illusions | 幻梦间奏曲 | Interlude of Illusions | 夢幻の間奏曲 | - | +| 20240725 | raid 20240328 | From Zero to Hero | - | - | - | 從零開始的魔王討伐之旅 | +| 20240815 | event 20240815 cn | Windborne Steel Wings | 铁翼擎风 | Windborne Steel Wings | 錬翼空翔 | - | +| 20240815 | event 20240425 cn | Heart-Linking Harmony | - | - | - | 共鳴的PASSION | diff --git a/campaign/campaign_main/campaign_12_2_leveling.py b/campaign/campaign_main/campaign_12_2_leveling.py deleted file mode 100644 index acc7444f6..000000000 --- a/campaign/campaign_main/campaign_12_2_leveling.py +++ /dev/null @@ -1,80 +0,0 @@ -from campaign.campaign_main.campaign_12_1 import Config as ConfigBase -from module.campaign.campaign_base import CampaignBase -from module.logger import logger -from module.map.map_base import CampaignMap -from module.map.map_grids import RoadGrids, SelectedGrids - -MAP = CampaignMap() -MAP.shape = 'I7' -MAP.camera_data = ['D2', 'D5', 'F2', 'F5'] -MAP.camera_data_spawn_point = ['D2', 'D5'] -MAP.map_data = """ - ++ MB ME ME ++ -- ME Me -- - ++ -- Me -- Me -- Me -- ++ - MB ME ++ ME SP ME -- ME ++ - MB __ ME -- SP ++ ++ __ Me - ++ -- -- Me ME -- ME -- ME - -- ME ME ++ -- -- Me ME -- - ME -- Me -- ME ME -- ++ ++ -""" -MAP.weight_data = """ - 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 -""" -MAP.spawn_data = [ - {'battle': 0, 'enemy': 3}, - {'battle': 1, 'enemy': 2}, - {'battle': 2, 'enemy': 1}, - {'battle': 3, 'enemy': 1}, - {'battle': 4, 'enemy': 1}, - {'battle': 5}, - {'battle': 6, 'boss': 1}, -] -A1, B1, C1, D1, E1, F1, G1, H1, I1, \ -A2, B2, C2, D2, E2, F2, G2, H2, I2, \ -A3, B3, C3, D3, E3, F3, G3, H3, I3, \ -A4, B4, C4, D4, E4, F4, G4, H4, I4, \ -A5, B5, C5, D5, E5, F5, G5, H5, I5, \ -A6, B6, C6, D6, E6, F6, G6, H6, I6, \ -A7, B7, C7, D7, E7, F7, G7, H7, I7, \ - = MAP.flatten() - - -class Config(ConfigBase): - ENABLE_AUTO_SEARCH = False - - -class Campaign(CampaignBase): - MAP = MAP - s3_enemy_count = 0 - - def check_s3_enemy(self): - if self.battle_count == 0: - self.s3_enemy_count = 0 - elif self.battle_count >= 5: - self.withdraw() - - current = self.map.select(is_enemy=True, enemy_scale=2) \ - .add(self.map.select(is_enemy=True, enemy_scale=1)) \ - .count - logger.attr('S2_enemy', current) - - if self.s3_enemy_count >= self.config.C122MediumLeveling_LargeEnemyTolerance and current == 0: - self.withdraw() - - def battle_0(self): - self.check_s3_enemy() - if self.clear_enemy(scale=(2,), genre=['light', 'main', 'treasure', 'enemy', 'carrier']): - return True - if self.clear_enemy(scale=(1,)): - return True - if self.clear_enemy(scale=(3,), genre=['light', 'carrier', 'enemy', 'treasure', 'main']): - self.s3_enemy_count += 1 - return True - - return self.battle_default() diff --git a/campaign/campaign_main/campaign_12_4_leveling.py b/campaign/campaign_main/campaign_12_4_leveling.py deleted file mode 100644 index f9120e0f2..000000000 --- a/campaign/campaign_main/campaign_12_4_leveling.py +++ /dev/null @@ -1,93 +0,0 @@ -from campaign.campaign_main.campaign_12_1 import Config as ConfigBase -from module.campaign.campaign_base import CampaignBase -from module.logger import logger -from module.map.map_base import CampaignMap -from module.map.map_grids import RoadGrids, SelectedGrids - -MAP = CampaignMap('12-4') -MAP.shape = 'K8' -MAP.camera_data = ['D2', 'D6', 'H2', 'H6'] -MAP.camera_data_spawn_point = ['D6'] -MAP.map_data = """ - MB MB ME -- ME ++ ++ ++ MB MB ++ - ME ++ -- ME -- MA ++ ++ ME Me ++ - -- ME __ Me Me -- Me Me -- Me -- - ++ -- ME ++ ++ Me ME __ ++ ++ ME - ++ ME ME -- ME ME -- ME -- ++ -- - ++ __ Me Me -- Me ME ++ __ -- ME - ME -- Me -- Me -- Me -- -- ME -- - -- -- -- ME SP SP ++ ++ ++ ME -- -""" -MAP.weight_data = """ - 50 50 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 50 50 - 50 50 50 50 50 50 50 50 50 50 50 -""" -MAP.spawn_data = [ - {'battle': 0, 'enemy': 2}, - {'battle': 1, 'enemy': 2}, - {'battle': 2, 'enemy': 2}, - {'battle': 3, 'enemy': 1}, - {'battle': 4, 'enemy': 1}, - {'battle': 5}, - {'battle': 6, 'boss': 1}, -] -A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, \ -A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, \ -A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, \ -A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, \ -A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, \ -A6, B6, C6, D6, E6, F6, G6, H6, I6, J6, K6, \ -A7, B7, C7, D7, E7, F7, G7, H7, I7, J7, K7, \ -A8, B8, C8, D8, E8, F8, G8, H8, I8, J8, K8, \ - = MAP.flatten() - - -class Config(ConfigBase): - ENABLE_AUTO_SEARCH = False - - -class Campaign(CampaignBase): - MAP = MAP - s3_enemy_count = 0 - non_s3_enemy_count = 0 - - def check_s3_enemy(self): - if self.battle_count == 0: - self.s3_enemy_count = 0 - self.non_s3_enemy_count = 0 - - current = self.map.select(is_enemy=True, enemy_scale=3).count - logger.attr('S3_enemy', current) - - if self.battle_count == self.config.C124LargeLeveling_NonLargeEnterTolerance \ - and self.config.C124LargeLeveling_NonLargeRetreatTolerance < 10: - if self.s3_enemy_count + current == 0: - self.withdraw() - elif self.battle_count > self.config.C124LargeLeveling_NonLargeEnterTolerance: - if self.non_s3_enemy_count >= self.config.C124LargeLeveling_NonLargeRetreatTolerance and current == 0: - self.withdraw() - - def battle_0(self): - self.check_s3_enemy() - - if self.battle_count >= self.config.C124LargeLeveling_PickupAmmo: - self.pick_up_ammo() - - if self.clear_enemy(scale=(3,), genre=['light', 'carrier', 'enemy', 'treasure', 'main']): - self.s3_enemy_count += 1 - self.non_s3_enemy_count = 0 - return True - if self.clear_enemy(scale=[2, 1]): - self.non_s3_enemy_count += 1 - return True - if not self.map.select(is_enemy=True, may_boss=False): - logger.info('No more enemies.') - self.withdraw() - - return self.battle_default() diff --git a/campaign/campaign_main/campaign_12_4_timeout_leveling.py b/campaign/campaign_main/campaign_12_4_timeout_leveling.py deleted file mode 100644 index 153decea0..000000000 --- a/campaign/campaign_main/campaign_12_4_timeout_leveling.py +++ /dev/null @@ -1,124 +0,0 @@ -from module.campaign.campaign_base import CampaignBase -from module.combat.assets import * -from module.logger import logger -from module.map.map_base import CampaignMap -from module.map.map_grids import RoadGrids, SelectedGrids - - -class Campaign(CampaignBase): - # MAP = MAP - - def battle_default(self): - if not self.map.select(enemy_scale=3, enemy_genre='Light'): - self.withdraw() - - def handle_combat_weapon_release(self): - if self.appear_then_click(READY_AIR_RAID, interval=5): - return True - - return False - - def handle_battle_status(self, save_get_items=False): - """ - Args: - save_get_items (bool): - - Returns: - bool: - """ - if self.is_combat_executing(): - return False - if self.appear_then_click(BATTLE_STATUS_S, screenshot=save_get_items, genre='status', interval=self.battle_status_click_interval): - if not save_get_items: - self.device.sleep((0.25, 0.5)) - return True - if self.appear_then_click(BATTLE_STATUS_A, screenshot=save_get_items, genre='status', interval=self.battle_status_click_interval): - logger.warning('Battle status: A') - if not save_get_items: - self.device.sleep((0.25, 0.5)) - return True - if self.appear_then_click(BATTLE_STATUS_B, screenshot=save_get_items, genre='status', interval=self.battle_status_click_interval): - logger.warning('Battle Status B') - if not save_get_items: - self.device.sleep((0.25, 0.5)) - return True - if self.appear_then_click(BATTLE_STATUS_C, screenshot=save_get_items, genre='status', interval=self.battle_status_click_interval): - if not save_get_items: - self.device.sleep((0.25, 0.5)) - return True - if self.appear_then_click(BATTLE_STATUS_D, screenshot=save_get_items, genre='status', interval=self.battle_status_click_interval): - logger.warning('Battle Status D') - if not save_get_items: - self.device.sleep((0.25, 0.5)) - return True - - return False - - def handle_get_items(self, save_get_items=False): - """ - Args: - save_get_items (bool): - - Returns: - bool: - """ - if self.appear_then_click(GET_ITEMS_1, screenshot=save_get_items, genre='get_items', offset=5, - interval=self.battle_status_click_interval): - self.interval_reset(BATTLE_STATUS_S) - self.interval_reset(BATTLE_STATUS_A) - self.interval_reset(BATTLE_STATUS_B) - self.interval_reset(BATTLE_STATUS_C) - self.interval_reset(BATTLE_STATUS_D) - return True - if self.appear_then_click(GET_ITEMS_2, screenshot=save_get_items, genre='get_items', offset=5, - interval=self.battle_status_click_interval): - self.interval_reset(BATTLE_STATUS_S) - self.interval_reset(BATTLE_STATUS_A) - self.interval_reset(BATTLE_STATUS_B) - self.interval_reset(BATTLE_STATUS_C) - self.interval_reset(BATTLE_STATUS_D) - return True - - return False - - def handle_exp_info(self): - """ - Returns: - bool: - """ - if self.is_combat_executing(): - return False - if self.appear_then_click(EXP_INFO_S): - self.device.sleep((0.25, 0.5)) - return True - if self.appear_then_click(EXP_INFO_A): - self.device.sleep((0.25, 0.5)) - return True - if self.appear_then_click(EXP_INFO_B): - self.device.sleep((0.25, 0.5)) - return True - if self.appear_then_click(EXP_INFO_C): - self.device.sleep((0.25, 0.5)) - return True - if self.appear_then_click(EXP_INFO_D): - self.device.sleep((0.25, 0.5)) - return True - if self.appear_then_click(OPTS_INFO_D, offset=(20, 20)): - self.device.sleep((0.25, 0.5)) - return True - - return False - - def combat(self, balance_hp=None, emotion_reduce=None, func=None, call_submarine_at_boss=None, save_get_items=None, - expected_end=None, fleet_index=1): - self.battle_status_click_interval = 7 if save_get_items else 0 - super().combat(balance_hp=False, expected_end='no_searching', auto_mode='hide_in_bottom_left', save_get_items=False) - - -from module.config.config import AzurLaneConfig - -az = Campaign(AzurLaneConfig('alas')) -for n in range(10000): - logger.hr(f'count: {n}') - az.map_offensive() - az.combat() diff --git a/campaign/campaign_main/campaign_1_1_affinity_farming.py b/campaign/campaign_main/campaign_1_1_affinity_farming.py deleted file mode 100644 index 9c774a1ee..000000000 --- a/campaign/campaign_main/campaign_1_1_affinity_farming.py +++ /dev/null @@ -1,37 +0,0 @@ -import numpy as np - -from module.campaign.campaign_base import CampaignBase -from module.exception import CampaignEnd, ScriptEnd -from module.logger import logger -from module.map.map_base import CampaignMap -from module.map.map_grids import RoadGrids, SelectedGrids - -from .campaign_1_1 import MAP -from .campaign_1_1 import Config as ConfigBase - -A1, B1, C1, D1, E1, F1, G1, \ - = MAP.flatten() - - -class Config(ConfigBase): - ENABLE_FAST_FORWARD = False - ENABLE_AUTO_SEARCH = False - AMBUSH_EVADE = False - - -class Campaign(CampaignBase): - MAP = MAP - affinity_battle = 0 - - def battle_default(self): - while self.affinity_battle < self.config.C11AffinityFarming_RunCount: - logger.attr('Affinity_battle', f'{self.affinity_battle}/{self.config.C11AffinityFarming_RunCount}') - self.goto(C1) - self.affinity_battle += 1 - self.goto(D1 if np.random.uniform() < 0.7 else B1) - - # End - try: - self.withdraw() - except CampaignEnd: - raise ScriptEnd('Reach condition: Affinity farming battle count') diff --git a/campaign/campaign_main/campaign_7_2_mystery_farming.py b/campaign/campaign_main/campaign_7_2_mystery_farming.py deleted file mode 100644 index 8571ba5c3..000000000 --- a/campaign/campaign_main/campaign_7_2_mystery_farming.py +++ /dev/null @@ -1,86 +0,0 @@ -from campaign.campaign_main.campaign_7_2 import MAP -from campaign.campaign_main.campaign_7_2 import Config as ConfigBase -from module.campaign.campaign_base import CampaignBase -from module.logger import logger -from module.map.map_base import CampaignMap -from module.map.map_grids import RoadGrids, SelectedGrids - -# MAP.in_map_swipe_preset_data = (-1, 0) - -A1, B1, C1, D1, E1, F1, G1, H1, \ -A2, B2, C2, D2, E2, F2, G2, H2, \ -A3, B3, C3, D3, E3, F3, G3, H3, \ -A4, B4, C4, D4, E4, F4, G4, H4, \ -A5, B5, C5, D5, E5, F5, G5, H5 = MAP.flatten() - -ROAD_MAIN = RoadGrids([A3, [C3, B4, C5], [F1, G2, G3]]) -GRIDS_FOR_FASTER = SelectedGrids([A3, C3, E3, G3]) -FLEET_2_STEP_ON = SelectedGrids([A3, G3, C3, E3]) - - -class Config(ConfigBase): - ENABLE_AUTO_SEARCH = False - - -class Campaign(CampaignBase): - MAP = MAP - - def battle_0(self): - if self.config.C72MysteryFarming_StepOnA3: - if self.fleet_2_step_on(FLEET_2_STEP_ON, roadblocks=[ROAD_MAIN]): - return True - - ignore = None - if self.fleet_at(A3, fleet=2) and A1.enemy_scale != 3 and not self.fleet_at(A1, fleet=1): - ignore = SelectedGrids([A2]) - if self.fleet_at(G3, fleet=2): - ignore = SelectedGrids([H3]) - - self.clear_all_mystery(nearby=False, ignore=ignore) - else: - self.clear_all_mystery(nearby=False) - - if self.clear_roadblocks([ROAD_MAIN], strongest=True): - return True - if self.clear_potential_roadblocks([ROAD_MAIN], strongest=True): - return True - - if self.clear_enemy(scale=(3,)): - return True - - if self.clear_grids_for_faster(GRIDS_FOR_FASTER, scale=(2,)): - return True - if self.clear_enemy(scale=(2,)): - return True - if self.clear_grids_for_faster(GRIDS_FOR_FASTER): - return True - - return self.battle_default() - - def battle_3(self): - if self.config.C72MysteryFarming_StepOnA3: - ignore = None - if self.fleet_at(A3, fleet=2): - ignore = SelectedGrids([A2]) - if self.fleet_at(G3, fleet=2): - ignore = SelectedGrids([H3]) - self.clear_all_mystery(nearby=False, ignore=ignore) - - if self.fleet_at(A3, fleet=2) and A2.is_mystery: - self.fleet_2.clear_chosen_mystery(A2) - if self.fleet_at(G3, fleet=2) and H3.is_mystery: - self.fleet_2.clear_chosen_mystery(H3) - else: - self.clear_all_mystery(nearby=False) - - if self.map.select(is_mystery=True, is_accessible=False): - logger.info('Roadblock blocks mystery.') - if self.fleet_1.clear_roadblocks([ROAD_MAIN]): - return True - - if not self.map.select(is_mystery=True): - self.withdraw() - - @property - def _map_battle(self): - return 3 diff --git a/campaign/campaign_main/campaign_8_4_leveling.py b/campaign/campaign_main/campaign_8_4_leveling.py deleted file mode 100644 index d55e39ae6..000000000 --- a/campaign/campaign_main/campaign_8_4_leveling.py +++ /dev/null @@ -1,38 +0,0 @@ -from .campaign_8_4 import Campaign as CampaignBase -from .campaign_8_4 import Config as ConfigBase -from .campaign_8_4 import * - - -class Config(ConfigBase): - ENABLE_AUTO_SEARCH = False - - -class Campaign(CampaignBase): - def battle_0(self): - self.fleet_2_push_forward() - - self.clear_all_mystery() - if self.map.select(is_mystery=True, is_accessible_1=False, is_accessible_2=True): - self.fleet_2.clear_all_mystery() - self.fleet_2_push_forward() - - if self.clear_roadblocks([road_D7, road_F3, road_main], strongest=True): - return True - if self.clear_potential_roadblocks([road_D7, road_F3, road_main], scale=(3,)): - return True - if self.clear_enemy(scale=(3,)): - return True - if self.clear_potential_roadblocks([road_D7, road_F3, road_main], strongest=True): - return True - if self.clear_first_roadblocks([road_D7, road_F3, road_main]): - return True - - return self.battle_default() - - def battle_4(self): - self.clear_all_mystery() - if self.map.select(is_mystery=True, is_accessible_1=False, is_accessible_2=True): - self.fleet_2.clear_all_mystery() - self.fleet_2_push_forward() - - return self.brute_clear_boss() diff --git a/campaign/event_20211111_cn/campaign_base.py b/campaign/event_20211111_cn/campaign_base.py deleted file mode 100644 index 0bd704eda..000000000 --- a/campaign/event_20211111_cn/campaign_base.py +++ /dev/null @@ -1,28 +0,0 @@ -from module.base.utils import color_similarity_2d -from module.campaign.campaign_base import CampaignBase as CampaignBase_ -from module.map_detection.grid import Grid -from module.template.assets import TEMPLATE_ENEMY_BOSS - - -class EventGrid(Grid): - def predict_boss(self): - # Small boss icon - if self.relative_hsv_count(area=(0.03, -0.15, 0.63, 0.15), h=(358 - 3, 358 + 3), shape=(50, 20)) > 100: - image = self.relative_crop((0.03, -0.15, 0.63, 0.15), shape=(50, 20)) - image = color_similarity_2d(image, color=(255, 77, 82)) - if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.7): - return True - - return False - - def predict_enemy_genre(self): - image = self.relative_crop((-0.55, -0.2, 0.45, 0.2), shape=(50, 20)) - image = color_similarity_2d(image, color=(255, 150, 24)) - if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.75): - return 'Siren_Siren' - - return super().predict_enemy_genre() - - -class CampaignBase(CampaignBase_): - grid_class = EventGrid diff --git a/campaign/event_20211111_cn/sp1.py b/campaign/event_20211111_cn/sp1.py index dd71be035..d17cdb26a 100644 --- a/campaign/event_20211111_cn/sp1.py +++ b/campaign/event_20211111_cn/sp1.py @@ -2,7 +2,7 @@ from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase MAP = CampaignMap('SP1') MAP.shape = 'H7' @@ -55,6 +55,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON_SMALL = True MAP_SWIPE_MULTIPLY = (1.122, 1.143) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.085, 1.105) MAP_SWIPE_MULTIPLY_MAATOUCH = (1.054, 1.073) diff --git a/campaign/event_20211111_cn/sp2.py b/campaign/event_20211111_cn/sp2.py index 5e60fca92..3f503c1b3 100644 --- a/campaign/event_20211111_cn/sp2.py +++ b/campaign/event_20211111_cn/sp2.py @@ -2,7 +2,7 @@ from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from .sp1 import Config as ConfigBase MAP = CampaignMap('SP2') diff --git a/campaign/event_20211111_cn/sp3.py b/campaign/event_20211111_cn/sp3.py index 550b8c954..c45ed4aeb 100644 --- a/campaign/event_20211111_cn/sp3.py +++ b/campaign/event_20211111_cn/sp3.py @@ -2,7 +2,7 @@ from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from .sp1 import Config as ConfigBase MAP = CampaignMap('SP3') diff --git a/campaign/event_20220526_cn/a1.py b/campaign/event_20220526_cn/a1.py index db5246d6e..caf40b76c 100644 --- a/campaign/event_20220526_cn/a1.py +++ b/campaign/event_20220526_cn/a1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase, EventGrid +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -54,6 +54,8 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True + MAP_SIREN_HAS_BOSS_ICON_SMALL = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (120, 255 - 49), 'width': (1.5, 10), @@ -75,7 +77,6 @@ class Config: class Campaign(CampaignBase): - grid_class = EventGrid MAP = MAP ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' diff --git a/campaign/event_20220526_cn/a2.py b/campaign/event_20220526_cn/a2.py index f908575cc..c9c68a8e2 100644 --- a/campaign/event_20220526_cn/a2.py +++ b/campaign/event_20220526_cn/a2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -58,6 +58,8 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = False + MAP_SIREN_HAS_BOSS_ICON_SMALL = False INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (120, 255 - 24), 'width': (1.5, 10), diff --git a/campaign/event_20220526_cn/a3.py b/campaign/event_20220526_cn/a3.py index 2799a4e54..251556a1f 100644 --- a/campaign/event_20220526_cn/a3.py +++ b/campaign/event_20220526_cn/a3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -58,6 +58,8 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = False + MAP_SIREN_HAS_BOSS_ICON_SMALL = False INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (120, 255 - 33), 'width': (1.5, 10), diff --git a/campaign/event_20220526_cn/b1.py b/campaign/event_20220526_cn/b1.py index e1117495a..a37caf197 100644 --- a/campaign/event_20220526_cn/b1.py +++ b/campaign/event_20220526_cn/b1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20220526_cn/b2.py b/campaign/event_20220526_cn/b2.py index a8896dab7..9a1291983 100644 --- a/campaign/event_20220526_cn/b2.py +++ b/campaign/event_20220526_cn/b2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase, EventGrid +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -57,6 +57,8 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True + MAP_SIREN_HAS_BOSS_ICON_SMALL = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (120, 255 - 33), 'width': (1.5, 10), @@ -77,7 +79,6 @@ class Config(ConfigBase): class Campaign(CampaignBase): - grid_class = EventGrid MAP = MAP ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' diff --git a/campaign/event_20220526_cn/b3.py b/campaign/event_20220526_cn/b3.py index 1c9e81ebd..a47aeb10b 100644 --- a/campaign/event_20220526_cn/b3.py +++ b/campaign/event_20220526_cn/b3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20220526_cn/c1.py b/campaign/event_20220526_cn/c1.py index f6aca31fd..2f7b1d6e3 100644 --- a/campaign/event_20220526_cn/c1.py +++ b/campaign/event_20220526_cn/c1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase, EventGrid +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -54,6 +54,8 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True + MAP_SIREN_HAS_BOSS_ICON_SMALL = True MAP_ENSURE_EDGE_INSIGHT_CORNER = 'bottom' MAP_SWIPE_MULTIPLY = (1.084, 1.104) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.048, 1.067) @@ -61,7 +63,6 @@ class Config: class Campaign(CampaignBase): - grid_class = EventGrid MAP = MAP ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' diff --git a/campaign/event_20220526_cn/c2.py b/campaign/event_20220526_cn/c2.py index 864f4e392..fccf2cd21 100644 --- a/campaign/event_20220526_cn/c2.py +++ b/campaign/event_20220526_cn/c2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -58,6 +58,8 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = False + MAP_SIREN_HAS_BOSS_ICON_SMALL = False INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (120, 255 - 17), 'width': (1.5, 10), diff --git a/campaign/event_20220526_cn/c3.py b/campaign/event_20220526_cn/c3.py index 9dbbe9434..d1910db7f 100644 --- a/campaign/event_20220526_cn/c3.py +++ b/campaign/event_20220526_cn/c3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -59,6 +59,8 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = False + MAP_SIREN_HAS_BOSS_ICON_SMALL = False INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (120, 255 - 33), 'width': (1.5, 10), diff --git a/campaign/event_20220526_cn/campaign_base.py b/campaign/event_20220526_cn/campaign_base.py deleted file mode 100644 index 0cf720d96..000000000 --- a/campaign/event_20220526_cn/campaign_base.py +++ /dev/null @@ -1,31 +0,0 @@ -from module.base.utils import color_similarity_2d -from module.campaign.campaign_base import CampaignBase as CampaignBase_ -from module.map_detection.grid import Grid -from module.template.assets import TEMPLATE_ENEMY_BOSS - - -class EventGrid(Grid): - def predict_enemy_genre(self): - image = self.relative_crop((-0.55, -0.2, 0.45, 0.2), shape=(50, 20)) - image = color_similarity_2d(image, color=(255, 150, 24)) - if image[image > 221].shape[0] > 200: - if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.6): - return 'Siren_Siren' - - # Small icon - if self.relative_hsv_count(area=(0.03, -0.15, 0.63, 0.15), h=(32 - 3, 32 + 3), shape=(50, 20)) > 100: - image = self.relative_crop((0.03, -0.15, 0.63, 0.15), shape=(50, 20)) - image = color_similarity_2d(image, color=(255, 150, 33)) - if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.7): - return 'Siren_Siren' - - return super().predict_enemy_genre() - - def predict_boss(self): - if self.enemy_genre == 'Siren_Siren': - return False - return super().predict_boss() - - -class CampaignBase(CampaignBase_): - pass diff --git a/campaign/event_20220526_cn/d1.py b/campaign/event_20220526_cn/d1.py index c229ef6cc..1a5b7edae 100644 --- a/campaign/event_20220526_cn/d1.py +++ b/campaign/event_20220526_cn/d1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20220526_cn/d2.py b/campaign/event_20220526_cn/d2.py index 39e24bd94..037a491ec 100644 --- a/campaign/event_20220526_cn/d2.py +++ b/campaign/event_20220526_cn/d2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase, EventGrid +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -58,6 +58,8 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True + MAP_SIREN_HAS_BOSS_ICON_SMALL = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (120, 255 - 33), 'width': (1.5, 10), @@ -78,7 +80,6 @@ class Config(ConfigBase): class Campaign(CampaignBase): - grid_class = EventGrid MAP = MAP ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' diff --git a/campaign/event_20220526_cn/d3.py b/campaign/event_20220526_cn/d3.py index 270d948a7..60bba3411 100644 --- a/campaign/event_20220526_cn/d3.py +++ b/campaign/event_20220526_cn/d3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20220526_cn/sp.py b/campaign/event_20220526_cn/sp.py index 1d28ed47d..cc78e69d1 100644 --- a/campaign/event_20220526_cn/sp.py +++ b/campaign/event_20220526_cn/sp.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20221222_cn/a1.py b/campaign/event_20221222_cn/a1.py index 6d7eb2365..f6e980041 100644 --- a/campaign/event_20221222_cn/a1.py +++ b/campaign/event_20221222_cn/a1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -57,6 +57,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (80, 255 - 33), 'width': (1.5, 10), diff --git a/campaign/event_20221222_cn/a2.py b/campaign/event_20221222_cn/a2.py index 5fc81b8b0..a422c6961 100644 --- a/campaign/event_20221222_cn/a2.py +++ b/campaign/event_20221222_cn/a2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20221222_cn/a3.py b/campaign/event_20221222_cn/a3.py index 2829d9f2f..12fee3ea6 100644 --- a/campaign/event_20221222_cn/a3.py +++ b/campaign/event_20221222_cn/a3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20221222_cn/b1.py b/campaign/event_20221222_cn/b1.py index 33387aa54..473cad7ee 100644 --- a/campaign/event_20221222_cn/b1.py +++ b/campaign/event_20221222_cn/b1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -58,6 +58,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (80, 255 - 33), 'width': (1.5, 10), diff --git a/campaign/event_20221222_cn/b2.py b/campaign/event_20221222_cn/b2.py index 84d9266ad..9c3b5aeb4 100644 --- a/campaign/event_20221222_cn/b2.py +++ b/campaign/event_20221222_cn/b2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20221222_cn/b3.py b/campaign/event_20221222_cn/b3.py index 1340106ed..8466b14b4 100644 --- a/campaign/event_20221222_cn/b3.py +++ b/campaign/event_20221222_cn/b3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20221222_cn/c1.py b/campaign/event_20221222_cn/c1.py index d4e7279d7..bc47b32db 100644 --- a/campaign/event_20221222_cn/c1.py +++ b/campaign/event_20221222_cn/c1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -57,6 +57,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (80, 255 - 24), 'width': (1.5, 10), diff --git a/campaign/event_20221222_cn/c2.py b/campaign/event_20221222_cn/c2.py index 013b72a7e..2451f532d 100644 --- a/campaign/event_20221222_cn/c2.py +++ b/campaign/event_20221222_cn/c2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20221222_cn/c3.py b/campaign/event_20221222_cn/c3.py index c77928b9f..f56a3c442 100644 --- a/campaign/event_20221222_cn/c3.py +++ b/campaign/event_20221222_cn/c3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20221222_cn/campaign_base.py b/campaign/event_20221222_cn/campaign_base.py deleted file mode 100644 index fa34e290d..000000000 --- a/campaign/event_20221222_cn/campaign_base.py +++ /dev/null @@ -1,27 +0,0 @@ -from module.base.utils import color_similarity_2d -from module.campaign.campaign_base import CampaignBase as CampaignBase_ -from module.map_detection.grid import Grid -from module.template.assets import TEMPLATE_ENEMY_BOSS - - -class EventGrid(Grid): - def predict_enemy_genre(self): - if self.enemy_scale: - return '' - - image = self.relative_crop((-0.55, -0.2, 0.45, 0.2), shape=(50, 20)) - image = color_similarity_2d(image, color=(255, 150, 24)) - if image[image > 221].shape[0] > 200: - if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.6): - return 'Siren_Siren' - - return super().predict_enemy_genre() - - def predict_boss(self): - if self.enemy_genre == 'Siren_Siren': - return False - return super().predict_boss() - - -class CampaignBase(CampaignBase_): - grid_class = EventGrid diff --git a/campaign/event_20221222_cn/d1.py b/campaign/event_20221222_cn/d1.py index bd0b4af00..a08c78a5a 100644 --- a/campaign/event_20221222_cn/d1.py +++ b/campaign/event_20221222_cn/d1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -58,6 +58,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (80, 255 - 24), 'width': (1.5, 10), diff --git a/campaign/event_20221222_cn/d2.py b/campaign/event_20221222_cn/d2.py index 08c0deff8..8910a2860 100644 --- a/campaign/event_20221222_cn/d2.py +++ b/campaign/event_20221222_cn/d2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20221222_cn/d3.py b/campaign/event_20221222_cn/d3.py index ad72bfbba..f4bff9369 100644 --- a/campaign/event_20221222_cn/d3.py +++ b/campaign/event_20221222_cn/d3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20221222_cn/sp.py b/campaign/event_20221222_cn/sp.py index ece331d40..4c5d37747 100644 --- a/campaign/event_20221222_cn/sp.py +++ b/campaign/event_20221222_cn/sp.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20230914_cn/a1.py b/campaign/event_20230914_cn/a1.py index 3e3876487..447dcae3a 100644 --- a/campaign/event_20230914_cn/a1.py +++ b/campaign/event_20230914_cn/a1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -57,6 +57,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (80, 255 - 17), 'width': (0.9, 10), diff --git a/campaign/event_20230914_cn/b1.py b/campaign/event_20230914_cn/b1.py index f0f422eda..ed6df54e8 100644 --- a/campaign/event_20230914_cn/b1.py +++ b/campaign/event_20230914_cn/b1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -55,6 +55,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (80, 255 - 17), 'width': (0.9, 10), diff --git a/campaign/event_20230914_cn/b2.py b/campaign/event_20230914_cn/b2.py index 011fa1453..b8eedf6ce 100644 --- a/campaign/event_20230914_cn/b2.py +++ b/campaign/event_20230914_cn/b2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20230914_cn/b3.py b/campaign/event_20230914_cn/b3.py index a7eebac86..e760b56b8 100644 --- a/campaign/event_20230914_cn/b3.py +++ b/campaign/event_20230914_cn/b3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20230914_cn/c1.py b/campaign/event_20230914_cn/c1.py index 95dee01d4..9ecf8bfdf 100644 --- a/campaign/event_20230914_cn/c1.py +++ b/campaign/event_20230914_cn/c1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -57,6 +57,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True MAP_SWIPE_MULTIPLY = (1.074, 1.095) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.039, 1.058) MAP_SWIPE_MULTIPLY_MAATOUCH = (1.009, 1.027) diff --git a/campaign/event_20230914_cn/campaign_base.py b/campaign/event_20230914_cn/campaign_base.py deleted file mode 100644 index e8bc9f382..000000000 --- a/campaign/event_20230914_cn/campaign_base.py +++ /dev/null @@ -1,30 +0,0 @@ -from module.base.utils import color_similarity_2d -from module.campaign.campaign_base import CampaignBase as CampaignBase_ -from module.map_detection.grid import Grid -from module.template.assets import TEMPLATE_ENEMY_BOSS - - -class EventGrid(Grid): - def predict_enemy_genre(self): - if self.enemy_scale: - return '' - - image = self.relative_crop((-0.55, -0.2, 0.45, 0.2), shape=(50, 20)) - image = color_similarity_2d(image, color=(255, 150, 24)) - if image[image > 221].shape[0] > 200: - if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.6): - return 'Siren_Siren' - - return super().predict_enemy_genre() - - def predict_boss(self): - if self.enemy_genre == 'Siren_Siren': - return False - return super().predict_boss() - - -class CampaignBase(CampaignBase_): - """ - Not all event maps use this, some are the same like the classics - """ - grid_class = EventGrid diff --git a/campaign/event_20230914_cn/d1.py b/campaign/event_20230914_cn/d1.py index 77ba7e3d8..dc752a208 100644 --- a/campaign/event_20230914_cn/d1.py +++ b/campaign/event_20230914_cn/d1.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -55,6 +55,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { 'height': (80, 255 - 17), 'width': (0.9, 10), diff --git a/campaign/event_20230914_cn/d2.py b/campaign/event_20230914_cn/d2.py index fbc308bee..dca1fb0aa 100644 --- a/campaign/event_20230914_cn/d2.py +++ b/campaign/event_20230914_cn/d2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20230914_cn/d3.py b/campaign/event_20230914_cn/d3.py index 4d424b052..2ee1e1687 100644 --- a/campaign/event_20230914_cn/d3.py +++ b/campaign/event_20230914_cn/d3.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20230914_cn/sp.py b/campaign/event_20230914_cn/sp.py index 827eca901..27a578a75 100644 --- a/campaign/event_20230914_cn/sp.py +++ b/campaign/event_20230914_cn/sp.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase +from module.campaign.campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger diff --git a/campaign/event_20231026_cn/campaign_base.py b/campaign/event_20231026_cn/campaign_base.py index cff808435..420866ae7 100644 --- a/campaign/event_20231026_cn/campaign_base.py +++ b/campaign/event_20231026_cn/campaign_base.py @@ -4,25 +4,6 @@ from module.map_detection.grid import Grid from module.template.assets import TEMPLATE_ENEMY_BOSS -class EventGrid(Grid): - def predict_enemy_genre(self): - if self.enemy_scale: - return '' - - image = self.relative_crop((-0.55, -0.2, 0.45, 0.2), shape=(50, 20)) - image = color_similarity_2d(image, color=(255, 150, 24)) - if image[image > 221].shape[0] > 200: - if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.6): - return 'Siren_Siren' - - return super().predict_enemy_genre() - - def predict_boss(self): - if self.enemy_genre == 'Siren_Siren': - return False - return super().predict_boss() - - class CampaignBase(CampaignBase_): STAGE_INCREASE = [ """ diff --git a/campaign/event_20231026_cn/t2.py b/campaign/event_20231026_cn/t2.py index 119c54f00..39600d3ba 100644 --- a/campaign/event_20231026_cn/t2.py +++ b/campaign/event_20231026_cn/t2.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase, EventGrid +from .campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -58,6 +58,7 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True MAP_SWIPE_MULTIPLY = (1.223, 1.246) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.183, 1.205) MAP_SWIPE_MULTIPLY_MAATOUCH = (1.149, 1.169) @@ -66,7 +67,6 @@ class Config(ConfigBase): class Campaign(CampaignBase): MAP = MAP ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' - grid_class = EventGrid def battle_0(self): if self.clear_siren(): diff --git a/campaign/event_20231026_cn/t5.py b/campaign/event_20231026_cn/t5.py index bf0af9334..ef0d13d47 100644 --- a/campaign/event_20231026_cn/t5.py +++ b/campaign/event_20231026_cn/t5.py @@ -1,4 +1,4 @@ -from .campaign_base import CampaignBase, EventGrid +from .campaign_base import CampaignBase from module.map.map_base import CampaignMap from module.map.map_grids import SelectedGrids, RoadGrids from module.logger import logger @@ -66,6 +66,7 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== + MAP_SIREN_HAS_BOSS_ICON = True MAP_SWIPE_MULTIPLY = (0.991, 1.009) MAP_SWIPE_MULTIPLY_MINITOUCH = (0.958, 0.976) MAP_SWIPE_MULTIPLY_MAATOUCH = (0.930, 0.947) @@ -74,7 +75,6 @@ class Config(ConfigBase): class Campaign(CampaignBase): MAP = MAP ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' - grid_class = EventGrid def battle_0(self): if self.clear_siren(): diff --git a/campaign/event_20240725_cn/campaign_base.py b/campaign/event_20240725_cn/campaign_base.py index 6da69fc05..f4ca6680f 100644 --- a/campaign/event_20240725_cn/campaign_base.py +++ b/campaign/event_20240725_cn/campaign_base.py @@ -1,9 +1,9 @@ from module.campaign.assets import SWITCH_20240725_COMBAT, SWITCH_20240725_STORY from module.campaign.campaign_base import CampaignBase as CampaignBase_ +from module.campaign.campaign_ui import ModeSwitch from module.logger import logger -from module.ui.switch import Switch -MODE_SWITCH_20240725 = Switch('Mode_switch_20240725', offset=(30, 30)) +MODE_SWITCH_20240725 = ModeSwitch('Mode_switch_20240725', offset=(30, 30)) MODE_SWITCH_20240725.add_status('combat', SWITCH_20240725_COMBAT) MODE_SWITCH_20240725.add_status('story', SWITCH_20240725_STORY) diff --git a/campaign/event_20240725_cn/ht1.py b/campaign/event_20240725_cn/ht1.py index 1ef05e4c8..2ebc9440a 100644 --- a/campaign/event_20240725_cn/ht1.py +++ b/campaign/event_20240725_cn/ht1.py @@ -55,7 +55,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== - STAGE_ENTRANCE = ['normal', '20240725'] + STAGE_ENTRANCE = ['half', '20240725'] MAP_HAS_MODE_SWITCH = True MAP_SWIPE_MULTIPLY = (1.236, 1.259) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.195, 1.217) diff --git a/campaign/event_20240725_cn/ht2.py b/campaign/event_20240725_cn/ht2.py index 524957476..0af5737c0 100644 --- a/campaign/event_20240725_cn/ht2.py +++ b/campaign/event_20240725_cn/ht2.py @@ -57,7 +57,7 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== - STAGE_ENTRANCE = ['normal', '20240725'] + STAGE_ENTRANCE = ['half', '20240725'] MAP_HAS_MODE_SWITCH = True MAP_SWIPE_MULTIPLY = (1.189, 1.211) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.150, 1.171) diff --git a/campaign/event_20240725_cn/ht3.py b/campaign/event_20240725_cn/ht3.py index 4c2ab18a0..a3127dec9 100644 --- a/campaign/event_20240725_cn/ht3.py +++ b/campaign/event_20240725_cn/ht3.py @@ -60,7 +60,7 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== - STAGE_ENTRANCE = ['normal', '20240725'] + STAGE_ENTRANCE = ['half', '20240725'] MAP_HAS_MODE_SWITCH = True MAP_SWIPE_MULTIPLY = (1.089, 1.109) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.053, 1.072) diff --git a/campaign/event_20240725_cn/sp.py b/campaign/event_20240725_cn/sp.py index 6399a7999..c29eb709c 100644 --- a/campaign/event_20240725_cn/sp.py +++ b/campaign/event_20240725_cn/sp.py @@ -66,7 +66,7 @@ class Config: STAR_REQUIRE_3 = 0 # ===== End of generated config ===== - STAGE_ENTRANCE = ['normal', '20240725'] + STAGE_ENTRANCE = ['half', '20240725'] MAP_IS_ONE_TIME_STAGE = True MAP_SWIPE_MULTIPLY = (1.073, 1.093) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.038, 1.057) diff --git a/campaign/event_20240725_cn/t1.py b/campaign/event_20240725_cn/t1.py index bab8993b3..6e37436c7 100644 --- a/campaign/event_20240725_cn/t1.py +++ b/campaign/event_20240725_cn/t1.py @@ -54,7 +54,7 @@ class Config: MAP_HAS_MYSTERY = False # ===== End of generated config ===== - STAGE_ENTRANCE = ['normal', '20240725'] + STAGE_ENTRANCE = ['half', '20240725'] MAP_HAS_MODE_SWITCH = True MAP_SWIPE_MULTIPLY = (1.236, 1.259) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.195, 1.217) diff --git a/campaign/event_20240725_cn/t2.py b/campaign/event_20240725_cn/t2.py index e315675aa..25b0750ed 100644 --- a/campaign/event_20240725_cn/t2.py +++ b/campaign/event_20240725_cn/t2.py @@ -55,7 +55,7 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== - STAGE_ENTRANCE = ['normal', '20240725'] + STAGE_ENTRANCE = ['half', '20240725'] MAP_HAS_MODE_SWITCH = True MAP_SWIPE_MULTIPLY = (1.189, 1.211) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.150, 1.171) diff --git a/campaign/event_20240725_cn/t3.py b/campaign/event_20240725_cn/t3.py index a2d9f8bbd..aa3c4e801 100644 --- a/campaign/event_20240725_cn/t3.py +++ b/campaign/event_20240725_cn/t3.py @@ -59,7 +59,7 @@ class Config(ConfigBase): MAP_HAS_MYSTERY = False # ===== End of generated config ===== - STAGE_ENTRANCE = ['normal', '20240725'] + STAGE_ENTRANCE = ['half', '20240725'] MAP_HAS_MODE_SWITCH = True MAP_SWIPE_MULTIPLY = (1.089, 1.109) MAP_SWIPE_MULTIPLY_MINITOUCH = (1.053, 1.072) diff --git a/campaign/event_20240815_cn/a1.py b/campaign/event_20240815_cn/a1.py new file mode 100644 index 000000000..a550437b0 --- /dev/null +++ b/campaign/event_20240815_cn/a1.py @@ -0,0 +1,92 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + +MAP = CampaignMap('A1') +MAP.shape = 'I8' +MAP.camera_data = ['E2', 'E5'] +MAP.camera_data_spawn_point = ['E2'] +MAP.map_data = """ + -- -- -- SP -- SP -- -- -- + -- ME -- -- MB -- -- ME -- + -- ++ Me -- -- -- ++ ++ -- + ++ ME -- MS -- MS ++ ++ -- + -- ME -- -- __ -- -- ME -- + -- -- ME -- Me -- -- ME -- + -- -- ++ ME -- ++ Me -- -- + -- -- ++ -- -- -- -- -- -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1, 'boss': 1}, + {'battle': 4, 'enemy': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = [] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SIREN_HAS_BOSS_ICON_SMALL = True + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (80, 255 - 33), + 'width': (0.9, 10), + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 33, 255), + 'prominence': 10, + 'distance': 50, + # 'width': (0, 7), + 'wlen': 1000 + } + MAP_SWIPE_MULTIPLY = (1.194, 1.217) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.155, 1.176) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.121, 1.142) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_3(self): + return self.clear_boss() diff --git a/campaign/event_20240815_cn/a2.py b/campaign/event_20240815_cn/a2.py new file mode 100644 index 000000000..8653c4c71 --- /dev/null +++ b/campaign/event_20240815_cn/a2.py @@ -0,0 +1,79 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .a1 import Config as ConfigBase + +MAP = CampaignMap('A2') +MAP.shape = 'I8' +MAP.camera_data = ['D2', 'D6', 'F2', 'F6'] +MAP.camera_data_spawn_point = ['D2', 'D6'] +MAP.map_data = """ + -- -- -- ME -- -- -- ++ ++ + ++ ++ Me -- Me ++ -- ++ ++ + ++ ++ -- -- -- ME -- ME -- + SP -- -- MS -- -- ME -- -- + -- -- Me __ ++ ++ ++ -- MB + SP -- -- MS -- -- -- -- -- + ++ -- -- -- -- ME -- ME -- + ++ -- -- ME ++ -- ME -- -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = [] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = (1.103, 1.124) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.067, 1.087) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.036, 1.055) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/event_20240815_cn/a3.py b/campaign/event_20240815_cn/a3.py new file mode 100644 index 000000000..2d2bd2ee5 --- /dev/null +++ b/campaign/event_20240815_cn/a3.py @@ -0,0 +1,70 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .a1 import Config as ConfigBase + +MAP = CampaignMap('A3') +MAP.shape = 'O5' +MAP.camera_data = ['F2', 'F3', 'J2', 'J3'] +MAP.camera_data_spawn_point = ['J2'] +MAP.map_data = """ + -- -- ++ ++ ++ ME -- ++ ++ SP -- SP -- ++ -- + -- -- -- ++ ME -- ME ++ ++ -- -- -- -- ++ ++ + -- -- MB -- -- __ -- ME -- -- MS -- MS ++ ++ + -- -- -- ++ ME -- -- -- -- Me -- ++ -- -- -- + -- -- ++ ++ ++ ME ME ++ Me -- Me ++ -- -- -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, L1, M1, N1, O1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, L2, M2, N2, O2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, L3, M3, N3, O3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, L4, M4, N4, O4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, L5, M5, N5, O5, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = [] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = (1.240, 1.263) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.199, 1.221) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.164, 1.185) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/event_20240815_cn/b1.py b/campaign/event_20240815_cn/b1.py new file mode 100644 index 000000000..ece3989d8 --- /dev/null +++ b/campaign/event_20240815_cn/b1.py @@ -0,0 +1,101 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + +MAP = CampaignMap('B1') +MAP.shape = 'K9' +MAP.camera_data = ['D2', 'D6', 'H3', 'H7'] +MAP.camera_data_spawn_point = ['D6'] +MAP.map_data = """ + -- -- -- ME -- ++ ++ ++ -- -- ++ + -- ++ ME -- ME ++ ++ ++ ME ME ++ + ME -- -- -- -- ++ ++ ++ -- -- -- + ++ -- Me -- -- -- MB -- -- ME -- + ++ -- ++ ++ ++ -- __ -- ++ ++ -- + SP -- -- MS -- -- ME -- -- ++ ME + -- -- MS ++ -- Me -- Me -- ME -- + SP -- -- MS -- ++ ++ ++ -- -- -- + ++ -- ++ -- -- -- ++ -- -- ME -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 2, 'boss': 1}, + {'battle': 5, 'enemy': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, J6, K6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, J7, K7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, J8, K8, \ +A9, B9, C9, D9, E9, F9, G9, H9, I9, J9, K9, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['Intruder'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (80, 255 - 33), + 'width': (0.9, 10), + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 33, 255), + 'prominence': 10, + 'distance': 50, + # 'width': (0, 7), + 'wlen': 1000 + } + HOMO_EDGE_COLOR_RANGE = (0, 33) + HOMO_EDGE_HOUGHLINES_THRESHOLD = 300 + MAP_HAS_MOVABLE_NORMAL_ENEMY = True + MAP_SWIPE_PREDICT = False + MAP_SIREN_MOVE_WAIT = 0.5 + MAP_WALK_USE_CURRENT_FLEET = True + MAP_SWIPE_MULTIPLY = (1.025, 1.044) + MAP_SWIPE_MULTIPLY_MINITOUCH = (0.991, 1.009) + MAP_SWIPE_MULTIPLY_MAATOUCH = (0.962, 0.979) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/event_20240815_cn/b2.py b/campaign/event_20240815_cn/b2.py new file mode 100644 index 000000000..4f1e563c4 --- /dev/null +++ b/campaign/event_20240815_cn/b2.py @@ -0,0 +1,98 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .b1 import Config as ConfigBase + +MAP = CampaignMap('B2') +MAP.shape = 'J8' +MAP.camera_data = ['D3', 'D6', 'G3', 'G6'] +MAP.camera_data_spawn_point = ['D3'] +MAP.map_data = """ + -- ++ -- -- -- ++ ++ -- -- -- + -- ++ SP -- SP ++ ++ ME ME ++ + -- Me -- -- -- Me -- -- -- -- + -- -- -- MS -- -- ++ -- ME -- + ++ -- MS -- MS -- ++ Me -- -- + -- -- ++ ++ ++ -- Me -- -- ME + ++ -- Me -- -- __ -- -- ++ ++ + MB -- ++ ME ME -- ME ME ++ ++ +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 2}, + {'battle': 5, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, J6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, J7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, J8, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = [] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_HAS_MOVABLE_NORMAL_ENEMY = False + MAP_SIREN_HAS_BOSS_ICON_SMALL = True + MAP_SWIPE_MULTIPLY = (1.206, 1.228) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.166, 1.188) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.132, 1.152) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() + + def before_boss(self): + # Handle giant boss at A8 + logger.info('B2 before boss') + grid = SelectedGrids([B6, C7]).sort('weight', 'cost')[0] + self.goto(grid) + self.goto(B8) + + def clear_boss(self): + self.before_boss() + super().clear_boss() + + def brute_clear_boss(self): + self.before_boss() + super().brute_clear_boss() + diff --git a/campaign/event_20240815_cn/b3.py b/campaign/event_20240815_cn/b3.py new file mode 100644 index 000000000..de7639d26 --- /dev/null +++ b/campaign/event_20240815_cn/b3.py @@ -0,0 +1,85 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .b1 import Config as ConfigBase + +MAP = CampaignMap('B3') +MAP.shape = 'K9' +MAP.camera_data = ['D3', 'D7', 'H3', 'H7'] +MAP.camera_data_spawn_point = ['D3'] +MAP.map_data = """ + -- -- ++ -- -- -- -- -- ++ ++ -- + ++ ++ ++ -- -- ++ -- ME ++ ++ -- + ++ ++ ++ -- Me ++ ME -- ME ME -- + ++ ++ ++ -- -- Me -- __ -- -- MB + SP -- SP -- -- -- -- ME -- -- MB + -- -- -- ++ ++ ME -- ++ ME ME -- + -- MS -- -- ++ Me -- ++ ++ ++ ++ + MS -- MS -- Me -- -- -- ME -- -- + ++ ++ ++ -- -- -- ++ ME -- -- -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 2}, + {'battle': 5, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, J6, K6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, J7, K7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, J8, K8, \ +A9, B9, C9, D9, E9, F9, G9, H9, I9, J9, K9, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['AmagiMasked'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_HAS_MOVABLE_NORMAL_ENEMY = True + MAP_SIREN_HAS_BOSS_ICON_SMALL = False + MAP_SWIPE_MULTIPLY = (1.108, 1.129) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.072, 1.092) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.041, 1.059) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/event_20240815_cn/c1.py b/campaign/event_20240815_cn/c1.py new file mode 100644 index 000000000..4f6402c8e --- /dev/null +++ b/campaign/event_20240815_cn/c1.py @@ -0,0 +1,92 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + +MAP = CampaignMap('C1') +MAP.shape = 'I8' +MAP.camera_data = ['E2', 'E5'] +MAP.camera_data_spawn_point = ['E2'] +MAP.map_data = """ + -- -- -- SP -- SP -- -- -- + -- ME -- -- MB -- -- ME -- + -- ++ Me -- -- -- ++ ++ -- + ++ ME -- MS -- MS ++ ++ -- + -- ME -- -- __ -- -- ME -- + -- -- ME -- Me -- -- ME -- + -- -- ++ ME -- ++ Me -- -- + -- -- ++ -- -- -- -- -- -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = [] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SIREN_HAS_BOSS_ICON_SMALL = True + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (80, 255 - 33), + 'width': (0.9, 10), + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 33, 255), + 'prominence': 10, + 'distance': 50, + # 'width': (0, 7), + 'wlen': 1000 + } + MAP_SWIPE_MULTIPLY = (1.194, 1.217) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.155, 1.176) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.121, 1.142) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/event_20240815_cn/c2.py b/campaign/event_20240815_cn/c2.py new file mode 100644 index 000000000..5fea784e0 --- /dev/null +++ b/campaign/event_20240815_cn/c2.py @@ -0,0 +1,79 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .c1 import Config as ConfigBase + +MAP = CampaignMap('C2') +MAP.shape = 'I8' +MAP.camera_data = ['D2', 'D6', 'F2', 'F6'] +MAP.camera_data_spawn_point = ['D2', 'D6'] +MAP.map_data = """ + -- -- -- ME -- -- -- ++ ++ + ++ ++ Me -- Me ++ -- ++ ++ + ++ ++ -- -- -- ME -- ME -- + SP -- -- MS -- -- ME -- -- + -- -- Me __ ++ ++ ++ -- MB + SP -- -- MS -- -- -- -- -- + ++ -- -- -- -- ME -- ME -- + ++ -- -- ME ++ -- ME -- -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = [] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = (1.103, 1.124) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.067, 1.087) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.036, 1.055) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/event_20240815_cn/c3.py b/campaign/event_20240815_cn/c3.py new file mode 100644 index 000000000..cb571d244 --- /dev/null +++ b/campaign/event_20240815_cn/c3.py @@ -0,0 +1,71 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .c1 import Config as ConfigBase + +MAP = CampaignMap('C3') +MAP.shape = 'O5' +MAP.camera_data = ['F2', 'F3', 'J2', 'J3'] +MAP.camera_data_spawn_point = ['J2'] +MAP.map_data = """ + -- -- ++ ++ ++ ME -- ++ ++ SP -- SP -- ++ -- + -- -- -- ++ ME -- ME ++ ++ -- -- -- -- ++ ++ + -- -- MB -- -- __ -- ME -- -- MS -- MS ++ ++ + -- -- -- ++ ME -- -- -- -- Me -- ++ -- -- -- + -- -- ++ ++ ++ ME ME ++ Me -- Me ++ -- -- -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1}, + {'battle': 5, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, L1, M1, N1, O1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, L2, M2, N2, O2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, L3, M3, N3, O3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, L4, M4, N4, O4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, L5, M5, N5, O5, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = [] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = (1.240, 1.263) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.199, 1.221) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.164, 1.185) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/event_20240815_cn/campaign_base.py b/campaign/event_20240815_cn/campaign_base.py new file mode 100644 index 000000000..db32d830b --- /dev/null +++ b/campaign/event_20240815_cn/campaign_base.py @@ -0,0 +1,84 @@ +from module.base.timer import Timer +from module.base.utils import area_in_area, area_pad +from module.campaign.campaign_base import CampaignBase as CampaignBase_ +from module.combat.assets import GET_ITEMS_1 +from module.exception import CampaignNameError +from module.logger import logger + + +class CampaignBase(CampaignBase_): + entrance_timer = Timer(2) + + def get_story_entrance(self): + """ + Returns: + Button: Or None if nothing matched. + """ + # 5 story stage after clearing A2 + # You can't go anywhere unless you clicked it + button = self.image_color_button( + area=(66, 200, 1200, 690), color=(0, 0, 0), + color_threshold=240, encourage=10, name='STORY_ENTRANCE') + if button is None: + return None + # Blacklisted area + if area_in_area(button.button, area_pad((424, 522, 444, 542), pad=-20)): + return None + return button + + def handle_story_entrance(self): + if not self.entrance_timer.reached(): + return False + + entrance = self.get_story_entrance() + if entrance is None: + return False + + self.device.click(entrance) + self.entrance_timer.reset() + return True + + def ensure_no_stage_entrance(self, skip_first_screenshot=True): + logger.info('ensure_no_stage_entrance') + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.is_in_stage_page(): + # End + try: + self._get_stage_name(self.device.image) + return True + except (IndexError, CampaignNameError): + pass + # Click + if self.handle_story_entrance(): + continue + if self.handle_story_skip(): + self.interval_clear(GET_ITEMS_1) + self.entrance_timer.clear() + continue + if self.appear_then_click(GET_ITEMS_1, offset=(20, 20), interval=3): + self.entrance_timer.clear() + continue + + def handle_in_stage(self): + # Click after stage ended + if self.is_in_stage_page(): + if self.handle_story_entrance(): + return False + return super().handle_in_stage() + + def handle_get_chapter_additional(self): + # Exit when having story entrance + if self.get_story_entrance(): + raise CampaignNameError + return super().handle_get_chapter_additional() + + def handle_campaign_ui_additional(self): + if self.get_story_entrance(): + self.ensure_no_stage_entrance() + return True + return super().handle_campaign_ui_additional() diff --git a/campaign/event_20240815_cn/d1.py b/campaign/event_20240815_cn/d1.py new file mode 100644 index 000000000..f955c486d --- /dev/null +++ b/campaign/event_20240815_cn/d1.py @@ -0,0 +1,101 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + +MAP = CampaignMap('D1') +MAP.shape = 'K9' +MAP.camera_data = ['D2', 'D6', 'H3', 'H7'] +MAP.camera_data_spawn_point = ['D6'] +MAP.map_data = """ + -- -- -- ME -- ++ ++ ++ -- -- ++ + -- ++ ME -- ME ++ ++ ++ ME ME ++ + ME -- -- -- -- ++ ++ ++ -- -- -- + ++ -- Me -- -- -- MB -- -- ME -- + ++ -- ++ ++ ++ -- __ -- ++ ++ -- + SP -- -- MS -- -- ME -- -- ++ ME + -- -- MS ++ -- Me -- Me -- ME -- + SP -- -- MS -- ++ ++ ++ -- -- -- + ++ -- ++ -- -- -- ++ -- -- ME -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 2}, + {'battle': 5, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, J6, K6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, J7, K7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, J8, K8, \ +A9, B9, C9, D9, E9, F9, G9, H9, I9, J9, K9, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['Intruder'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (80, 255 - 33), + 'width': (0.9, 10), + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 33, 255), + 'prominence': 10, + 'distance': 50, + # 'width': (0, 7), + 'wlen': 1000 + } + HOMO_EDGE_COLOR_RANGE = (0, 33) + HOMO_EDGE_HOUGHLINES_THRESHOLD = 300 + MAP_HAS_MOVABLE_NORMAL_ENEMY = True + MAP_SWIPE_PREDICT = False + MAP_SIREN_MOVE_WAIT = 0.5 + MAP_WALK_USE_CURRENT_FLEET = True + MAP_SWIPE_MULTIPLY = (1.025, 1.044) + MAP_SWIPE_MULTIPLY_MINITOUCH = (0.991, 1.009) + MAP_SWIPE_MULTIPLY_MAATOUCH = (0.962, 0.979) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/event_20240815_cn/d2.py b/campaign/event_20240815_cn/d2.py new file mode 100644 index 000000000..5fac5e519 --- /dev/null +++ b/campaign/event_20240815_cn/d2.py @@ -0,0 +1,106 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .d1 import Config as ConfigBase + +MAP = CampaignMap('D2') +MAP.shape = 'J8' +MAP.camera_data = ['D3', 'D6', 'G3', 'G6'] +MAP.camera_data_spawn_point = ['D3'] +MAP.map_data = """ + -- ++ -- -- -- ++ ++ -- -- -- + -- ++ SP -- SP ++ ++ ME ME ++ + -- Me -- -- -- Me -- -- -- -- + -- -- -- MS -- -- ++ -- ME -- + ++ -- MS -- MS -- ++ Me -- -- + -- -- ++ ++ ++ -- Me -- -- ME + ++ -- Me -- -- __ -- -- ++ ++ + MB -- ++ ME ME -- ME ME ++ ++ +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2, 'siren': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 2}, + {'battle': 5, 'enemy': 1}, + {'battle': 6, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, J6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, J7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, J8, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = [] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_HAS_MOVABLE_NORMAL_ENEMY = False + MAP_SIREN_HAS_BOSS_ICON_SMALL = True + MAP_SWIPE_MULTIPLY = (1.206, 1.228) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.166, 1.188) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.132, 1.152) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=1): + return True + + return self.battle_default() + + def battle_5(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_6(self): + return self.fleet_boss.clear_boss() + + def before_boss(self): + # Handle giant boss at A8 + logger.info('B2 before boss') + grid = SelectedGrids([B6, C7]).sort('weight', 'cost')[0] + self.goto(grid) + self.goto(B8) + + def clear_boss(self): + self.before_boss() + super().clear_boss() + + def brute_clear_boss(self): + self.before_boss() + super().brute_clear_boss() diff --git a/campaign/event_20240815_cn/d3.py b/campaign/event_20240815_cn/d3.py new file mode 100644 index 000000000..4aa22a947 --- /dev/null +++ b/campaign/event_20240815_cn/d3.py @@ -0,0 +1,94 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .d1 import Config as ConfigBase + +MAP = CampaignMap('D3') +MAP.shape = 'K9' +MAP.camera_data = ['D3', 'D7', 'H3', 'H7'] +MAP.camera_data_spawn_point = ['D3'] +MAP.map_data = """ + -- -- ++ -- -- -- -- -- ++ ++ -- + ++ ++ ++ -- -- ++ -- ME ++ ++ -- + ++ ++ ++ -- Me ++ ME -- ME ME -- + ++ ++ ++ -- -- Me -- __ -- -- MB + SP -- SP -- -- -- -- ME -- -- MB + -- -- -- ++ ++ ME -- ++ ME ME -- + -- MS -- -- ++ Me -- ++ ++ ++ ++ + MS -- MS -- Me -- -- -- ME -- -- + ++ ++ ++ -- -- -- ++ ME -- -- -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2, 'siren': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 2}, + {'battle': 5, 'enemy': 1}, + {'battle': 6, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, J6, K6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, J7, K7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, J8, K8, \ +A9, B9, C9, D9, E9, F9, G9, H9, I9, J9, K9, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['AmagiMasked'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_HAS_MOVABLE_NORMAL_ENEMY = True + MAP_SIREN_HAS_BOSS_ICON_SMALL = False + MAP_SWIPE_MULTIPLY = (1.108, 1.129) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.072, 1.092) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.041, 1.059) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=1): + return True + + return self.battle_default() + + def battle_5(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_6(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/event_20240815_cn/sp.py b/campaign/event_20240815_cn/sp.py new file mode 100644 index 000000000..43992db29 --- /dev/null +++ b/campaign/event_20240815_cn/sp.py @@ -0,0 +1,117 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + +MAP = CampaignMap('SP') +MAP.shape = 'I9' +MAP.camera_data = ['D4', 'F4', 'E3'] +MAP.camera_data_spawn_point = ['E6'] +MAP.map_data = """ + ++ ++ ++ -- MB -- ++ ++ ++ + -- ++ -- -- -- -- -- ++ -- + ME -- ME ++ -- ++ ME -- ME + -- ME ++ ++ -- ++ ++ ME -- + -- -- ++ ++ MS ++ ++ -- -- + ++ -- -- MS -- MS -- -- ++ + ++ ++ -- -- __ -- -- ++ ++ + ++ ++ ++ SP -- SP ++ ++ ++ + -- -- -- ++ -- ++ -- -- -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 6, 'siren': 3}, + {'battle': 1}, + {'battle': 2}, + {'battle': 3}, + {'battle': 4}, + {'battle': 5}, + {'battle': 6}, + {'battle': 7, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, \ +A9, B9, C9, D9, E9, F9, G9, H9, I9, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['AmagiMasked'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = False + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + STAR_REQUIRE_1 = 0 + STAR_REQUIRE_2 = 0 + STAR_REQUIRE_3 = 0 + # ===== End of generated config ===== + + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (80, 255 - 33), + 'width': (0.9, 10), + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 33, 255), + 'prominence': 10, + 'distance': 50, + # 'width': (0, 7), + 'wlen': 1000 + } + HOMO_EDGE_COLOR_RANGE = (0, 33) + HOMO_EDGE_HOUGHLINES_THRESHOLD = 300 + MAP_IS_ONE_TIME_STAGE = True + MAP_SWIPE_MULTIPLY = (1.063, 1.083) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.028, 1.047) + MAP_SWIPE_MULTIPLY_MAATOUCH = (0.998, 1.016) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def map_data_init(self, map_): + super().map_data_init(map_) + E5.is_siren = True + D6.is_siren = True + F6.is_siren = True + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=2): + return True + + return self.battle_default() + + def battle_5(self): + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_7(self): + return self.fleet_boss.clear_boss() diff --git a/deploy/adb.py b/deploy/adb.py index d4feb1235..90b4e8210 100644 --- a/deploy/adb.py +++ b/deploy/adb.py @@ -5,6 +5,14 @@ from deploy.emulator import EmulatorConnect from deploy.logger import logger from deploy.utils import * +IGNORE_SERIAL = [ + # Water-cooling display + # https://github.com/LmeSzinc/AzurLaneAutoScript/issues/3412 + 'HRBDFUN', + # USB network card + '1234567890ABCDEF', +] + def show_fix_tip(module): logger.info(f""" @@ -59,6 +67,8 @@ class AdbManager(DeployConfig): del os.environ[k] for device in adbutils.adb.iter_device(): + if device.serial in IGNORE_SERIAL: + continue logger.info(f'Init device {device}') initer = init.Initer(device, loglevel=logging.DEBUG) # MuMu X has no ro.product.cpu.abi, pick abi from ro.product.cpu.abilist diff --git a/dev_tools/map_extractor.py b/dev_tools/map_extractor.py index 5d561ca86..1c1290588 100644 --- a/dev_tools/map_extractor.py +++ b/dev_tools/map_extractor.py @@ -266,6 +266,11 @@ DIC_SIREN_NAME_CHI_TO_ENG = { 'aerfuleiduo': 'AlfredoOriani', 'guogan': 'LAudacieux', 'dipulaikesi': 'Dupleix', + + # Windborne Steel Wings + 'qinraozhe_IV': 'Intruder', + 'tiancheng_m_qingxun': 'AmagiMasked', + 'tiancheng_m_zhongxun': 'AmagiMasked', } @@ -469,7 +474,7 @@ class MapData: lines.append(f'MAP.portal_data = {self.portal}') lines.append('MAP.map_data = \"\"\"') for y in range(self.shape[1] + 1): - lines.append(' ' + ' '.join([self.map_data[(x, y)] for x in range(self.shape[0] + 1)])) + lines.append(' ' + ' '.join([self.map_data.get((x, y), '??') for x in range(self.shape[0] + 1)])) lines.append('\"\"\"') if self.map_data_loop is not None: lines.append('MAP.map_data_loop = \"\"\"') diff --git a/module/campaign/campaign_ocr.py b/module/campaign/campaign_ocr.py index d0ed29e41..c414efe58 100644 --- a/module/campaign/campaign_ocr.py +++ b/module/campaign/campaign_ocr.py @@ -6,6 +6,7 @@ from module.base.timer import Timer from module.base.utils import * from module.exception import CampaignNameError from module.logger import logger +from module.map.assets import WITHDRAW from module.ocr.ocr import Ocr from module.template.assets import * @@ -321,6 +322,15 @@ class CampaignOcr(ModuleBase): logger.attr('Chapter', self.campaign_chapter) logger.attr('Stage', ', '.join(self.stage_entrance.keys())) + def handle_get_chapter_additional(self): + """ + Returns: + bool: If clicked + """ + if self.appear(WITHDRAW, offset=(30, 30)): + logger.warning(f'get_chapter_index: WITHDRAW appears') + raise CampaignNameError + def get_chapter_index(self, image): """ A tricky method for ui_ensure_index @@ -335,7 +345,8 @@ class CampaignOcr(ModuleBase): while 1: if timeout.reached(): raise CampaignNameError - + if self.handle_get_chapter_additional(): + continue try: self._get_stage_name(image) break diff --git a/module/campaign/campaign_ui.py b/module/campaign/campaign_ui.py index 7c0137f60..5912a5064 100644 --- a/module/campaign/campaign_ui.py +++ b/module/campaign/campaign_ui.py @@ -2,20 +2,30 @@ from module.base.timer import Timer from module.campaign.assets import * from module.campaign.campaign_event import CampaignEvent from module.campaign.campaign_ocr import CampaignOcr -from module.exception import CampaignNameError, ScriptEnd +from module.exception import CampaignEnd, CampaignNameError, ScriptEnd from module.logger import logger +from module.map.assets import WITHDRAW +from module.map.map_operation import MapOperation from module.ui.assets import CAMPAIGN_CHECK from module.ui.switch import Switch -MODE_SWITCH_1 = Switch('Mode_switch_1', offset=(30, 10)) + +class ModeSwitch(Switch): + def handle_additional(self, main): + if main.appear(WITHDRAW, offset=(30, 30)): + logger.warning(f'ModeSwitch: WITHDRAW appears') + raise CampaignNameError + + +MODE_SWITCH_1 = ModeSwitch('Mode_switch_1', offset=(30, 10)) MODE_SWITCH_1.add_status('normal', SWITCH_1_NORMAL) MODE_SWITCH_1.add_status('hard', SWITCH_1_HARD) -MODE_SWITCH_2 = Switch('Mode_switch_2', offset=(30, 10)) +MODE_SWITCH_2 = ModeSwitch('Mode_switch_2', offset=(30, 10)) MODE_SWITCH_2.add_status('hard', SWITCH_2_HARD) MODE_SWITCH_2.add_status('ex', SWITCH_2_EX) -class CampaignUI(CampaignEvent, CampaignOcr): +class CampaignUI(MapOperation, CampaignEvent, CampaignOcr): ENTRANCE = Button(area=(), color=(), button=(), name='default_button') def campaign_ensure_chapter(self, index, skip_first_screenshot=True): @@ -202,6 +212,21 @@ class CampaignUI(CampaignEvent, CampaignOcr): else: logger.warning(f'Unknown campaign chapter: {name}') + def handle_campaign_ui_additional(self): + """ + Returns: + bool: If handled + """ + if self.appear(WITHDRAW, offset=(30, 30)): + # logger.info("WITHDRAW button found, wait until map loaded to prevent bugs in game client") + self.ensure_no_info_bar(timeout=2) + try: + self.withdraw() + except CampaignEnd: + pass + return True + return False + def ensure_campaign_ui(self, name, mode='normal'): for n in range(20): try: @@ -210,6 +235,8 @@ class CampaignUI(CampaignEvent, CampaignOcr): return True except CampaignNameError: pass + if self.handle_campaign_ui_additional(): + continue self.device.screenshot() diff --git a/module/config/argument/args.json b/module/config/argument/args.json index d220edefe..ecd315f88 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -62,6 +62,8 @@ "cn_android-21", "cn_android-22", "cn_android-23", + "cn_android-24", + "cn_android-25", "cn_ios-0", "cn_ios-1", "cn_ios-2", @@ -1927,17 +1929,18 @@ "event_20240229_cn", "event_20240425_cn", "event_20240521_cn", - "event_20240725_cn" + "event_20240725_cn", + "event_20240815_cn" ], "display": "hide", "option_bold": [ - "event_20211229_cn", - "event_20240725_cn" + "event_20240425_cn", + "event_20240815_cn" ], - "cn": "event_20240725_cn", - "en": "event_20240725_cn", - "jp": "event_20240725_cn", - "tw": "event_20211229_cn" + "cn": "event_20240815_cn", + "en": "event_20240815_cn", + "jp": "event_20240815_cn", + "tw": "event_20240425_cn" }, "Mode": { "type": "select", @@ -5031,16 +5034,17 @@ "event_20240229_cn", "event_20240425_cn", "event_20240521_cn", - "event_20240725_cn" + "event_20240725_cn", + "event_20240815_cn" ], "option_bold": [ - "event_20211229_cn", - "event_20240725_cn" + "event_20240425_cn", + "event_20240815_cn" ], - "cn": "event_20240725_cn", - "en": "event_20240725_cn", - "jp": "event_20240725_cn", - "tw": "event_20211229_cn" + "cn": "event_20240815_cn", + "en": "event_20240815_cn", + "jp": "event_20240815_cn", + "tw": "event_20240425_cn" }, "Mode": { "type": "select", @@ -5933,16 +5937,17 @@ "event_20240229_cn", "event_20240425_cn", "event_20240521_cn", - "event_20240725_cn" + "event_20240725_cn", + "event_20240815_cn" ], "option_bold": [ - "event_20211229_cn", - "event_20240725_cn" + "event_20240425_cn", + "event_20240815_cn" ], - "cn": "event_20240725_cn", - "en": "event_20240725_cn", - "jp": "event_20240725_cn", - "tw": "event_20211229_cn" + "cn": "event_20240815_cn", + "en": "event_20240815_cn", + "jp": "event_20240815_cn", + "tw": "event_20240425_cn" }, "Mode": { "type": "select", @@ -6347,13 +6352,12 @@ "raid_20240328" ], "option_bold": [ - "raid_20230629", "raid_20240328" ], "cn": "raid_20240328", "en": "raid_20240328", "jp": "raid_20240328", - "tw": "raid_20230629" + "tw": "raid_20240328" }, "Mode": { "type": "select", @@ -7343,16 +7347,17 @@ "event_20240229_cn", "event_20240425_cn", "event_20240521_cn", - "event_20240725_cn" + "event_20240725_cn", + "event_20240815_cn" ], "option_bold": [ - "event_20211229_cn", - "event_20240725_cn" + "event_20240425_cn", + "event_20240815_cn" ], - "cn": "event_20240725_cn", - "en": "event_20240725_cn", - "jp": "event_20240725_cn", - "tw": "event_20211229_cn" + "cn": "event_20240815_cn", + "en": "event_20240815_cn", + "jp": "event_20240815_cn", + "tw": "event_20240425_cn" }, "Mode": { "type": "select", @@ -7811,16 +7816,17 @@ "event_20240229_cn", "event_20240425_cn", "event_20240521_cn", - "event_20240725_cn" + "event_20240725_cn", + "event_20240815_cn" ], "option_bold": [ - "event_20211229_cn", - "event_20240725_cn" + "event_20240425_cn", + "event_20240815_cn" ], - "cn": "event_20240725_cn", - "en": "event_20240725_cn", - "jp": "event_20240725_cn", - "tw": "event_20211229_cn" + "cn": "event_20240815_cn", + "en": "event_20240815_cn", + "jp": "event_20240815_cn", + "tw": "event_20240425_cn" }, "Mode": { "type": "select", @@ -8279,16 +8285,17 @@ "event_20240229_cn", "event_20240425_cn", "event_20240521_cn", - "event_20240725_cn" + "event_20240725_cn", + "event_20240815_cn" ], "option_bold": [ - "event_20211229_cn", - "event_20240725_cn" + "event_20240425_cn", + "event_20240815_cn" ], - "cn": "event_20240725_cn", - "en": "event_20240725_cn", - "jp": "event_20240725_cn", - "tw": "event_20211229_cn" + "cn": "event_20240815_cn", + "en": "event_20240815_cn", + "jp": "event_20240815_cn", + "tw": "event_20240425_cn" }, "Mode": { "type": "select", @@ -8747,16 +8754,17 @@ "event_20240229_cn", "event_20240425_cn", "event_20240521_cn", - "event_20240725_cn" + "event_20240725_cn", + "event_20240815_cn" ], "option_bold": [ - "event_20211229_cn", - "event_20240725_cn" + "event_20240425_cn", + "event_20240815_cn" ], - "cn": "event_20240725_cn", - "en": "event_20240725_cn", - "jp": "event_20240725_cn", - "tw": "event_20211229_cn" + "cn": "event_20240815_cn", + "en": "event_20240815_cn", + "jp": "event_20240815_cn", + "tw": "event_20240425_cn" }, "Mode": { "type": "select", @@ -9205,16 +9213,17 @@ "event_20240229_cn", "event_20240425_cn", "event_20240521_cn", - "event_20240725_cn" + "event_20240725_cn", + "event_20240815_cn" ], "option_bold": [ - "event_20211229_cn", - "event_20240725_cn" + "event_20240425_cn", + "event_20240815_cn" ], - "cn": "event_20240725_cn", - "en": "event_20240725_cn", - "jp": "event_20240725_cn", - "tw": "event_20211229_cn" + "cn": "event_20240815_cn", + "en": "event_20240815_cn", + "jp": "event_20240815_cn", + "tw": "event_20240425_cn" }, "Mode": { "type": "select", @@ -9616,13 +9625,12 @@ "raid_20240328" ], "option_bold": [ - "raid_20230629", "raid_20240328" ], "cn": "raid_20240328", "en": "raid_20240328", "jp": "raid_20240328", - "tw": "raid_20230629" + "tw": "raid_20240328" }, "Mode": { "type": "select", diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 4b845df66..818726e92 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -20,7 +20,7 @@ class GeneratedConfig: # Group `Emulator` Emulator_Serial = 'auto' Emulator_PackageName = 'auto' # auto, com.bilibili.azurlane, com.YoStarEN.AzurLane, com.YoStarJP.AzurLane, com.hkmanjuu.azurlane.gp, com.bilibili.blhx.huawei, com.bilibili.blhx.mi, com.tencent.tmgp.bilibili.blhx, com.bilibili.blhx.baidu, com.bilibili.blhx.qihoo, com.bilibili.blhx.nearme.gamecenter, com.bilibili.blhx.vivo, com.bilibili.blhx.mz, com.bilibili.blhx.dl, com.bilibili.blhx.lenovo, com.bilibili.blhx.uc, com.bilibili.blhx.mzw, com.yiwu.blhx.yx15, com.bilibili.blhx.m4399, com.bilibili.blhx.bilibiliMove, com.hkmanjuu.azurlane.gp.mc - Emulator_ServerName = 'disabled' # disabled, cn_android-0, cn_android-1, cn_android-2, cn_android-3, cn_android-4, cn_android-5, cn_android-6, cn_android-7, cn_android-8, cn_android-9, cn_android-10, cn_android-11, cn_android-12, cn_android-13, cn_android-14, cn_android-15, cn_android-16, cn_android-17, cn_android-18, cn_android-19, cn_android-20, cn_android-21, cn_android-22, cn_android-23, cn_ios-0, cn_ios-1, cn_ios-2, cn_ios-3, cn_ios-4, cn_ios-5, cn_ios-6, cn_ios-7, cn_ios-8, cn_ios-9, cn_ios-10, cn_channel-0, cn_channel-1, cn_channel-2, cn_channel-3, cn_channel-4, en-0, en-1, en-2, en-3, en-4, en-5, jp-0, jp-1, jp-2, jp-3, jp-4, jp-5, jp-6, jp-7, jp-8, jp-9, jp-10, jp-11, jp-12, jp-13, jp-14, jp-15, jp-16, jp-17 + Emulator_ServerName = 'disabled' # disabled, cn_android-0, cn_android-1, cn_android-2, cn_android-3, cn_android-4, cn_android-5, cn_android-6, cn_android-7, cn_android-8, cn_android-9, cn_android-10, cn_android-11, cn_android-12, cn_android-13, cn_android-14, cn_android-15, cn_android-16, cn_android-17, cn_android-18, cn_android-19, cn_android-20, cn_android-21, cn_android-22, cn_android-23, cn_android-24, cn_android-25, cn_ios-0, cn_ios-1, cn_ios-2, cn_ios-3, cn_ios-4, cn_ios-5, cn_ios-6, cn_ios-7, cn_ios-8, cn_ios-9, cn_ios-10, cn_channel-0, cn_channel-1, cn_channel-2, cn_channel-3, cn_channel-4, en-0, en-1, en-2, en-3, en-4, en-5, jp-0, jp-1, jp-2, jp-3, jp-4, jp-5, jp-6, jp-7, jp-8, jp-9, jp-10, jp-11, jp-12, jp-13, jp-14, jp-15, jp-16, jp-17 Emulator_ScreenshotMethod = 'auto' # auto, ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast, DroidCast_raw, scrcpy, nemu_ipc, ldopengl Emulator_ControlMethod = 'MaaTouch' # ADB, uiautomator2, minitouch, Hermit, MaaTouch Emulator_ScreenshotDedithering = False diff --git a/module/config/config_manual.py b/module/config/config_manual.py index 67ef0593c..e6b1c7576 100644 --- a/module/config/config_manual.py +++ b/module/config/config_manual.py @@ -144,6 +144,8 @@ class ManualConfig: MAP_ENEMY_GENRE_SIMILARITY = 0.85 MAP_SIREN_MOVE_WAIT = 1.5 # The enemy moving takes about 1.2 ~ 1.5s. MAP_SIREN_COUNT = 0 + MAP_SIREN_HAS_BOSS_ICON = False # Anonymous siren with small boss icon at bottom-right + MAP_SIREN_HAS_BOSS_ICON_SMALL = False MAP_HAS_MYSTERY = True MAP_MYSTERY_MAP_CLICK = True MAP_MYSTERY_HAS_CARRIER = False diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 9650d07b4..70c30bded 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -397,6 +397,8 @@ "cn_android-21": "[国服] 莱茵河卫兵", "cn_android-22": "[国服] 北极光计划", "cn_android-23": "[国服] 长戟计划", + "cn_android-24": "[国服] 暴雨行动", + "cn_android-25": "[国服] 水仙行动", "cn_ios-0": "[国服] 夏威夷", "cn_ios-1": "[国服] 珊瑚海", "cn_ios-2": "[国服] 中途岛", @@ -784,6 +786,7 @@ "event_20240425_cn": "Heart-Linking Harmony", "event_20240521_cn": "Light of the Martyrium", "event_20240725_cn": "Interlude of Illusions", + "event_20240815_cn": "Windborne Steel Wings", "raid_20200624": "Air Raid Drills with Essex Rerun", "raid_20210708": "Cross Wave rerun", "raid_20220127": "Mystery Investigation", diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index a60b19493..219a048a0 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -397,6 +397,8 @@ "cn_android-21": "[国服] 莱茵河卫兵", "cn_android-22": "[国服] 北极光计划", "cn_android-23": "[国服] 长戟计划", + "cn_android-24": "[国服] 暴雨行动", + "cn_android-25": "[国服] 水仙行动", "cn_ios-0": "[国服] 夏威夷", "cn_ios-1": "[国服] 珊瑚海", "cn_ios-2": "[国服] 中途岛", @@ -784,6 +786,7 @@ "event_20240425_cn": "共鳴のパッション", "event_20240521_cn": "赫輝のマルティリウム", "event_20240725_cn": "夢幻の間奏曲", + "event_20240815_cn": "錬翼空翔", "raid_20200624": "特別演習超空強襲波(復刻)", "raid_20210708": "交錯する新たな波 (復刻)", "raid_20220127": "秘密事件調査", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 0f2dc2cf2..8a68ef430 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -397,6 +397,8 @@ "cn_android-21": "[国服] 莱茵河卫兵", "cn_android-22": "[国服] 北极光计划", "cn_android-23": "[国服] 长戟计划", + "cn_android-24": "[国服] 暴雨行动", + "cn_android-25": "[国服] 水仙行动", "cn_ios-0": "[国服] 夏威夷", "cn_ios-1": "[国服] 珊瑚海", "cn_ios-2": "[国服] 中途岛", @@ -784,6 +786,7 @@ "event_20240425_cn": "共鸣的PASSION", "event_20240521_cn": "绽放于辉光之城", "event_20240725_cn": "幻梦间奏曲", + "event_20240815_cn": "铁翼擎风", "raid_20200624": "复刻特别演习埃塞克斯级", "raid_20210708": "复刻穿越彼方的水线", "raid_20220127": "演习神秘事件调查", diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 04fb79d38..b1fd2c72f 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -397,6 +397,8 @@ "cn_android-21": "[国服] 莱茵河卫兵", "cn_android-22": "[国服] 北极光计划", "cn_android-23": "[国服] 长戟计划", + "cn_android-24": "[国服] 暴雨行动", + "cn_android-25": "[国服] 水仙行动", "cn_ios-0": "[国服] 夏威夷", "cn_ios-1": "[国服] 珊瑚海", "cn_ios-2": "[国服] 中途岛", @@ -781,9 +783,10 @@ "event_20231123_cn": "蒼閃忍法帖", "event_20231221_cn": "Light-Chasing Sea of Stars", "event_20240229_cn": "Snowrealm Peregrination", - "event_20240425_cn": "Heart-Linking Harmony", + "event_20240425_cn": "共鳴的PASSION", "event_20240521_cn": "Light of the Martyrium", "event_20240725_cn": "Interlude of Illusions", + "event_20240815_cn": "Windborne Steel Wings", "raid_20200624": "特別演習埃塞克斯級(復刻)", "raid_20210708": "復刻穿越彼方的水線", "raid_20220127": "演習神秘事件調查", @@ -792,7 +795,7 @@ "raid_20230118": "冬日的尋路人", "raid_20230629": "綠洲往事", "raid_20240130": "Spring Festive Fiasco", - "raid_20240328": "From Zero to Hero", + "raid_20240328": "從零開始的魔王討伐之旅", "war_archives_20180607_cn": "檔案 墨染的鋼鐵之花", "war_archives_20180726_cn": "檔案 光與影的鳶尾之華", "war_archives_20181020_en": "檔案 努力希望和計劃", diff --git a/module/config/server.py b/module/config/server.py index e26060a18..85f3b0758 100644 --- a/module/config/server.py +++ b/module/config/server.py @@ -36,6 +36,35 @@ VALID_CHANNEL_PACKAGE = { # Tw 'com.hkmanjuu.azurlane.gp.mc': ('tw', 'MyCard'), } +DICT_PACKAGE_TO_ACTIVITY = { + # com.manjuu.azurlane.MainActivity + # VALID_PACKAGE + 'com.bilibili.azurlane': 'com.manjuu.azurlane.MainActivity', + 'com.YoStarEN.AzurLane': 'com.manjuu.azurlane.PrePermissionActivity', + 'com.YoStarJP.AzurLane': 'com.manjuu.azurlane.PrePermissionActivity', + 'com.hkmanjuu.azurlane.gp': 'com.manjuu.azurlane.PrePermissionActivity', + # App stores + 'com.bilibili.blhx.huawei': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.mi': 'com.manjuu.azurlane.SplashActivity', + 'com.tencent.tmgp.bilibili.blhx': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.baidu': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.qihoo': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.nearme.gamecenter': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.vivo': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.mz': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.dl': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.lenovo': 'com.manjuu.azurlane.SplashActivity', + + # 3rd party gaming platforms + 'com.bilibili.blhx.uc': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.mzw': 'com.manjuu.azurlane.SplashActivity', + 'com.yiwu.blhx.yx15': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.m4399': 'com.manjuu.azurlane.SplashActivity', + 'com.bilibili.blhx.bilibiliMove': 'com.manjuu.azurlane.SplashActivity', + + # Tw + 'com.hkmanjuu.azurlane.gp.mc': 'com.manjuu.azurlane.PrePermissionActivity', +} VALID_SERVER_LIST = { 'cn_android': [ '莱茵演习', '巴巴罗萨', '霸王行动', '冰山行动', '彩虹计划', @@ -43,7 +72,7 @@ VALID_SERVER_LIST = { '杜立特空袭', '地狱犬行动', '开罗宣言', '奥林匹克行动', '小王冠行动', '波茨坦公告', '白色方案', '瓦尔基里行动', '曼哈顿计划', '八月风暴', '秋季旅行', '水星行动', '莱茵河卫兵', - '北极光计划', '长戟计划' + '北极光计划', '长戟计划', '暴雨行动', '水仙行动', ], 'cn_ios': [ '夏威夷', '珊瑚海', '中途岛', '铁底湾', '所罗门', '马里亚纳', diff --git a/module/daemon/benchmark.py b/module/daemon/benchmark.py index 9bbdc362d..cce5e37a2 100644 --- a/module/daemon/benchmark.py +++ b/module/daemon/benchmark.py @@ -207,8 +207,7 @@ class Benchmark(DaemonBase, CampaignUI): def run(self): self.config.override(Emulator_ScreenshotMethod='ADB') self.device.uninstall_minicap() - self.ui_goto_campaign() - self.campaign_set_chapter('7-2') + self.ensure_campaign_ui('7-2', mode='normal') logger.attr('DeviceType', self.config.Benchmark_DeviceType) logger.attr('TestScene', self.config.Benchmark_TestScene) diff --git a/module/device/connection.py b/module/device/connection.py index 5e1ef5300..a0a6636e5 100644 --- a/module/device/connection.py +++ b/module/device/connection.py @@ -265,6 +265,7 @@ class Connection(ConnectionAttr): return self.adb_shell(['getprop', name]).strip() @cached_property + @retry def cpu_abi(self) -> str: """ Returns: @@ -276,6 +277,7 @@ class Connection(ConnectionAttr): return abi @cached_property + @retry def sdk_ver(self) -> int: """ Android SDK/API levels, see https://apilevels.com/ @@ -289,6 +291,7 @@ class Connection(ConnectionAttr): return 0 @cached_property + @retry def is_avd(self): if get_serial_pair(self.serial)[0] is None: return False @@ -299,12 +302,35 @@ class Connection(ConnectionAttr): return False @cached_property + @retry + def is_waydroid(self): + res = self.adb_getprop('ro.product.brand') + logger.attr('ro.product.brand', res) + return 'waydroid' in res.lower() + + @cached_property + @retry def nemud_app_keep_alive(self) -> str: res = self.adb_getprop('nemud.app_keep_alive') logger.attr('nemud.app_keep_alive', res) return res + @cached_property @retry + def nemud_player_version(self) -> str: + # [nemud.player_product_version]: [3.8.27.2950] + res = self.adb_getprop('nemud.player_version') + logger.attr('nemud.player_version', res) + return res + + @cached_property + @retry + def nemud_player_engine(self) -> str: + # NEMUX or MACPRO + res = self.adb_getprop('nemud.player_engine') + logger.attr('nemud.player_engine', res) + return res + def check_mumu_app_keep_alive(self): if not self.is_mumu_family: return False @@ -324,6 +350,15 @@ class Connection(ConnectionAttr): logger.warning(f'Invalid nemud.app_keep_alive value: {res}') return False + @cached_property + def is_mumu_over_version_400(self) -> bool: + if not self.is_mumu_family: + return False + # >= 4.0 has no info in getprop + if self.nemud_player_version == '': + return True + return False + @cached_property def is_mumu_over_version_356(self) -> bool: """ @@ -334,12 +369,12 @@ class Connection(ConnectionAttr): """ if not self.is_mumu_family: return False + if self.is_mumu_over_version_400: + return True if self.nemud_app_keep_alive != '': return True if IS_MACINTOSH: - res = self.adb_getprop('nemud.player_engine') - logger.attr('nemud.player_engine', res) - if 'MACPRO' in res: + if 'MACPRO' in self.nemud_player_engine: return True return False @@ -659,13 +694,9 @@ class Connection(ConnectionAttr): # Brute force connect nearby ports to handle serial switches if self.is_mumu12_family: before = self.serial - for port_offset in [1, -1, 2, -2]: - port = self.port + port_offset - serial = self.serial.replace(str(self.port), str(port)) - msg = self.adb_client.connect(serial) - logger.info(msg) - if 'connected' in msg: - break + serial_list = [self.serial.replace(str(self.port), str(self.port + offset)) + for offset in [1, -1, 2, -2]] + self.adb_brute_force_connect(serial_list) self.detect_device() if self.serial != before: return True @@ -678,6 +709,25 @@ class Connection(ConnectionAttr): self.detect_device() return False + def adb_brute_force_connect(self, serial_list): + """ + Args: + serial_list (list[str]): + """ + import asyncio + ev = asyncio.new_event_loop() + + def _connect(serial): + msg = self.adb_client.connect(serial) + logger.info(msg) + return msg + + async def connect(): + tasks = [ev.run_in_executor(None, _connect, serial) for serial in serial_list] + await asyncio.gather(*tasks) + + ev.run_until_complete(connect()) + @Config.when(DEVICE_OVER_HTTP=True) def adb_connect(self): # No adb connect if over http diff --git a/module/device/connection_attr.py b/module/device/connection_attr.py index cb8257783..0814b9524 100644 --- a/module/device/connection_attr.py +++ b/module/device/connection_attr.py @@ -189,6 +189,10 @@ class ConnectionAttr: def is_network_device(self): return bool(re.match(r'\d+\.\d+\.\d+\.\d+:\d+', self.serial)) + @cached_property + def is_local_network_device(self): + return bool(re.match(r'192\.168\.\d+\.\d+:\d+', self.serial)) + @cached_property def is_over_http(self): return bool(re.match(r"^https?://", self.serial)) diff --git a/module/device/method/adb.py b/module/device/method/adb.py index 87b076997..d256e226a 100644 --- a/module/device/method/adb.py +++ b/module/device/method/adb.py @@ -8,6 +8,7 @@ from adbutils.errors import AdbError from lxml import etree from module.base.decorator import Config +from module.config.server import DICT_PACKAGE_TO_ACTIVITY from module.device.connection import Connection from module.device.method.utils import (RETRY_TRIES, retry_sleep, remove_prefix, handle_adb_error, handle_unknown_host_service, ImageTruncated, PackageNotInstalled) @@ -251,7 +252,7 @@ class Adb(Connection): raise OSError("Couldn't get focused app") @retry - def app_start_adb(self, package_name=None, allow_failure=False): + def _app_start_adb_monkey(self, package_name=None, allow_failure=False): """ Args: package_name (str): @@ -259,6 +260,9 @@ class Adb(Connection): Returns: bool: If success to start + + Raises: + PackageNotInstalled: """ if not package_name: package_name = self.package @@ -275,26 +279,125 @@ class Adb(Connection): raise PackageNotInstalled(package_name) elif 'inaccessible' in result: # /system/bin/sh: monkey: inaccessible or not found - pass + return False else: # Events injected: 1 # ## Network stats: elapsed time=4ms (0ms mobile, 0ms wifi, 4ms not connected) return True - result = self.adb_shell(['dumpsys', 'package', package_name]) - res = re.search(r'android.intent.action.MAIN:\s+\w+ ([\w.\/]+) filter \w+\s+' - r'.*\s+Category: "android.intent.category.LAUNCHER"', - result) - if res: - activity_name = res.group(1) - else: + @retry + def _app_start_adb_am(self, package_name=None, activity_name=None, allow_failure=False): + """ + Args: + package_name (str): + activity_name (str): + allow_failure (bool): + + Returns: + bool: If success to start + + Raises: + PackageNotInstalled: + """ + if not package_name: + package_name = self.package + if not activity_name: + result = self.adb_shell(['dumpsys', 'package', package_name]) + res = re.search(r'android.intent.action.MAIN:\s+\w+ ([\w.\/]+) filter \w+\s+' + r'.*\s+Category: "android.intent.category.LAUNCHER"', + result) + if res: + # com.bilibili.azurlane/com.manjuu.azurlane.MainActivity + activity_name = res.group(1) + try: + activity_name = activity_name.split('/')[-1] + except IndexError: + logger.error(f'No activity name from {activity_name}') + return False + else: + if allow_failure: + return False + else: + logger.error(result) + raise PackageNotInstalled(package_name) + + cmd = ['am', 'start', '-a', 'android.intent.action.MAIN', '-c', + 'android.intent.category.LAUNCHER', '-n', f'{package_name}/{activity_name}'] + if self.is_local_network_device and self.is_waydroid: + cmd += ['--windowingMode', '4'] + ret = self.adb_shell(cmd) + # Invalid activity + # Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=... } + # Error type 3 + # Error: Activity class {.../...} does not exist. + if 'Error: Activity class' in ret: if allow_failure: return False else: - logger.error(result) - raise PackageNotInstalled(package_name) - self.adb_shell(['am', 'start', '-a', 'android.intent.action.MAIN', '-c', - 'android.intent.category.LAUNCHER', '-n', activity_name]) + logger.error(ret) + return False + # Already running + # Warning: Activity not started, intent has been delivered to currently running top-most instance. + if 'Warning: Activity not started' in ret: + logger.info('App activity is already started') + return True + # Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.YoStarEN.AzurLane/com.manjuu.azurlane.MainActivity } + # java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.YoStarEN.AzurLane/com.manjuu.azurlane.MainActivity } from null (pid=5140, uid=2000) not exported from uid 10064 + # at android.os.Parcel.readException(Parcel.java:1692) + # at android.os.Parcel.readException(Parcel.java:1645) + # at android.app.ActivityManagerProxy.startActivityAsUser(ActivityManagerNative.java:3152) + # at com.android.commands.am.Am.runStart(Am.java:643) + # at com.android.commands.am.Am.onRun(Am.java:394) + # at com.android.internal.os.BaseCommand.run(BaseCommand.java:51) + # at com.android.commands.am.Am.main(Am.java:124) + # at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) + # at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:290) + if 'Permission Denial' in ret: + if allow_failure: + return False + else: + logger.error(ret) + logger.error('Permission Denial while starting app, probably because activity invalid') + return False + # Success + # Starting: Intent... + return True + + # No @retry decorator since _app_start_adb_am and _app_start_adb_monkey have @retry already + # @retry + def app_start_adb(self, package_name=None, activity_name=None, allow_failure=False): + """ + Args: + package_name (str): + If None, to get from config + activity_name (str): + If None, to get from DICT_PACKAGE_TO_ACTIVITY + If still None, launch from monkey + If monkey failed, fetch activity name and launch from am + allow_failure (bool): + True for no PackageNotInstalled raising, just return False + + Returns: + bool: If success to start + + Raises: + PackageNotInstalled: + """ + if not package_name: + package_name = self.package + if not activity_name: + activity_name = DICT_PACKAGE_TO_ACTIVITY.get(package_name) + + if activity_name: + if self._app_start_adb_am(package_name, activity_name, allow_failure): + return True + if self._app_start_adb_monkey(package_name, allow_failure): + return True + if self._app_start_adb_am(package_name, activity_name, allow_failure): + return True + + logger.error('app_start_adb: All trials failed') + return False @retry def app_stop_adb(self, package_name=None): diff --git a/module/device/method/nemu_ipc.py b/module/device/method/nemu_ipc.py index 315c1897e..cdb159f48 100644 --- a/module/device/method/nemu_ipc.py +++ b/module/device/method/nemu_ipc.py @@ -1,5 +1,6 @@ import asyncio import ctypes +import json import os import sys from functools import partial, wraps @@ -10,6 +11,7 @@ import numpy as np from module.base.decorator import cached_property, del_cached_property, has_cached_property from module.base.timer import Timer from module.base.utils import ensure_time +from module.config.utils import deep_get from module.device.method.minitouch import insert_swipe, random_rectangle_point from module.device.method.utils import RETRY_TRIES, retry_sleep from module.device.platform import Platform @@ -486,6 +488,9 @@ class NemuIpc(Platform): def nemu_ipc_available(self) -> bool: if not self.is_mumu_family: return False + # >= 4.0 has no info in getprop + if self.nemud_player_version == '': + return True if self.nemud_app_keep_alive == '': return False try: @@ -494,6 +499,58 @@ class NemuIpc(Platform): return False return True + @staticmethod + def check_mumu_app_keep_alive_400(file): + """ + Check app_keep_alive from emulator config if version >= 4.0 + + Args: + file: E:/ProgramFiles/MuMuPlayer-12.0/vms/MuMuPlayer-12.0-1/config/customer_config.json + + Returns: + bool: If success to read file + """ + # with E:\ProgramFiles\MuMuPlayer-12.0\shell\MuMuPlayer.exe + # config is E:\ProgramFiles\MuMuPlayer-12.0\vms\MuMuPlayer-12.0-1\config\customer_config.json + try: + with open(file, mode='r', encoding='utf-8') as f: + s = f.read() + data = json.loads(s) + except FileNotFoundError: + logger.warning(f'Failed to check check_mumu_app_keep_alive, file {file} not exists') + return False + value = deep_get(data, keys='customer.app_keptlive', default=None) + logger.attr('customer.app_keptlive', value) + if str(value).lower() == 'true': + # https://mumu.163.com/help/20230802/35047_1102450.html + logger.critical('请在MuMu模拟器设置内关闭 "后台挂机时保活运行"') + raise RequestHumanTakeover + return True + + def check_mumu_app_keep_alive(self): + if not self.is_mumu_over_version_400: + return super().check_mumu_app_keep_alive() + + # Try existing settings first + if self.config.EmulatorInfo_path: + index = NemuIpcImpl.serial_to_id(self.serial) + if index is not None: + file = os.path.abspath(os.path.join( + self.config.EmulatorInfo_path, f'../../vms/MuMuPlayer-12.0-{index}/configs/customer_config.json')) + if self.check_mumu_app_keep_alive_400(file): + return True + + # Search emulator instance + if self.emulator_instance is None: + logger.warning('Failed to check check_mumu_app_keep_alive as emulator_instance is None') + return False + name = self.emulator_instance.name + file = self.emulator_instance.emulator.abspath(f'../vms/{name}/configs/customer_config.json') + if self.check_mumu_app_keep_alive_400(file): + return True + + return False + def nemu_ipc_release(self): if has_cached_property(self, 'nemu_ipc'): self.nemu_ipc.disconnect() diff --git a/module/device/method/uiautomator_2.py b/module/device/method/uiautomator_2.py index 15110eb87..d5d4e951d 100644 --- a/module/device/method/uiautomator_2.py +++ b/module/device/method/uiautomator_2.py @@ -9,6 +9,7 @@ from adbutils.errors import AdbError from lxml import etree from module.base.utils import * +from module.config.server import DICT_PACKAGE_TO_ACTIVITY from module.device.connection import Connection from module.device.method.utils import (ImageTruncated, PackageNotInstalled, RETRY_TRIES, handle_adb_error, handle_unknown_host_service, possible_reasons, retry_sleep) @@ -224,15 +225,147 @@ class Uiautomator2(Connection): return result['package'] @retry - def app_start_uiautomator2(self, package_name=None): + def _app_start_u2_monkey(self, package_name=None, allow_failure=False): + """ + Args: + package_name (str): + allow_failure (bool): + + Returns: + bool: If success to start + + Raises: + PackageNotInstalled: + """ if not package_name: package_name = self.package - try: - self.u2.app_start(package_name) - except u2.exceptions.BaseError as e: - # BaseError: package "com.bilibili.azurlane" not found - logger.error(e) - raise PackageNotInstalled(package_name) + result = self.u2.shell([ + 'monkey', '-p', package_name, '-c', + 'android.intent.category.LAUNCHER', '--pct-syskeys', '0', '1' + ]) + if 'No activities found' in result.output: + # ** No activities found to run, monkey aborted. + if allow_failure: + return False + else: + logger.error(result) + raise PackageNotInstalled(package_name) + elif 'inaccessible' in result: + # /system/bin/sh: monkey: inaccessible or not found + return False + else: + # Events injected: 1 + # ## Network stats: elapsed time=4ms (0ms mobile, 0ms wifi, 4ms not connected) + return True + + @retry + def _app_start_u2_am(self, package_name=None, activity_name=None, allow_failure=False): + """ + Args: + package_name (str): + activity_name (str): + allow_failure (bool): + + Returns: + bool: If success to start + + Raises: + PackageNotInstalled: + """ + if not package_name: + package_name = self.package + if not activity_name: + try: + info = self.u2.app_info(package_name) + except u2.BaseError as e: + if allow_failure: + return False + # BaseError('package "111" not found') + elif 'not found' in str(e): + logger.error(e) + raise PackageNotInstalled(package_name) + # Unknown error + else: + raise + activity_name = info['mainActivity'] + + cmd = ['am', 'start', '-a', 'android.intent.action.MAIN', '-c', + 'android.intent.category.LAUNCHER', '-n', f'{package_name}/{activity_name}'] + if self.is_local_network_device and self.is_waydroid: + cmd += ['--windowingMode', '4'] + ret = self.u2.shell(cmd) + # Invalid activity + # Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=... } + # Error type 3 + # Error: Activity class {.../...} does not exist. + if 'Error: Activity class' in ret.output: + if allow_failure: + return False + else: + logger.error(ret) + return False + # Already running + # Warning: Activity not started, intent has been delivered to currently running top-most instance. + if 'Warning: Activity not started' in ret.output: + logger.info('App activity is already started') + return True + # Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.YoStarEN.AzurLane/com.manjuu.azurlane.MainActivity } + # java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.YoStarEN.AzurLane/com.manjuu.azurlane.MainActivity } from null (pid=5140, uid=2000) not exported from uid 10064 + # at android.os.Parcel.readException(Parcel.java:1692) + # at android.os.Parcel.readException(Parcel.java:1645) + # at android.app.ActivityManagerProxy.startActivityAsUser(ActivityManagerNative.java:3152) + # at com.android.commands.am.Am.runStart(Am.java:643) + # at com.android.commands.am.Am.onRun(Am.java:394) + # at com.android.internal.os.BaseCommand.run(BaseCommand.java:51) + # at com.android.commands.am.Am.main(Am.java:124) + # at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) + # at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:290) + if 'Permission Denial' in ret.output: + if allow_failure: + return False + else: + logger.error(ret) + logger.error('Permission Denial while starting app, probably because activity invalid') + return False + # Success + # Starting: Intent... + return True + + # No @retry decorator since _app_start_adb_am and _app_start_adb_monkey have @retry already + # @retry + def app_start_uiautomator2(self, package_name=None, activity_name=None, allow_failure=False): + """ + Args: + package_name (str): + If None, to get from config + activity_name (str): + If None, to get from DICT_PACKAGE_TO_ACTIVITY + If still None, launch from monkey + If monkey failed, fetch activity name and launch from am + allow_failure (bool): + True for no PackageNotInstalled raising, just return False + + Returns: + bool: If success to start + + Raises: + PackageNotInstalled: + """ + if not package_name: + package_name = self.package + if not activity_name: + activity_name = DICT_PACKAGE_TO_ACTIVITY.get(package_name) + + if activity_name: + if self._app_start_u2_am(package_name, activity_name, allow_failure): + return True + if self._app_start_u2_monkey(package_name, allow_failure): + return True + if self._app_start_u2_am(package_name, activity_name, allow_failure): + return True + + logger.error('app_start_uiautomator2: All trials failed') + return False @retry def app_stop_uiautomator2(self, package_name=None): diff --git a/module/freebies/assets.py b/module/freebies/assets.py index 9481df6e9..a0f1ac059 100644 --- a/module/freebies/assets.py +++ b/module/freebies/assets.py @@ -19,6 +19,7 @@ MAIL_EMPTY_2 = Button(area={'cn': (507, 364, 596, 391), 'en': (507, 364, 596, 39 MAIL_ENTER = Button(area={'cn': (1207, 393, 1253, 429), 'en': (1207, 393, 1253, 429), 'jp': (1207, 393, 1253, 429), 'tw': (1207, 393, 1253, 429)}, color={'cn': (109, 107, 95), 'en': (109, 107, 95), 'jp': (109, 107, 95), 'tw': (109, 107, 95)}, button={'cn': (1207, 393, 1253, 429), 'en': (1207, 393, 1253, 429), 'jp': (1207, 393, 1253, 429), 'tw': (1207, 393, 1253, 429)}, file={'cn': './assets/cn/freebies/MAIL_ENTER.png', 'en': './assets/en/freebies/MAIL_ENTER.png', 'jp': './assets/jp/freebies/MAIL_ENTER.png', 'tw': './assets/tw/freebies/MAIL_ENTER.png'}) MAIL_GUILD_MESSAGE = Button(area={'cn': (412, 214, 461, 235), 'en': (412, 214, 461, 235), 'jp': (412, 214, 461, 235), 'tw': (412, 214, 461, 235)}, color={'cn': (123, 124, 126), 'en': (123, 124, 126), 'jp': (123, 124, 126), 'tw': (123, 124, 126)}, button={'cn': (412, 214, 461, 235), 'en': (412, 214, 461, 235), 'jp': (412, 214, 461, 235), 'tw': (412, 214, 461, 235)}, file={'cn': './assets/cn/freebies/MAIL_GUILD_MESSAGE.png', 'en': './assets/en/freebies/MAIL_GUILD_MESSAGE.png', 'jp': './assets/jp/freebies/MAIL_GUILD_MESSAGE.png', 'tw': './assets/tw/freebies/MAIL_GUILD_MESSAGE.png'}) MAIL_MANAGE = Button(area={'cn': (415, 639, 485, 658), 'en': (393, 641, 463, 660), 'jp': (407, 641, 495, 658), 'tw': (415, 639, 485, 658)}, color={'cn': (116, 210, 255), 'en': (131, 214, 255), 'jp': (115, 209, 255), 'tw': (116, 210, 255)}, button={'cn': (415, 639, 485, 658), 'en': (393, 641, 463, 660), 'jp': (407, 641, 495, 658), 'tw': (415, 639, 485, 658)}, file={'cn': './assets/cn/freebies/MAIL_MANAGE.png', 'en': './assets/en/freebies/MAIL_MANAGE.png', 'jp': './assets/jp/freebies/MAIL_MANAGE.png', 'tw': './assets/cn/freebies/MAIL_MANAGE.png'}) +MAIL_SELECT_ALL = Button(area={'cn': (390, 319, 410, 339), 'en': (390, 319, 410, 339), 'jp': (390, 323, 410, 343), 'tw': (390, 319, 410, 339)}, color={'cn': (87, 88, 88), 'en': (87, 88, 88), 'jp': (91, 90, 91), 'tw': (87, 88, 88)}, button={'cn': (390, 319, 410, 339), 'en': (390, 319, 410, 339), 'jp': (390, 323, 410, 343), 'tw': (390, 319, 410, 339)}, file={'cn': './assets/cn/freebies/MAIL_SELECT_ALL.png', 'en': './assets/en/freebies/MAIL_SELECT_ALL.png', 'jp': './assets/jp/freebies/MAIL_SELECT_ALL.png', 'tw': './assets/tw/freebies/MAIL_SELECT_ALL.png'}) MAIL_SELECT_COINS = Button(area={'cn': (562, 401, 582, 421), 'en': (562, 401, 582, 421), 'jp': (562, 410, 582, 430), 'tw': (562, 401, 582, 421)}, color={'cn': (241, 240, 241), 'en': (241, 240, 241), 'jp': (239, 239, 239), 'tw': (241, 240, 241)}, button={'cn': (562, 401, 582, 421), 'en': (562, 401, 582, 421), 'jp': (562, 410, 582, 430), 'tw': (562, 401, 582, 421)}, file={'cn': './assets/cn/freebies/MAIL_SELECT_COINS.png', 'en': './assets/en/freebies/MAIL_SELECT_COINS.png', 'jp': './assets/jp/freebies/MAIL_SELECT_COINS.png', 'tw': './assets/tw/freebies/MAIL_SELECT_COINS.png'}) MAIL_SELECT_CUBE = Button(area={'cn': (442, 401, 462, 421), 'en': (442, 401, 462, 421), 'jp': (442, 410, 462, 430), 'tw': (442, 401, 462, 421)}, color={'cn': (241, 241, 241), 'en': (241, 241, 241), 'jp': (239, 239, 239), 'tw': (241, 241, 241)}, button={'cn': (442, 401, 462, 421), 'en': (442, 401, 462, 421), 'jp': (442, 410, 462, 430), 'tw': (442, 401, 462, 421)}, file={'cn': './assets/cn/freebies/MAIL_SELECT_CUBE.png', 'en': './assets/en/freebies/MAIL_SELECT_CUBE.png', 'jp': './assets/jp/freebies/MAIL_SELECT_CUBE.png', 'tw': './assets/tw/freebies/MAIL_SELECT_CUBE.png'}) MAIL_SELECT_GEMS = Button(area={'cn': (442, 441, 462, 461), 'en': (442, 441, 462, 461), 'jp': (442, 460, 462, 480), 'tw': (442, 441, 462, 461)}, color={'cn': (241, 241, 241), 'en': (241, 241, 241), 'jp': (239, 239, 239), 'tw': (241, 241, 241)}, button={'cn': (442, 441, 462, 461), 'en': (442, 441, 462, 461), 'jp': (442, 460, 462, 480), 'tw': (442, 441, 462, 461)}, file={'cn': './assets/cn/freebies/MAIL_SELECT_GEMS.png', 'en': './assets/en/freebies/MAIL_SELECT_GEMS.png', 'jp': './assets/jp/freebies/MAIL_SELECT_GEMS.png', 'tw': './assets/tw/freebies/MAIL_SELECT_GEMS.png'}) diff --git a/module/freebies/mail_white.py b/module/freebies/mail_white.py index 24b7c8630..d8c7dc462 100644 --- a/module/freebies/mail_white.py +++ b/module/freebies/mail_white.py @@ -27,6 +27,18 @@ class MailWhite(UI): ) return setting + @cached_property + def mail_select_all_setting(self): + setting = MailSelectSetting('MailAll', main=self) + setting.reset_first = False + setting.add_setting( + setting='all', + option_buttons=[MAIL_SELECT_ALL], + option_names=['all'], + option_default='all' + ) + return setting + def _mail_enter(self, skip_first_screenshot=True): """ Returns: @@ -222,6 +234,7 @@ class MailWhite(UI): if delete: logger.hr('Mail delete', level=2) self._mail_enter() + self.mail_select_all_setting.set(contains=['all']) self._mail_delete() self._mail_quit() diff --git a/module/map/map_operation.py b/module/map/map_operation.py index 305d86217..8f5b22baf 100644 --- a/module/map/map_operation.py +++ b/module/map/map_operation.py @@ -315,7 +315,7 @@ class MapOperation(MysteryHandler, FleetPreparation, Retirement, FastForwardHand return False percent = self.get_map_clear_percentage() - logger.attr('Map_clear_percentage', percent) + logger.attr('Map_clear_percentage', f'{int(percent * 100)}%') # Comment this because percentage starts from 100% and increase from 0% to actual value # 2022.08.21 Still enable this when `percent` was raised from 0. if percent > 0.95 and 0 <= self.map_clear_percentage_prev < 0.95: diff --git a/module/map_detection/grid_predictor.py b/module/map_detection/grid_predictor.py index 93cbeafd7..4e220f4cb 100644 --- a/module/map_detection/grid_predictor.py +++ b/module/map_detection/grid_predictor.py @@ -187,6 +187,21 @@ class GridPredictor: return scale def predict_enemy_genre(self): + if self.config.MAP_SIREN_HAS_BOSS_ICON: + if self.enemy_scale: + return '' + image = self.relative_crop((-0.55, -0.2, 0.45, 0.2), shape=(50, 20)) + image = color_similarity_2d(image, color=(255, 150, 24)) + if image[image > 221].shape[0] > 200: + if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.6): + return 'Siren_Siren' + if self.config.MAP_SIREN_HAS_BOSS_ICON_SMALL: + if self.relative_hsv_count(area=(0.03, -0.15, 0.63, 0.15), h=(32 - 3, 32 + 3), shape=(50, 20)) > 100: + image = self.relative_crop((0.03, -0.15, 0.63, 0.15), shape=(50, 20)) + image = color_similarity_2d(image, color=(255, 150, 33)) + if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.7): + return 'Siren_Siren' + image_dic = {} scaling_dic = self.config.MAP_ENEMY_GENRE_DETECTION_SCALING for name, template in self.template_enemy_genre.items(): @@ -210,6 +225,9 @@ class GridPredictor: return None def predict_boss(self): + if self.enemy_genre == 'Siren_Siren': + return False + image = self.relative_crop((-0.55, -0.2, 0.45, 0.2), shape=(50, 20)) image = color_similarity_2d(image, color=(255, 77, 82)) if TEMPLATE_ENEMY_BOSS.match(image, similarity=0.75): diff --git a/module/minigame/new_year_challenge.py b/module/minigame/new_year_challenge.py index e886689e6..c1397ea4d 100644 --- a/module/minigame/new_year_challenge.py +++ b/module/minigame/new_year_challenge.py @@ -36,6 +36,7 @@ class NewYearChallenge(MinigameRun): return False def choose_game(self, skip_first_screenshot=True): + self.interval_clear(page_game_room.check_button) while 1: if skip_first_screenshot: skip_first_screenshot = False @@ -50,12 +51,13 @@ class NewYearChallenge(MinigameRun): if self.appear_then_click(GOTO_CHOOSE_GAME, offset=(5, 5), interval=3): continue # choose game - if self.appear(NEW_YEAR_CHALLENGE_ENTRANCE, offset=(5, 5), interval=3): + if self.appear(NEW_YEAR_CHALLENGE_ENTRANCE, offset=(5, 500), interval=3): self.device.click(NEW_YEAR_CHALLENGE_ENTRANCE) + self.interval_reset(page_game_room.check_button, interval=3) continue # swipe down - if self.ui_page_appear(page_game_room) and MINIGAME_SCROLL.appear(main=self) \ - and not MINIGAME_SCROLL.at_bottom(main=self): + if self.ui_page_appear(page_game_room, interval=3) and MINIGAME_SCROLL.appear(main=self) \ + and not MINIGAME_SCROLL.set(main=self, position=0.25, distance_check=False): MINIGAME_SCROLL.set_bottom(main=self) continue diff --git a/module/os_handler/action_point.py b/module/os_handler/action_point.py index d39a4b246..0e8645beb 100644 --- a/module/os_handler/action_point.py +++ b/module/os_handler/action_point.py @@ -380,10 +380,18 @@ class ActionPointHandler(UI, MapEventHandler): # Buy action points if self.config.OpsiGeneral_BuyActionPointLimit > 0 and not buy_checked: if self.action_point_buy(preserve=self.config.OpsiGeneral_OilLimit): + self.action_point_safe_get() continue else: buy_checked = True + # Recheck if total ap is less than cost + # If it is, skip using boxes + if self._action_point_total < cost: + logger.info('Not having enough action points') + self.action_point_quit() + raise ActionPointLimit + # Sort action point boxes box = [] for index in [1, 2, 3]: diff --git a/module/research/project.py b/module/research/project.py index 600f9f817..6f8c8c627 100644 --- a/module/research/project.py +++ b/module/research/project.py @@ -548,7 +548,11 @@ class ResearchProject: suffix = 'UL' # TW ocr errors, convert B to D if prefix == 'B' and number in ResearchProject.D_PROJECT_NUMBERS: - prefix = 'D' + # Keep B-397-RF, S7 D-397-MI and S* B-397-RF shares 397 + if number == '397' and suffix == 'RF': + pass + else: + prefix = 'D' # I-483-RF revised to -483-RF -> D-483-RF if prefix == '' and number in ResearchProject.D_PROJECT_NUMBERS: prefix = 'D' diff --git a/module/retire/retirement.py b/module/retire/retirement.py index 16d4d824c..408f0e32d 100644 --- a/module/retire/retirement.py +++ b/module/retire/retirement.py @@ -362,6 +362,7 @@ class Retirement(Enhancement, QuickRetireSettingHandler): if self.appear_then_click(RETIRE_APPEAR_1, offset=(20, 20), interval=3): self.interval_clear(IN_RETIREMENT_CHECK) self.interval_reset([AUTO_SEARCH_MAP_OPTION_OFF, AUTO_SEARCH_MAP_OPTION_ON]) + self.map_cat_attack_timer.reset() return False if self.appear(IN_RETIREMENT_CHECK, offset=(20, 20), interval=10): self._retire_handler(mode='one_click_retire') diff --git a/module/statistics/azurstats.py b/module/statistics/azurstats.py index 551403fc4..45fbfa200 100644 --- a/module/statistics/azurstats.py +++ b/module/statistics/azurstats.py @@ -217,10 +217,10 @@ class AzurStats: save_thread = threading.Thread( target=self._save, args=(image, genre, filename)) save_thread.start() - if upload: - upload_thread = threading.Thread( - target=self._upload, args=(image, genre, filename)) - upload_thread.start() + # if upload: + # upload_thread = threading.Thread( + # target=self._upload, args=(image, genre, filename)) + # upload_thread.start() return True diff --git a/module/template/assets.py b/module/template/assets.py index d41d2d695..919e5ed20 100644 --- a/module/template/assets.py +++ b/module/template/assets.py @@ -44,6 +44,7 @@ TEMPLATE_SIREN_Akashi = Template(file={'cn': './assets/cn/template/TEMPLATE_SIRE TEMPLATE_SIREN_AlbacoreIdol = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_AlbacoreIdol.gif', 'en': './assets/en/template/TEMPLATE_SIREN_AlbacoreIdol.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_AlbacoreIdol.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_AlbacoreIdol.gif'}) TEMPLATE_SIREN_AlfredoOriani = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_AlfredoOriani.gif', 'en': './assets/en/template/TEMPLATE_SIREN_AlfredoOriani.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_AlfredoOriani.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_AlfredoOriani.gif'}) TEMPLATE_SIREN_Algerie = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Algerie.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Algerie.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Algerie.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Algerie.gif'}) +TEMPLATE_SIREN_AmagiMasked = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_AmagiMasked.gif', 'en': './assets/en/template/TEMPLATE_SIREN_AmagiMasked.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_AmagiMasked.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_AmagiMasked.gif'}) TEMPLATE_SIREN_Amazon = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Amazon.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Amazon.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Amazon.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Amazon.gif'}) TEMPLATE_SIREN_Arethusa = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Arethusa.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Arethusa.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Arethusa.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Arethusa.gif'}) TEMPLATE_SIREN_Arizona = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Arizona.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Arizona.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Arizona.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Arizona.gif'}) @@ -232,7 +233,7 @@ TEMPLATE_SIREN_shuguangjixie_shanliu = Template(file={'cn': './assets/cn/templat TEMPLATE_STAGE_BLUE_CLEAR = Template(file={'cn': './assets/cn/template/TEMPLATE_STAGE_BLUE_CLEAR.png', 'en': './assets/en/template/TEMPLATE_STAGE_BLUE_CLEAR.png', 'jp': './assets/jp/template/TEMPLATE_STAGE_BLUE_CLEAR.png', 'tw': './assets/tw/template/TEMPLATE_STAGE_BLUE_CLEAR.png'}) TEMPLATE_STAGE_BLUE_PERCENT = Template(file={'cn': './assets/cn/template/TEMPLATE_STAGE_BLUE_PERCENT.png', 'en': './assets/en/template/TEMPLATE_STAGE_BLUE_PERCENT.png', 'jp': './assets/jp/template/TEMPLATE_STAGE_BLUE_PERCENT.png', 'tw': './assets/tw/template/TEMPLATE_STAGE_BLUE_PERCENT.png'}) TEMPLATE_STAGE_CLEAR = Template(file={'cn': './assets/cn/template/TEMPLATE_STAGE_CLEAR.png', 'en': './assets/en/template/TEMPLATE_STAGE_CLEAR.png', 'jp': './assets/jp/template/TEMPLATE_STAGE_CLEAR.png', 'tw': './assets/tw/template/TEMPLATE_STAGE_CLEAR.png'}) -TEMPLATE_STAGE_CLEAR_20240725 = Template(file={'cn': './assets/cn/template/TEMPLATE_STAGE_CLEAR_20240725.png', 'en': './assets/en/template/TEMPLATE_STAGE_CLEAR_20240725.png', 'jp': './assets/jp/template/TEMPLATE_STAGE_CLEAR_20240725.png', 'tw': './assets/tw/template/TEMPLATE_STAGE_CLEAR_20240725.png'}) +TEMPLATE_STAGE_CLEAR_20240725 = Template(file={'cn': './assets/cn/template/TEMPLATE_STAGE_CLEAR_20240725.gif', 'en': './assets/en/template/TEMPLATE_STAGE_CLEAR_20240725.gif', 'jp': './assets/jp/template/TEMPLATE_STAGE_CLEAR_20240725.gif', 'tw': './assets/tw/template/TEMPLATE_STAGE_CLEAR_20240725.gif'}) TEMPLATE_STAGE_CLEAR_SMALL = Template(file={'cn': './assets/cn/template/TEMPLATE_STAGE_CLEAR_SMALL.png', 'en': './assets/cn/template/TEMPLATE_STAGE_CLEAR_SMALL.png', 'jp': './assets/cn/template/TEMPLATE_STAGE_CLEAR_SMALL.png', 'tw': './assets/cn/template/TEMPLATE_STAGE_CLEAR_SMALL.png'}) TEMPLATE_STAGE_GREEN_CLEAR = Template(file={'cn': './assets/cn/template/TEMPLATE_STAGE_GREEN_CLEAR.png', 'en': './assets/en/template/TEMPLATE_STAGE_GREEN_CLEAR.png', 'jp': './assets/jp/template/TEMPLATE_STAGE_GREEN_CLEAR.png', 'tw': './assets/tw/template/TEMPLATE_STAGE_GREEN_CLEAR.png'}) TEMPLATE_STAGE_HALF_PERCENT = Template(file={'cn': './assets/cn/template/TEMPLATE_STAGE_HALF_PERCENT.png', 'en': './assets/en/template/TEMPLATE_STAGE_HALF_PERCENT.png', 'jp': './assets/jp/template/TEMPLATE_STAGE_HALF_PERCENT.png', 'tw': './assets/tw/template/TEMPLATE_STAGE_HALF_PERCENT.png'}) diff --git a/module/ui/page.py b/module/ui/page.py index 10440a7d7..7aeb728a6 100644 --- a/module/ui/page.py +++ b/module/ui/page.py @@ -1,6 +1,7 @@ import traceback from module.coalition.assets import * +from module.raid.assets import * from module.ui.assets import * from module.ui_white.assets import * @@ -287,22 +288,23 @@ page_mail.link(button=GOTO_MAIN_WHITE, destination=page_main) page_main_white.link(button=MAIL_ENTER_WHITE, destination=page_mail) # RPG event (raid_20240328) -# page_rpg_stage = Page(RPG_GOTO_STORY) -# page_rpg_story = Page(RPG_GOTO_STAGE) -# page_rpg_stage.link(button=RPG_GOTO_STORY, destination=page_rpg_story) -# page_rpg_stage.link(button=RPG_HOME, destination=page_main) -# page_rpg_story.link(button=RPG_GOTO_STAGE, destination=page_rpg_stage) -# page_rpg_story.link(button=RPG_HOME, destination=page_main) -# +page_rpg_stage = Page(RPG_GOTO_STORY) +page_rpg_story = Page(RPG_GOTO_STAGE) +page_rpg_stage.link(button=RPG_GOTO_STORY, destination=page_rpg_story) +page_rpg_stage.link(button=RPG_HOME, destination=page_main) +page_rpg_story.link(button=RPG_GOTO_STAGE, destination=page_rpg_stage) +page_rpg_story.link(button=RPG_HOME, destination=page_main) + +page_campaign_menu.link(button=CAMPAIGN_MENU_GOTO_EVENT, destination=page_rpg_stage) # page_main.link(button=MAIN_GOTO_RAID, destination=page_rpg_stage) -# # page_main_white.link(button=MAIN_GOTO_RAID_WHITE, destination=page_rpg_stage) -# -# page_rpg_city = Page(RPG_LEAVE_CITY) -# page_rpg_city.link(button=RPG_LEAVE_CITY, destination=page_rpg_stage) -# page_rpg_city.link(button=RPG_HOME, destination=page_main) +# page_main_white.link(button=MAIN_GOTO_RAID_WHITE, destination=page_rpg_stage) + +page_rpg_city = Page(RPG_LEAVE_CITY) +page_rpg_city.link(button=RPG_LEAVE_CITY, destination=page_rpg_stage) +page_rpg_city.link(button=RPG_HOME, destination=page_main) # Keep page_rpg_stage, so Raid can import -page_rpg_stage = page_raid +# page_rpg_stage = page_raid # Player page_player = Page(PLAYER_CHECK) diff --git a/module/ui/ui.py b/module/ui/ui.py index ff713b100..6f8034912 100644 --- a/module/ui/ui.py +++ b/module/ui/ui.py @@ -200,9 +200,12 @@ class UI(InfoHandler): if self.appear_then_click(GOTO_MAIN, offset=(30, 30), interval=2): timeout.reset() continue - # if self.appear_then_click(RPG_HOME, offset=(30, 30), interval=2): - # timeout.reset() - # continue + if self.appear_then_click(GOTO_MAIN_WHITE, offset=(30, 30), interval=2): + timeout.reset() + continue + if self.appear_then_click(RPG_HOME, offset=(30, 30), interval=2): + timeout.reset() + continue if self.ui_additional(): timeout.reset() continue @@ -552,7 +555,8 @@ class UI(InfoHandler): # - Game client freezes at page_campaign W12, clicking anywhere on the screen doesn't get responses # - Restart game client again fix the issue logger.info("WITHDRAW button found, wait until map loaded to prevent bugs in game client") - self.device.sleep(3) + self.device.sleep(2) + self.device.screenshot() if self.appear_then_click(WITHDRAW, offset=(30, 30)): self.interval_reset(WITHDRAW) return True @@ -573,8 +577,8 @@ class UI(InfoHandler): return True # RPG event (raid_20240328) - # if self.appear_then_click(RPG_STATUS_POPUP, offset=(30, 30), interval=3): - # return True + if self.appear_then_click(RPG_STATUS_POPUP, offset=(30, 30), interval=3): + return True # Idle page if self.get_interval_timer(IDLE, interval=3).reached(): @@ -611,5 +615,5 @@ class UI(InfoHandler): self.interval_reset(RAID_CHECK) if button == SHOP_GOTO_SUPPLY_PACK: self.interval_reset(EXCHANGE_CHECK) - # if button in [RPG_GOTO_STAGE, RPG_GOTO_STORY, RPG_LEAVE_CITY]: - # self.interval_timer[GET_SHIP.name] = Timer(5).reset() + if button in [RPG_GOTO_STAGE, RPG_GOTO_STORY, RPG_LEAVE_CITY]: + self.interval_timer[GET_SHIP.name] = Timer(5).reset() diff --git a/module/ui_white/assets.py b/module/ui_white/assets.py index f313bbd02..c83714ccb 100644 --- a/module/ui_white/assets.py +++ b/module/ui_white/assets.py @@ -6,7 +6,7 @@ from module.base.template import Template BACK_ARROW_WHITE = Button(area={'cn': (54, 30, 69, 53), 'en': (54, 30, 69, 53), 'jp': (54, 30, 69, 53), 'tw': (54, 30, 69, 53)}, color={'cn': (199, 199, 199), 'en': (199, 199, 199), 'jp': (199, 199, 199), 'tw': (199, 199, 199)}, button={'cn': (54, 30, 69, 53), 'en': (54, 30, 69, 53), 'jp': (54, 30, 69, 53), 'tw': (54, 30, 69, 53)}, file={'cn': './assets/cn/ui_white/BACK_ARROW_WHITE.png', 'en': './assets/cn/ui_white/BACK_ARROW_WHITE.png', 'jp': './assets/cn/ui_white/BACK_ARROW_WHITE.png', 'tw': './assets/cn/ui_white/BACK_ARROW_WHITE.png'}) GOTO_MAIN_WHITE = Button(area={'cn': (1211, 24, 1239, 47), 'en': (1211, 24, 1239, 47), 'jp': (1211, 24, 1239, 47), 'tw': (1211, 24, 1239, 47)}, color={'cn': (151, 151, 152), 'en': (151, 151, 152), 'jp': (151, 151, 152), 'tw': (151, 151, 152)}, button={'cn': (1211, 24, 1239, 47), 'en': (1211, 24, 1239, 47), 'jp': (1211, 24, 1239, 47), 'tw': (1211, 24, 1239, 47)}, file={'cn': './assets/cn/ui_white/GOTO_MAIN_WHITE.png', 'en': './assets/cn/ui_white/GOTO_MAIN_WHITE.png', 'jp': './assets/cn/ui_white/GOTO_MAIN_WHITE.png', 'tw': './assets/cn/ui_white/GOTO_MAIN_WHITE.png'}) -MAIL_CHECK = Button(area={'cn': (141, 21, 181, 41), 'en': (141, 21, 181, 41), 'jp': (141, 21, 181, 41), 'tw': (141, 21, 181, 41)}, color={'cn': (101, 109, 122), 'en': (101, 109, 122), 'jp': (101, 109, 122), 'tw': (101, 109, 122)}, button={'cn': (141, 21, 181, 41), 'en': (141, 21, 181, 41), 'jp': (141, 21, 181, 41), 'tw': (141, 21, 181, 41)}, file={'cn': './assets/cn/ui_white/MAIL_CHECK.png', 'en': './assets/cn/ui_white/MAIL_CHECK.png', 'jp': './assets/cn/ui_white/MAIL_CHECK.png', 'tw': './assets/cn/ui_white/MAIL_CHECK.png'}) +MAIL_CHECK = Button(area={'cn': (141, 21, 181, 41), 'en': (141, 21, 181, 41), 'jp': (139, 22, 206, 43), 'tw': (141, 21, 181, 41)}, color={'cn': (101, 109, 122), 'en': (101, 109, 122), 'jp': (53, 60, 76), 'tw': (101, 109, 122)}, button={'cn': (141, 21, 181, 41), 'en': (141, 21, 181, 41), 'jp': (139, 22, 206, 43), 'tw': (141, 21, 181, 41)}, file={'cn': './assets/cn/ui_white/MAIL_CHECK.png', 'en': './assets/cn/ui_white/MAIL_CHECK.png', 'jp': './assets/jp/ui_white/MAIL_CHECK.png', 'tw': './assets/cn/ui_white/MAIL_CHECK.png'}) MAIL_ENTER_WHITE = Button(area={'cn': (1018, 22, 1087, 52), 'en': (1018, 22, 1085, 52), 'jp': (1020, 23, 1084, 51), 'tw': (1019, 21, 1086, 52)}, color={'cn': (194, 187, 190), 'en': (196, 194, 194), 'jp': (195, 187, 190), 'tw': (204, 196, 200)}, button={'cn': (1018, 22, 1087, 52), 'en': (1018, 22, 1085, 52), 'jp': (1020, 23, 1084, 51), 'tw': (1019, 21, 1086, 52)}, file={'cn': './assets/cn/ui_white/MAIL_ENTER_WHITE.png', 'en': './assets/en/ui_white/MAIL_ENTER_WHITE.png', 'jp': './assets/jp/ui_white/MAIL_ENTER_WHITE.png', 'tw': './assets/tw/ui_white/MAIL_ENTER_WHITE.png'}) MAIN_GOTO_BUILD_WHITE = Button(area={'cn': (959, 657, 1103, 703), 'en': (959, 657, 1104, 703), 'jp': (959, 657, 1105, 703), 'tw': (959, 657, 1105, 703)}, color={'cn': (229, 216, 216), 'en': (226, 220, 222), 'jp': (225, 216, 218), 'tw': (223, 212, 218)}, button={'cn': (959, 657, 1103, 703), 'en': (959, 657, 1104, 703), 'jp': (959, 657, 1105, 703), 'tw': (959, 657, 1105, 703)}, file={'cn': './assets/cn/ui_white/MAIN_GOTO_BUILD_WHITE.png', 'en': './assets/en/ui_white/MAIN_GOTO_BUILD_WHITE.png', 'jp': './assets/jp/ui_white/MAIN_GOTO_BUILD_WHITE.png', 'tw': './assets/tw/ui_white/MAIN_GOTO_BUILD_WHITE.png'}) MAIN_GOTO_CAMPAIGN_WHITE = Button(area={'cn': (1132, 529, 1187, 557), 'en': (1126, 549, 1219, 577), 'jp': (1133, 528, 1186, 554), 'tw': (1133, 527, 1188, 554)}, color={'cn': (152, 161, 161), 'en': (156, 164, 165), 'jp': (156, 165, 165), 'tw': (143, 152, 151)}, button={'cn': (1124, 437, 1260, 579), 'en': (1123, 439, 1260, 580), 'jp': (1123, 438, 1260, 580), 'tw': (1124, 437, 1260, 580)}, file={'cn': './assets/cn/ui_white/MAIN_GOTO_CAMPAIGN_WHITE.png', 'en': './assets/en/ui_white/MAIN_GOTO_CAMPAIGN_WHITE.png', 'jp': './assets/jp/ui_white/MAIN_GOTO_CAMPAIGN_WHITE.png', 'tw': './assets/tw/ui_white/MAIN_GOTO_CAMPAIGN_WHITE.png'})