diff --git a/assets/cn/combat_ui/PAUSE_HolyLight.png b/assets/cn/combat_ui/PAUSE_HolyLight.png new file mode 100644 index 000000000..d286ebd1e Binary files /dev/null and b/assets/cn/combat_ui/PAUSE_HolyLight.png differ diff --git a/assets/cn/combat_ui/PAUSE_Pharaoh.png b/assets/cn/combat_ui/PAUSE_Pharaoh.png new file mode 100644 index 000000000..91faee781 Binary files /dev/null and b/assets/cn/combat_ui/PAUSE_Pharaoh.png differ diff --git a/assets/cn/combat_ui/QUIT_Pharaoh.png b/assets/cn/combat_ui/QUIT_Pharaoh.png new file mode 100644 index 000000000..d5e99db4c Binary files /dev/null and b/assets/cn/combat_ui/QUIT_Pharaoh.png differ diff --git a/assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png b/assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png index 9876394b4..b5175cfe1 100644 Binary files a/assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png and b/assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png differ diff --git a/assets/cn/meta_reward/SYNC_ENTER.png b/assets/cn/meta_reward/SYNC_ENTER.png new file mode 100644 index 000000000..c200b54af Binary files /dev/null and b/assets/cn/meta_reward/SYNC_ENTER.png differ diff --git a/assets/cn/meta_reward/SYNC_REWARD_NOTICE.png b/assets/cn/meta_reward/SYNC_REWARD_NOTICE.png new file mode 100644 index 000000000..cb2239e38 Binary files /dev/null and b/assets/cn/meta_reward/SYNC_REWARD_NOTICE.png differ diff --git a/assets/cn/meta_reward/SYNC_TAP.png b/assets/cn/meta_reward/SYNC_TAP.png new file mode 100644 index 000000000..448a82a03 Binary files /dev/null and b/assets/cn/meta_reward/SYNC_TAP.png differ diff --git a/assets/cn/template/TEMPLATE_SIREN_BolzanoAlter.gif b/assets/cn/template/TEMPLATE_SIREN_BolzanoAlter.gif new file mode 100644 index 000000000..240a5aa70 Binary files /dev/null and b/assets/cn/template/TEMPLATE_SIREN_BolzanoAlter.gif differ diff --git a/assets/cn/template/TEMPLATE_SIREN_CesareAlter.gif b/assets/cn/template/TEMPLATE_SIREN_CesareAlter.gif new file mode 100644 index 000000000..e13ef5bb0 Binary files /dev/null and b/assets/cn/template/TEMPLATE_SIREN_CesareAlter.gif differ diff --git a/assets/cn/template/TEMPLATE_SIREN_SirenBoss25.gif b/assets/cn/template/TEMPLATE_SIREN_SirenBoss25.gif new file mode 100644 index 000000000..6ff8223cf Binary files /dev/null and b/assets/cn/template/TEMPLATE_SIREN_SirenBoss25.gif differ diff --git a/assets/cn/template/TEMPLATE_SIREN_SirenBoss26.gif b/assets/cn/template/TEMPLATE_SIREN_SirenBoss26.gif new file mode 100644 index 000000000..bbb95b27a Binary files /dev/null and b/assets/cn/template/TEMPLATE_SIREN_SirenBoss26.gif differ diff --git a/assets/cn/template/TEMPLATE_SIREN_TrentoAlter.gif b/assets/cn/template/TEMPLATE_SIREN_TrentoAlter.gif new file mode 100644 index 000000000..d789a3db1 Binary files /dev/null and b/assets/cn/template/TEMPLATE_SIREN_TrentoAlter.gif differ diff --git a/assets/cn/ui/CHANNEL_CHECK.png b/assets/cn/ui/CHANNEL_CHECK.png new file mode 100644 index 000000000..ec0d77d63 Binary files /dev/null and b/assets/cn/ui/CHANNEL_CHECK.png differ diff --git a/assets/en/meta_reward/SYNC_ENTER.png b/assets/en/meta_reward/SYNC_ENTER.png new file mode 100644 index 000000000..06440dd0c Binary files /dev/null and b/assets/en/meta_reward/SYNC_ENTER.png differ diff --git a/assets/en/meta_reward/SYNC_TAP.png b/assets/en/meta_reward/SYNC_TAP.png new file mode 100644 index 000000000..dd6226b6a Binary files /dev/null and b/assets/en/meta_reward/SYNC_TAP.png differ diff --git a/assets/en/template/TEMPLATE_SIREN_BolzanoAlter.gif b/assets/en/template/TEMPLATE_SIREN_BolzanoAlter.gif new file mode 100644 index 000000000..240a5aa70 Binary files /dev/null and b/assets/en/template/TEMPLATE_SIREN_BolzanoAlter.gif differ diff --git a/assets/en/template/TEMPLATE_SIREN_CesareAlter.gif b/assets/en/template/TEMPLATE_SIREN_CesareAlter.gif new file mode 100644 index 000000000..e13ef5bb0 Binary files /dev/null and b/assets/en/template/TEMPLATE_SIREN_CesareAlter.gif differ diff --git a/assets/en/template/TEMPLATE_SIREN_SirenBoss25.gif b/assets/en/template/TEMPLATE_SIREN_SirenBoss25.gif new file mode 100644 index 000000000..6ff8223cf Binary files /dev/null and b/assets/en/template/TEMPLATE_SIREN_SirenBoss25.gif differ diff --git a/assets/en/template/TEMPLATE_SIREN_SirenBoss26.gif b/assets/en/template/TEMPLATE_SIREN_SirenBoss26.gif new file mode 100644 index 000000000..bbb95b27a Binary files /dev/null and b/assets/en/template/TEMPLATE_SIREN_SirenBoss26.gif differ diff --git a/assets/en/template/TEMPLATE_SIREN_TrentoAlter.gif b/assets/en/template/TEMPLATE_SIREN_TrentoAlter.gif new file mode 100644 index 000000000..d789a3db1 Binary files /dev/null and b/assets/en/template/TEMPLATE_SIREN_TrentoAlter.gif differ diff --git a/assets/jp/meta_reward/SYNC_ENTER.png b/assets/jp/meta_reward/SYNC_ENTER.png new file mode 100644 index 000000000..d555105e7 Binary files /dev/null and b/assets/jp/meta_reward/SYNC_ENTER.png differ diff --git a/assets/jp/meta_reward/SYNC_TAP.png b/assets/jp/meta_reward/SYNC_TAP.png new file mode 100644 index 000000000..b96f3cf2f Binary files /dev/null and b/assets/jp/meta_reward/SYNC_TAP.png differ diff --git a/assets/jp/os_ash/ASH_DAILY_STATUS.png b/assets/jp/os_ash/ASH_DAILY_STATUS.png index 5772d9e18..c19ff3240 100644 Binary files a/assets/jp/os_ash/ASH_DAILY_STATUS.png and b/assets/jp/os_ash/ASH_DAILY_STATUS.png differ diff --git a/assets/jp/template/TEMPLATE_SIREN_BolzanoAlter.gif b/assets/jp/template/TEMPLATE_SIREN_BolzanoAlter.gif new file mode 100644 index 000000000..240a5aa70 Binary files /dev/null and b/assets/jp/template/TEMPLATE_SIREN_BolzanoAlter.gif differ diff --git a/assets/jp/template/TEMPLATE_SIREN_CesareAlter.gif b/assets/jp/template/TEMPLATE_SIREN_CesareAlter.gif new file mode 100644 index 000000000..e13ef5bb0 Binary files /dev/null and b/assets/jp/template/TEMPLATE_SIREN_CesareAlter.gif differ diff --git a/assets/jp/template/TEMPLATE_SIREN_SirenBoss25.gif b/assets/jp/template/TEMPLATE_SIREN_SirenBoss25.gif new file mode 100644 index 000000000..6ff8223cf Binary files /dev/null and b/assets/jp/template/TEMPLATE_SIREN_SirenBoss25.gif differ diff --git a/assets/jp/template/TEMPLATE_SIREN_SirenBoss26.gif b/assets/jp/template/TEMPLATE_SIREN_SirenBoss26.gif new file mode 100644 index 000000000..bbb95b27a Binary files /dev/null and b/assets/jp/template/TEMPLATE_SIREN_SirenBoss26.gif differ diff --git a/assets/jp/template/TEMPLATE_SIREN_TrentoAlter.gif b/assets/jp/template/TEMPLATE_SIREN_TrentoAlter.gif new file mode 100644 index 000000000..d789a3db1 Binary files /dev/null and b/assets/jp/template/TEMPLATE_SIREN_TrentoAlter.gif differ diff --git a/assets/tw/guild/OCR_GUILD_OPERATIONS_PROGRESS.png b/assets/tw/guild/OCR_GUILD_OPERATIONS_PROGRESS.png deleted file mode 100644 index 9876394b4..000000000 Binary files a/assets/tw/guild/OCR_GUILD_OPERATIONS_PROGRESS.png and /dev/null differ diff --git a/assets/jp/guild/OCR_GUILD_OPERATIONS_PROGRESS.png b/assets/tw/raid/CHIENWU_RAID_EASY.png similarity index 50% rename from assets/jp/guild/OCR_GUILD_OPERATIONS_PROGRESS.png rename to assets/tw/raid/CHIENWU_RAID_EASY.png index 9876394b4..e66eda643 100644 Binary files a/assets/jp/guild/OCR_GUILD_OPERATIONS_PROGRESS.png and b/assets/tw/raid/CHIENWU_RAID_EASY.png differ diff --git a/assets/tw/raid/CHIENWU_RAID_HARD.png b/assets/tw/raid/CHIENWU_RAID_HARD.png new file mode 100644 index 000000000..825777b59 Binary files /dev/null and b/assets/tw/raid/CHIENWU_RAID_HARD.png differ diff --git a/assets/en/guild/OCR_GUILD_OPERATIONS_PROGRESS.png b/assets/tw/raid/CHIENWU_RAID_NORMAL.png similarity index 51% rename from assets/en/guild/OCR_GUILD_OPERATIONS_PROGRESS.png rename to assets/tw/raid/CHIENWU_RAID_NORMAL.png index 9876394b4..12645af20 100644 Binary files a/assets/en/guild/OCR_GUILD_OPERATIONS_PROGRESS.png and b/assets/tw/raid/CHIENWU_RAID_NORMAL.png differ diff --git a/assets/tw/template/TEMPLATE_SIREN_BolzanoAlter.gif b/assets/tw/template/TEMPLATE_SIREN_BolzanoAlter.gif new file mode 100644 index 000000000..240a5aa70 Binary files /dev/null and b/assets/tw/template/TEMPLATE_SIREN_BolzanoAlter.gif differ diff --git a/assets/tw/template/TEMPLATE_SIREN_CesareAlter.gif b/assets/tw/template/TEMPLATE_SIREN_CesareAlter.gif new file mode 100644 index 000000000..e13ef5bb0 Binary files /dev/null and b/assets/tw/template/TEMPLATE_SIREN_CesareAlter.gif differ diff --git a/assets/tw/template/TEMPLATE_SIREN_SirenBoss25.gif b/assets/tw/template/TEMPLATE_SIREN_SirenBoss25.gif new file mode 100644 index 000000000..6ff8223cf Binary files /dev/null and b/assets/tw/template/TEMPLATE_SIREN_SirenBoss25.gif differ diff --git a/assets/tw/template/TEMPLATE_SIREN_SirenBoss26.gif b/assets/tw/template/TEMPLATE_SIREN_SirenBoss26.gif new file mode 100644 index 000000000..bbb95b27a Binary files /dev/null and b/assets/tw/template/TEMPLATE_SIREN_SirenBoss26.gif differ diff --git a/assets/tw/template/TEMPLATE_SIREN_TrentoAlter.gif b/assets/tw/template/TEMPLATE_SIREN_TrentoAlter.gif new file mode 100644 index 000000000..d789a3db1 Binary files /dev/null and b/assets/tw/template/TEMPLATE_SIREN_TrentoAlter.gif differ diff --git a/campaign/Readme.md b/campaign/Readme.md index 0c35d4482..c14e233eb 100644 --- a/campaign/Readme.md +++ b/campaign/Readme.md @@ -229,3 +229,8 @@ To add a new event, add a new row in here, and run `python -m module.config.conf | 20250102 | raid 20240130 | Spring Festive Fiasco | - | - | - | 寰昌宇定家事忙 | | 20250109 | event 20221222 cn | Parallel Superimposition Rerun | 复刻定向折叠 | Parallel Superimposition Rerun | 積重なる事象の幻界(復刻) | - | | 20250116 | raid 20250116 | Spring Fashion Festa | 华裳巧展喜事长 | Spring Fashion Festa | 新春華裳協奏曲 | - | +| 20250123 | raid 20250116 | Spring Fashion Festa | - | - | - | 華裳巧展喜事長 | +| 20250206 | event 20220818 cn | Operation Convergence Rerun | 复刻远汇点作战 | Operation Convergence Return | 結像点作戦(復刻) | - | +| 20250213 | event 20240815 cn | Windborne Steel Wings | - | - | - | 鐵翼擎風 | +| 20250227 | event 20250227 cn | Paradiso of Shackled Light | 樊笼内的神光 | Paradiso of Shackled Light | 籠檻に囚われし神光 | - | +| 20250227 | event 20240725 cn | Interlude of Illusions | - | - | - | 幻夢間奏曲 | diff --git a/campaign/campaign_main/campaign_2_1.py b/campaign/campaign_main/campaign_2_1.py index ab99751c4..aebced245 100644 --- a/campaign/campaign_main/campaign_2_1.py +++ b/campaign/campaign_main/campaign_2_1.py @@ -1,4 +1,4 @@ -from module.campaign.campaign_base import CampaignBase +from .campaign_2_base import CampaignBase from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids diff --git a/campaign/campaign_main/campaign_2_2.py b/campaign/campaign_main/campaign_2_2.py index d005d83e6..b29a40fc0 100644 --- a/campaign/campaign_main/campaign_2_2.py +++ b/campaign/campaign_main/campaign_2_2.py @@ -1,5 +1,5 @@ from campaign.campaign_main.campaign_2_1 import Config -from module.campaign.campaign_base import CampaignBase +from .campaign_2_base import CampaignBase from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids diff --git a/campaign/campaign_main/campaign_2_3.py b/campaign/campaign_main/campaign_2_3.py index 41b9291d6..2633b88be 100644 --- a/campaign/campaign_main/campaign_2_3.py +++ b/campaign/campaign_main/campaign_2_3.py @@ -1,5 +1,5 @@ from campaign.campaign_main.campaign_2_1 import Config -from module.campaign.campaign_base import CampaignBase +from .campaign_2_base import CampaignBase from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids diff --git a/campaign/campaign_main/campaign_2_4.py b/campaign/campaign_main/campaign_2_4.py index 3a18d4e2e..3d374e7ec 100644 --- a/campaign/campaign_main/campaign_2_4.py +++ b/campaign/campaign_main/campaign_2_4.py @@ -1,5 +1,5 @@ from campaign.campaign_main.campaign_2_1 import Config as ConfigBase -from module.campaign.campaign_base import CampaignBase +from .campaign_2_base import CampaignBase from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids diff --git a/campaign/campaign_main/campaign_2_base.py b/campaign/campaign_main/campaign_2_base.py new file mode 100644 index 000000000..5f1565154 --- /dev/null +++ b/campaign/campaign_main/campaign_2_base.py @@ -0,0 +1,10 @@ +from module.campaign.campaign_base import CampaignBase as CampaignBase_ +from module.ui.page import page_campaign + + +class CampaignBase(CampaignBase_): + def handle_exp_info(self): + # Random background of Main Chapter 2 hits EXP_INFO_B + if self.ui_page_appear(page_campaign): + return False + return super().handle_exp_info() diff --git a/campaign/campaign_main/campaign_3_1.py b/campaign/campaign_main/campaign_3_1.py index 29b778c09..969f356e7 100644 --- a/campaign/campaign_main/campaign_3_1.py +++ b/campaign/campaign_main/campaign_3_1.py @@ -1,4 +1,4 @@ -from module.campaign.campaign_base import CampaignBase +from .campaign_3_base import CampaignBase from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids diff --git a/campaign/campaign_main/campaign_3_2.py b/campaign/campaign_main/campaign_3_2.py index 70fbd7d56..2bae2e3a6 100644 --- a/campaign/campaign_main/campaign_3_2.py +++ b/campaign/campaign_main/campaign_3_2.py @@ -1,5 +1,5 @@ from campaign.campaign_main.campaign_3_1 import Config -from module.campaign.campaign_base import CampaignBase +from .campaign_3_base import CampaignBase from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids diff --git a/campaign/campaign_main/campaign_3_3.py b/campaign/campaign_main/campaign_3_3.py index b4b7c3b30..289bb8cc6 100644 --- a/campaign/campaign_main/campaign_3_3.py +++ b/campaign/campaign_main/campaign_3_3.py @@ -1,5 +1,5 @@ from campaign.campaign_main.campaign_3_1 import Config as ConfigBase -from module.campaign.campaign_base import CampaignBase +from .campaign_3_base import CampaignBase from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids diff --git a/campaign/campaign_main/campaign_3_4.py b/campaign/campaign_main/campaign_3_4.py index 5eb826e21..fa4fd406f 100644 --- a/campaign/campaign_main/campaign_3_4.py +++ b/campaign/campaign_main/campaign_3_4.py @@ -1,5 +1,5 @@ from campaign.campaign_main.campaign_3_1 import Config as Config31 -from module.campaign.campaign_base import CampaignBase +from .campaign_3_base import CampaignBase from module.logger import logger from module.map.map_base import CampaignMap from module.map.map_grids import RoadGrids, SelectedGrids diff --git a/campaign/campaign_main/campaign_3_base.py b/campaign/campaign_main/campaign_3_base.py new file mode 100644 index 000000000..1567e3c6b --- /dev/null +++ b/campaign/campaign_main/campaign_3_base.py @@ -0,0 +1,11 @@ +from module.campaign.campaign_base import CampaignBase as CampaignBase_ +from module.logger import logger +from module.ui.page import page_campaign + + +class CampaignBase(CampaignBase_): + def handle_exp_info(self): + # Random background of Main Chapter 3 hits EXP_INFO_B + if self.ui_page_appear(page_campaign): + return False + return super().handle_exp_info() diff --git a/campaign/event_20220224_cn/campaign_base.py b/campaign/event_20220224_cn/campaign_base.py index f96d2f7bf..c874590eb 100644 --- a/campaign/event_20220224_cn/campaign_base.py +++ b/campaign/event_20220224_cn/campaign_base.py @@ -1,4 +1,5 @@ from module.campaign.campaign_base import CampaignBase as CampaignBase_ +from module.ui.page import page_event class CampaignBase(CampaignBase_): @@ -6,3 +7,9 @@ class CampaignBase(CampaignBase_): if super().handle_clear_mode_config_cover(): self.config.MAP_SIREN_TEMPLATE = ['SS'] self.config.MAP_HAS_SIREN = True + + def handle_exp_info(self): + # Random background hits EXP_INFO_B + if self.ui_page_appear(page_event): + return False + return super().handle_exp_info() diff --git a/campaign/event_20240815_cn/campaign_base.py b/campaign/event_20240815_cn/campaign_base.py index db32d830b..415e17f00 100644 --- a/campaign/event_20240815_cn/campaign_base.py +++ b/campaign/event_20240815_cn/campaign_base.py @@ -4,6 +4,7 @@ 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 +from module.ui.page import page_event class CampaignBase(CampaignBase_): @@ -82,3 +83,9 @@ class CampaignBase(CampaignBase_): self.ensure_no_stage_entrance() return True return super().handle_campaign_ui_additional() + + def handle_exp_info(self): + # Random background hits EXP_INFO_B + if self.ui_page_appear(page_event): + return False + return super().handle_exp_info() diff --git a/campaign/event_20250227_cn/a1.py b/campaign/event_20250227_cn/a1.py new file mode 100644 index 000000000..60aba3579 --- /dev/null +++ b/campaign/event_20250227_cn/a1.py @@ -0,0 +1,96 @@ +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 + +MAP = CampaignMap('A1') +MAP.shape = 'I8' +MAP.camera_data = ['D2', 'D6', 'F2', 'F6'] +MAP.camera_data_spawn_point = ['F2'] +MAP.map_data = """ + ++ -- ME -- -- SP SP ++ MB + -- ME -- ME -- -- -- -- -- + ME -- ++ ++ Me -- -- __ Me + -- -- ++ ++ -- MS MS -- -- + -- 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 + 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 = False + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_CHAPTER_SWITCH_20241219 = True + MAP_HAS_MODE_SWITCH = True + STAGE_ENTRANCE = ['half', '20240725'] + STAGE_INCREASE_AB = True + 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.227, 1.250) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.187, 1.209) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.152, 1.173) + + +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_20250227_cn/a2.py b/campaign/event_20250227_cn/a2.py new file mode 100644 index 000000000..b298d1fb7 --- /dev/null +++ b/campaign/event_20250227_cn/a2.py @@ -0,0 +1,79 @@ +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 +from .a1 import Config as ConfigBase + +MAP = CampaignMap('A2') +MAP.shape = 'I8' +MAP.camera_data = ['D2', 'D6', 'F2', 'F6'] +MAP.camera_data_spawn_point = ['F2', 'D2'] +MAP.map_data = """ + -- -- ME -- -- ME -- ++ -- + ME ++ -- ME -- -- -- -- ME + -- -- ME ++ ++ ++ -- Me -- + -- ME -- SP -- SP -- -- -- + -- -- -- -- -- -- -- -- ME + -- ME -- -- __ -- -- Me -- + ME ++ -- MS -- MS -- ++ ++ + -- ++ Me -- MB -- 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 = False + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = (1.128, 1.149) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.091, 1.111) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.059, 1.078) + + +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_20250227_cn/a3.py b/campaign/event_20250227_cn/a3.py new file mode 100644 index 000000000..494c376df --- /dev/null +++ b/campaign/event_20250227_cn/a3.py @@ -0,0 +1,79 @@ +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 +from .a1 import Config as ConfigBase + +MAP = CampaignMap('A3') +MAP.shape = 'J8' +MAP.camera_data = ['D3', 'D6', 'G3', 'G6'] +MAP.camera_data_spawn_point = ['D2'] +MAP.map_data = """ + -- SP -- -- -- ++ -- ++ ++ -- + SP -- -- MS -- -- Me ++ ++ -- + ++ ++ ++ -- MS -- -- -- ME -- + -- -- -- __ -- -- Me -- -- ME + -- ME -- -- Me ++ Me -- ME ++ + ME -- MB -- -- ++ 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 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, \ +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 = False + 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_20250227_cn/b1.py b/campaign/event_20250227_cn/b1.py new file mode 100644 index 000000000..7602ea1b1 --- /dev/null +++ b/campaign/event_20250227_cn/b1.py @@ -0,0 +1,96 @@ +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 + +MAP = CampaignMap('B1') +MAP.shape = 'K8' +MAP.camera_data = ['D2', 'D6', 'H2', 'H6'] +MAP.camera_data_spawn_point = ['D6'] +MAP.map_data = """ + ++ ME -- -- -- ME -- ++ -- ME -- + ME -- ++ -- ++ -- ME ++ ME -- ME + -- -- -- MB -- -- -- -- -- -- -- + -- Me ++ -- ++ Me -- ++ ++ ++ -- + -- -- MS __ MS -- -- -- ME -- -- + Me -- -- MS -- -- Me -- -- ME -- + ++ ++ -- -- -- ++ ++ Me ME -- ++ + ++ ++ 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 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, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['TrentoAlter', 'BolzanoAlter', 'CesareAlter'] + 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 + # ===== End of generated config ===== + + MAP_CHAPTER_SWITCH_20241219 = True + MAP_HAS_MODE_SWITCH = True + STAGE_ENTRANCE = ['half', '20240725'] + STAGE_INCREASE_AB = 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.131, 1.152) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.094, 1.114) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.062, 1.081) + + +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_20250227_cn/b2.py b/campaign/event_20250227_cn/b2.py new file mode 100644 index 000000000..374091673 --- /dev/null +++ b/campaign/event_20250227_cn/b2.py @@ -0,0 +1,80 @@ +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 +from .b1 import Config as ConfigBase + +MAP = CampaignMap('B2') +MAP.shape = 'K8' +MAP.camera_data = ['E2', 'E6', 'G2', 'G6'] +MAP.camera_data_spawn_point = ['G2', 'E2'] +MAP.map_data = """ + -- -- ++ ++ SP -- SP ++ ++ -- -- + -- -- ++ ++ -- -- -- ++ ++ -- -- + -- -- Me -- -- MS -- -- Me -- -- + ++ ME -- -- MS -- MS -- -- ME ++ + ++ -- -- ME -- MB -- 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 50 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, 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): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['SirenBoss26'] + 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 + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = (1.258, 1.282) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.217, 1.239) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.182, 1.203) + + +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_20250227_cn/b3.py b/campaign/event_20250227_cn/b3.py new file mode 100644 index 000000000..33e212116 --- /dev/null +++ b/campaign/event_20250227_cn/b3.py @@ -0,0 +1,85 @@ +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 +from .b1 import Config as ConfigBase + +MAP = CampaignMap('B3') +MAP.shape = 'K9' +MAP.camera_data = ['E3', 'E6', 'G3', 'G6'] +MAP.camera_data_spawn_point = ['E6', 'G6'] +MAP.map_data = """ + -- ++ ++ -- -- -- -- -- ++ ++ -- + -- ++ ++ -- -- MB -- -- ++ ++ -- + -- -- ++ ++ ++ -- ++ ++ ++ -- -- + -- ++ -- Me -- __ -- Me -- ++ -- + -- ME Me -- MS -- MS -- Me ME -- + -- ME -- -- -- MS -- -- -- 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 10 10 10 50 50 50 50 + 50 50 50 50 10 10 10 50 50 50 50 + 50 50 50 50 10 10 10 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 = ['SirenBoss26', 'SirenBoss25'] + 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 + # ===== End of generated config ===== + + MAP_ENSURE_EDGE_INSIGHT_CORNER = 'bottom' + MAP_WALK_USE_CURRENT_FLEET = 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 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_20250227_cn/c1.py b/campaign/event_20250227_cn/c1.py new file mode 100644 index 000000000..089505dfe --- /dev/null +++ b/campaign/event_20250227_cn/c1.py @@ -0,0 +1,96 @@ +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 + +MAP = CampaignMap('C1') +MAP.shape = 'I8' +MAP.camera_data = ['D2', 'D6', 'F2', 'F6'] +MAP.camera_data_spawn_point = ['F2'] +MAP.map_data = """ + ++ -- ME -- -- SP SP ++ MB + -- ME -- ME -- -- -- -- -- + ME -- ++ ++ Me -- -- __ Me + -- -- ++ ++ -- MS MS -- -- + -- 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 + 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 = False + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_CHAPTER_SWITCH_20241219 = True + MAP_HAS_MODE_SWITCH = True + STAGE_ENTRANCE = ['half', '20240725'] + STAGE_INCREASE_AB = True + 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.227, 1.250) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.187, 1.209) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.152, 1.173) + + +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_20250227_cn/c2.py b/campaign/event_20250227_cn/c2.py new file mode 100644 index 000000000..7c6304e46 --- /dev/null +++ b/campaign/event_20250227_cn/c2.py @@ -0,0 +1,79 @@ +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 +from .c1 import Config as ConfigBase + +MAP = CampaignMap('C2') +MAP.shape = 'I8' +MAP.camera_data = ['D2', 'D6', 'F2', 'F6'] +MAP.camera_data_spawn_point = ['F2', 'D2'] +MAP.map_data = """ + -- -- ME -- -- ME -- ++ -- + ME ++ -- ME -- -- -- -- ME + -- -- ME ++ ++ ++ -- Me -- + -- ME -- SP -- SP -- -- -- + -- -- -- -- -- -- -- -- ME + -- ME -- -- __ -- -- Me -- + ME ++ -- MS -- MS -- ++ ++ + -- ++ Me -- MB -- 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 = False + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = (1.128, 1.149) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.091, 1.111) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.059, 1.078) + + +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_20250227_cn/c3.py b/campaign/event_20250227_cn/c3.py new file mode 100644 index 000000000..c00feb34f --- /dev/null +++ b/campaign/event_20250227_cn/c3.py @@ -0,0 +1,80 @@ +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 +from .c1 import Config as ConfigBase + +MAP = CampaignMap('C3') +MAP.shape = 'J8' +MAP.camera_data = ['D2', 'D6', 'G2', 'G6'] +MAP.camera_data_spawn_point = ['D2'] +MAP.map_data = """ + -- SP -- -- -- ++ -- ++ ++ -- + SP -- -- MS -- -- Me ++ ++ -- + ++ ++ ++ -- MS -- -- -- ME -- + -- -- -- __ -- -- Me -- -- ME + -- ME -- -- Me ++ Me -- ME ++ + ME -- MB -- -- ++ 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 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, \ +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 = False + 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_20250227_cn/d1.py b/campaign/event_20250227_cn/d1.py new file mode 100644 index 000000000..e5b0cbeb7 --- /dev/null +++ b/campaign/event_20250227_cn/d1.py @@ -0,0 +1,96 @@ +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 + +MAP = CampaignMap('D1') +MAP.shape = 'K8' +MAP.camera_data = ['D2', 'D6', 'H2', 'H6'] +MAP.camera_data_spawn_point = ['D6'] +MAP.map_data = """ + ++ ME -- -- -- ME -- ++ -- ME -- + ME -- ++ -- ++ -- ME ++ ME -- ME + -- -- -- MB -- -- -- -- -- -- -- + -- Me ++ -- ++ Me -- ++ ++ ++ -- + -- -- MS __ MS -- -- -- ME -- -- + Me -- -- MS -- -- Me -- -- ME -- + ++ ++ -- -- -- ++ ++ Me ME -- ++ + ++ ++ 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 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, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['TrentoAlter', 'BolzanoAlter', 'CesareAlter'] + 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 + # ===== End of generated config ===== + + MAP_CHAPTER_SWITCH_20241219 = True + MAP_HAS_MODE_SWITCH = True + STAGE_ENTRANCE = ['half', '20240725'] + STAGE_INCREASE_AB = 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.131, 1.152) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.094, 1.114) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.062, 1.081) + + +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_20250227_cn/d2.py b/campaign/event_20250227_cn/d2.py new file mode 100644 index 000000000..5f495dc4a --- /dev/null +++ b/campaign/event_20250227_cn/d2.py @@ -0,0 +1,89 @@ +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 +from .d1 import Config as ConfigBase + +MAP = CampaignMap('D2') +MAP.shape = 'K8' +MAP.camera_data = ['E2', 'E6', 'G2', 'G6'] +MAP.camera_data_spawn_point = ['G2', 'E2'] +MAP.map_data = """ + -- -- ++ ++ SP -- SP ++ ++ -- -- + -- -- ++ ++ -- -- -- ++ ++ -- -- + -- -- Me -- -- MS -- -- Me -- -- + ++ ME -- -- MS -- MS -- -- ME ++ + ++ -- -- ME -- MB -- 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 50 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, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['SirenBoss26'] + 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 + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = (1.258, 1.282) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.217, 1.239) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.182, 1.203) + + +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_20250227_cn/d3.py b/campaign/event_20250227_cn/d3.py new file mode 100644 index 000000000..6d1d33f7c --- /dev/null +++ b/campaign/event_20250227_cn/d3.py @@ -0,0 +1,94 @@ +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 +from .d1 import Config as ConfigBase + +MAP = CampaignMap('D3') +MAP.shape = 'K9' +MAP.camera_data = ['E3', 'E6', 'G3', 'G6'] +MAP.camera_data_spawn_point = ['E6', 'G6'] +MAP.map_data = """ + -- ++ ++ -- -- -- -- -- ++ ++ -- + -- ++ ++ -- -- MB -- -- ++ ++ -- + -- -- ++ ++ ++ -- ++ ++ ++ -- -- + -- ++ -- Me -- __ -- Me -- ++ -- + -- ME Me -- MS -- MS -- Me ME -- + -- ME -- -- -- MS -- -- -- 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 10 10 10 50 50 50 50 + 50 50 50 50 10 10 10 50 50 50 50 + 50 50 50 50 10 10 10 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 = ['SirenBoss26', 'SirenBoss25'] + 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 + # ===== End of generated config ===== + + MAP_ENSURE_EDGE_INSIGHT_CORNER = 'bottom' + MAP_WALK_USE_CURRENT_FLEET = 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 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_20250227_cn/sp.py b/campaign/event_20250227_cn/sp.py new file mode 100644 index 000000000..2b429ca4e --- /dev/null +++ b/campaign/event_20250227_cn/sp.py @@ -0,0 +1,141 @@ +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 + +MAP = CampaignMap('SP') +MAP.shape = 'I9' +MAP.camera_data = ['E3', 'E5', 'E7'] +MAP.camera_data_spawn_point = ['E7'] +MAP.map_data = """ + -- ++ ++ -- -- -- ++ ++ -- + -- ++ ++ -- MB -- ++ ++ -- + -- ++ ++ ++ -- ++ ++ ++ -- + -- -- ME -- -- -- ME -- -- + ++ ME -- -- -- -- -- ME ++ + ++ -- ME -- __ -- ME -- ++ + -- ME -- -- -- -- -- ME -- + -- ++ -- -- -- -- -- ++ -- + -- -- ++ 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': 8}, + {'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_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 ===== + + MAP_CHAPTER_SWITCH_20241219 = True + # MAP_HAS_MODE_SWITCH = True + STAGE_ENTRANCE = ['half', '20240725'] + STAGE_INCREASE_AB = True + MAP_IS_ONE_TIME_STAGE = 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.153, 1.175) + MAP_SWIPE_MULTIPLY_MINITOUCH = (1.115, 1.136) + MAP_SWIPE_MULTIPLY_MAATOUCH = (1.082, 1.102) + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + _is_D9 = False + + def battle_0(self): + if self.fleet_at(D9): + self._is_D9 = True + + if self._is_D9: + self.clear_chosen_enemy(D7) + else: + self.clear_chosen_enemy(F7) + + return True + + def battle_1(self): + if self._is_D9: + self.clear_chosen_enemy(F7) + else: + self.clear_chosen_enemy(D7) + + return True + + def battle_2(self): + if self._is_D9: + self.clear_chosen_enemy(D7) + else: + self.clear_chosen_enemy(F7) + + return True + + def battle_3(self): + if self._is_D9: + self.clear_chosen_enemy(F7) + else: + self.clear_chosen_enemy(D7) + + return True + + def battle_4(self): + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=2): + return True + + return self.battle_default() + + def battle_5(self): + 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/campaign/war_archives_20220224_cn/campaign_base.py b/campaign/war_archives_20220224_cn/campaign_base.py index 2eff6aa49..b8b32ebb9 100644 --- a/campaign/war_archives_20220224_cn/campaign_base.py +++ b/campaign/war_archives_20220224_cn/campaign_base.py @@ -1,3 +1,4 @@ +from module.ui.page import page_event from ..campaign_war_archives.campaign_base import CampaignBase as CampaignBase_ @@ -6,3 +7,9 @@ class CampaignBase(CampaignBase_): if super().handle_clear_mode_config_cover(): self.config.MAP_SIREN_TEMPLATE = ['SS'] self.config.MAP_HAS_SIREN = True + + def handle_exp_info(self): + # Random background hits EXP_INFO_B + if self.ui_page_appear(page_event): + return False + return super().handle_exp_info() diff --git a/config/deploy.template-AidLux-cn.yaml b/config/deploy.template-AidLux-cn.yaml index 98e8d1c0e..b37ef13b2 100644 --- a/config/deploy.template-AidLux-cn.yaml +++ b/config/deploy.template-AidLux-cn.yaml @@ -35,9 +35,9 @@ Deploy: # [Other] Use you own python, and its version should be 3.7.6 64bit PythonExecutable: /usr/bin/python # URL of pypi mirror - # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [CN user] Use 'https://mirrors.aliyun.com/pypi/simple' for faster and more stable download # [Other] Use null - PypiMirror: https://pypi.tuna.tsinghua.edu.cn/simple + PypiMirror: https://mirrors.aliyun.com/pypi/simple # Install dependencies at startup # [In most cases] Use true InstallDependencies: true diff --git a/config/deploy.template-AidLux.yaml b/config/deploy.template-AidLux.yaml index 68a19e145..4eb0dda8b 100644 --- a/config/deploy.template-AidLux.yaml +++ b/config/deploy.template-AidLux.yaml @@ -35,7 +35,7 @@ Deploy: # [Other] Use you own python, and its version should be 3.7.6 64bit PythonExecutable: /usr/bin/python # URL of pypi mirror - # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [CN user] Use 'https://mirrors.aliyun.com/pypi/simple' for faster and more stable download # [Other] Use null PypiMirror: null # Install dependencies at startup diff --git a/config/deploy.template-cn.yaml b/config/deploy.template-cn.yaml index 1db55dd1e..59cbec818 100644 --- a/config/deploy.template-cn.yaml +++ b/config/deploy.template-cn.yaml @@ -35,9 +35,9 @@ Deploy: # [Other] Use you own python, and its version should be 3.7.6 64bit PythonExecutable: ./toolkit/python.exe # URL of pypi mirror - # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [CN user] Use 'https://mirrors.aliyun.com/pypi/simple' for faster and more stable download # [Other] Use null - PypiMirror: https://pypi.tuna.tsinghua.edu.cn/simple + PypiMirror: https://mirrors.aliyun.com/pypi/simple # Install dependencies at startup # [In most cases] Use true InstallDependencies: true diff --git a/config/deploy.template-docker-cn.yaml b/config/deploy.template-docker-cn.yaml index 3ea1701c4..ad4a491aa 100644 --- a/config/deploy.template-docker-cn.yaml +++ b/config/deploy.template-docker-cn.yaml @@ -35,9 +35,9 @@ Deploy: # [Other] Use you own python, and its version should be 3.7.6 64bit PythonExecutable: /usr/local/bin/python # URL of pypi mirror - # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [CN user] Use 'https://mirrors.aliyun.com/pypi/simple' for faster and more stable download # [Other] Use null - PypiMirror: https://pypi.tuna.tsinghua.edu.cn/simple + PypiMirror: https://mirrors.aliyun.com/pypi/simple # Install dependencies at startup # [In most cases] Use true InstallDependencies: true diff --git a/config/deploy.template-docker.yaml b/config/deploy.template-docker.yaml index 1dc8421ec..b197bd921 100644 --- a/config/deploy.template-docker.yaml +++ b/config/deploy.template-docker.yaml @@ -35,7 +35,7 @@ Deploy: # [Other] Use you own python, and its version should be 3.7.6 64bit PythonExecutable: /usr/local/bin/python # URL of pypi mirror - # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [CN user] Use 'https://mirrors.aliyun.com/pypi/simple' for faster and more stable download # [Other] Use null PypiMirror: null # Install dependencies at startup diff --git a/config/deploy.template-linux-cn.yaml b/config/deploy.template-linux-cn.yaml index 09b287d61..26693d759 100644 --- a/config/deploy.template-linux-cn.yaml +++ b/config/deploy.template-linux-cn.yaml @@ -35,9 +35,9 @@ Deploy: # [Other] Use you own python, and its version should be 3.7.6 64bit PythonExecutable: python # URL of pypi mirror - # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [CN user] Use 'https://mirrors.aliyun.com/pypi/simple' for faster and more stable download # [Other] Use null - PypiMirror: https://pypi.tuna.tsinghua.edu.cn/simple + PypiMirror: https://mirrors.aliyun.com/pypi/simple # Install dependencies at startup # [In most cases] Use true InstallDependencies: true diff --git a/config/deploy.template-linux.yaml b/config/deploy.template-linux.yaml index a1abe262f..6b420880c 100644 --- a/config/deploy.template-linux.yaml +++ b/config/deploy.template-linux.yaml @@ -35,7 +35,7 @@ Deploy: # [Other] Use you own python, and its version should be 3.7.6 64bit PythonExecutable: python # URL of pypi mirror - # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [CN user] Use 'https://mirrors.aliyun.com/pypi/simple' for faster and more stable download # [Other] Use null PypiMirror: null # Install dependencies at startup diff --git a/config/deploy.template.yaml b/config/deploy.template.yaml index 5453b9c6f..a841c48da 100644 --- a/config/deploy.template.yaml +++ b/config/deploy.template.yaml @@ -35,7 +35,7 @@ Deploy: # [Other] Use you own python, and its version should be 3.7.6 64bit PythonExecutable: ./toolkit/python.exe # URL of pypi mirror - # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [CN user] Use 'https://mirrors.aliyun.com/pypi/simple' for faster and more stable download # [Other] Use null PypiMirror: null # Install dependencies at startup diff --git a/deploy/AidLux/0.92/requirements.txt b/deploy/AidLux/0.92/requirements.txt index 6bdd5e1ab..b2e80c1e1 100644 --- a/deploy/AidLux/0.92/requirements.txt +++ b/deploy/AidLux/0.92/requirements.txt @@ -9,7 +9,7 @@ jellyfish==0.11.2 lz4 mxnet==1.6.0 numpy -onepush +onepush==1.4.0 pillow prettytable==2.2.1 psutil==5.9.3 diff --git a/deploy/Windows/config.py b/deploy/Windows/config.py index 90ca2ae6c..db41aaa1a 100644 --- a/deploy/Windows/config.py +++ b/deploy/Windows/config.py @@ -81,7 +81,6 @@ class DeployConfig(ConfigModel): self.config_template = {} self.read() - self.write() self.show_config() def show_config(self): @@ -89,7 +88,7 @@ class DeployConfig(ConfigModel): for k, v in self.config.items(): if k in ("Password", "SSHUser"): continue - if self.config_template[k] == v: + if self.config_template.get(k) == v: continue logger.info(f"{k}: {v}") @@ -98,7 +97,8 @@ class DeployConfig(ConfigModel): def read(self): self.config = poor_yaml_read(DEPLOY_TEMPLATE) self.config_template = copy.deepcopy(self.config) - self.config.update(poor_yaml_read(self.file)) + origin = poor_yaml_read(self.file) + self.config.update(origin) for key, value in self.config.items(): if hasattr(self, key): @@ -106,6 +106,9 @@ class DeployConfig(ConfigModel): self.config_redirect() + if self.config != origin: + self.write() + def write(self): poor_yaml_write(self.config, self.file) diff --git a/deploy/config.py b/deploy/config.py index 528a16078..c99d72997 100644 --- a/deploy/config.py +++ b/deploy/config.py @@ -73,9 +73,9 @@ class DeployConfig(ConfigModel): """ self.file = file self.config = {} + self.config_template = {} self.read() - self.write() self.show_config() def show_config(self): @@ -92,7 +92,8 @@ class DeployConfig(ConfigModel): def read(self): self.config = poor_yaml_read(DEPLOY_TEMPLATE) self.config_template = copy.deepcopy(self.config) - self.config.update(poor_yaml_read(self.file)) + origin = poor_yaml_read(self.file) + self.config.update(origin) for key, value in self.config.items(): if hasattr(self, key): @@ -100,6 +101,9 @@ class DeployConfig(ConfigModel): self.config_redirect() + if self.config != origin: + self.write() + def write(self): poor_yaml_write(self.config, self.file) @@ -115,6 +119,12 @@ class DeployConfig(ConfigModel): 'https://git.saarcenter.com/LmeSzinc/AzurLaneAutoScript.git', ]: self.Repository = 'git://git.lyoko.io/AzurLaneAutoScript' + self.config['Repository'] = 'git://git.lyoko.io/AzurLaneAutoScript' + if self.PypiMirror in [ + 'https://pypi.tuna.tsinghua.edu.cn/simple' + ]: + self.PypiMirror = 'https://mirrors.aliyun.com/pypi/simple' + self.config['PypiMirror'] = 'https://mirrors.aliyun.com/pypi/simple' # Bypass webui.config.DeployConfig.__setattr__() # Don't write these into deploy.yaml diff --git a/deploy/docker/Dockerfile b/deploy/docker/Dockerfile index 7d9296bb1..cc68a0bb6 100644 --- a/deploy/docker/Dockerfile +++ b/deploy/docker/Dockerfile @@ -9,7 +9,7 @@ COPY requirements.txt /tmp/requirements.txt # Initial download of UiAutomator2 is slow outside of China using appetizer mirror, switch to GitHub RUN apt update \ - && apt install -y git adb libgomp1 \ + && apt install -y git adb libgomp1 openssh-client \ && git config --global --add safe.directory '*' \ && pip install -r /tmp/requirements.txt \ && rm /tmp/requirements.txt \ diff --git a/deploy/docker/Dockerfile.cn b/deploy/docker/Dockerfile.cn index 3bf41604a..2db750c3a 100644 --- a/deploy/docker/Dockerfile.cn +++ b/deploy/docker/Dockerfile.cn @@ -19,7 +19,7 @@ deb https://mirrors.aliyun.com/debian/ bullseye-backports main non-free contrib\ deb-src https://mirrors.aliyun.com/debian/ bullseye-backports main non-free contrib" \ > /etc/apt/sources.list \ && apt update \ - && apt install -y git adb libgomp1 \ + && apt install -y git adb libgomp1 openssh-client \ && git config --global --add safe.directory '*' \ && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo 'Asia/Shanghai' > /etc/timezone \ diff --git a/deploy/docker/requirements.txt b/deploy/docker/requirements.txt index 27ad15099..06d392fc1 100644 --- a/deploy/docker/requirements.txt +++ b/deploy/docker/requirements.txt @@ -27,7 +27,7 @@ prettytable==2.2.1 anyio==1.3.1 # Pushing -onepush==1.3.0 +onepush==1.4.0 pycryptodome==3.9.9 pypresence==4.2.1 diff --git a/deploy/headless/requirements.txt b/deploy/headless/requirements.txt index b10cef4d5..f8dcc6dfd 100644 --- a/deploy/headless/requirements.txt +++ b/deploy/headless/requirements.txt @@ -27,7 +27,7 @@ prettytable==2.2.1 anyio==1.3.1 # Pushing -onepush==1.3.0 +onepush==1.4.0 pycryptodome==3.9.9 pypresence==4.2.1 diff --git a/deploy/template b/deploy/template index 1f6355c99..e8dd17cb0 100644 --- a/deploy/template +++ b/deploy/template @@ -35,7 +35,7 @@ Deploy: # [Other] Use you own python, and its version should be 3.7.6 64bit PythonExecutable: './toolkit/python.exe' # URL of pypi mirror - # [CN user] Use 'https://pypi.tuna.tsinghua.edu.cn/simple' for faster and more stable download + # [CN user] Use 'https://mirrors.aliyun.com/pypi/simple' for faster and more stable download # [Other] Use null PypiMirror: null # Install dependencies at startup diff --git a/dev_tools/map_extractor.py b/dev_tools/map_extractor.py index 3ba289eb5..b0974c282 100644 --- a/dev_tools/map_extractor.py +++ b/dev_tools/map_extractor.py @@ -287,6 +287,13 @@ DIC_SIREN_NAME_CHI_TO_ENG = { 'tolove_renxing03': 'ToLoveNana03', 'tolove_renxing04': 'ToLoveHaruna04', 'tolove_renxing05': 'ToLoveGoldenDarkness05', + + # Paradiso of Shackled Light + 'boerzhanuo_alter': 'BolzanoAlter', + 'kaisa_alter': 'CesareAlter', + 'teluntuo_alter': 'TrentoAlter', + 'sairenboss26': 'SirenBoss26', + 'sairenboss25': 'SirenBoss25', } diff --git a/module/awaken/awaken.py b/module/awaken/awaken.py index cb76ea508..6e4de4715 100644 --- a/module/awaken/awaken.py +++ b/module/awaken/awaken.py @@ -131,7 +131,8 @@ class Awaken(Dock): if LEVEL_UP.match_luma(self.device.image): logger.info(f'awaken_once ended at {LEVEL_UP}') return 'no_exp' - if interval.reached() and AWAKENING.match_luma(self.device.image): + # Lower similarity due to random background + if interval.reached() and AWAKENING.match_luma(self.device.image, similarity=0.7): self.device.click(AWAKENING) interval.reset() continue diff --git a/module/base/utils.py b/module/base/utils.py index a0469f134..60c49d4ff 100644 --- a/module/base/utils.py +++ b/module/base/utils.py @@ -498,6 +498,22 @@ def location2node(location): return col2name(x) + str(y) +def xywh2xyxy(area): + """ + Convert (x, y, width, height) to (x1, y1, x2, y2) + """ + x, y, w, h = area + return x, y, x + w, y + h + + +def xyxy2xywh(area): + """ + Convert (x1, y1, x2, y2) to (x, y, width, height) + """ + x1, y1, x2, y2 = area + return min(x1, x2), min(y1, y2), abs(x2 - x1), abs(y2 - y1) + + def load_image(file, area=None): """ Load an image like pillow and drop alpha channel. @@ -715,7 +731,7 @@ def get_bbox(image, threshold=0): return x[0], y[0], x[-1] + 1, y[-1] + 1 -def get_bbox_reversed(image, threshold=0): +def get_bbox_reversed(image, threshold=255): """ Similar to `get_bbox` but for black contents on white background. diff --git a/module/campaign/campaign_event.py b/module/campaign/campaign_event.py index 53428045c..e9abb5223 100644 --- a/module/campaign/campaign_event.py +++ b/module/campaign/campaign_event.py @@ -18,14 +18,18 @@ class CampaignEvent(CampaignStatus): tasks (list[str]): Task name """ with self.config.multi_set(): + # Disable normal events for task in tasks: - if task in ['GemsFarming']: + if task in GEMS_FARMINGS: continue keys = f'{task}.Scheduler.Enable' logger.info(f'Disable task `{task}`') self.config.cross_set(keys=keys, value=False) - for task in ['GemsFarming']: + # Reset GemsFarming + for task in tasks: + if task not in GEMS_FARMINGS: + continue name = self.config.cross_get(keys=f'{task}.Campaign.Name', default='2-4') if not self.stage_is_main(name): from module.config.utils import deep_get @@ -64,7 +68,7 @@ class CampaignEvent(CampaignStatus): if limit <= 0 or command not in tasks: self.get_event_pt() return False - if command == 'GemsFarming' and self.stage_is_main(self.config.Campaign_Name): + if command in GEMS_FARMINGS and self.stage_is_main(self.config.Campaign_Name): self.get_event_pt() return False @@ -90,7 +94,7 @@ class CampaignEvent(CampaignStatus): command = self.config.Scheduler_Command if command not in tasks or limit == DEFAULT_TIME: return False - if command == 'GemsFarming' and self.stage_is_main(self.config.Campaign_Name): + if command in GEMS_FARMINGS and self.stage_is_main(self.config.Campaign_Name): return False now = datetime.now().replace(microsecond=0) @@ -146,7 +150,7 @@ class CampaignEvent(CampaignStatus): """ if self.appear(CAMPAIGN_MENU_NO_EVENT, offset=(20, 20)): logger.info('Event unavailable, disable task') - tasks = EVENTS + COALITIONS + GEMS_FARMINGS + tasks = EVENTS + RAIDS + COALITIONS + GEMS_FARMINGS self._disable_tasks(tasks) self.config.task_stop() else: @@ -195,6 +199,26 @@ class CampaignEvent(CampaignStatus): self.ui_goto(page_coalition) return True + def disable_raid_on_event(self): + """ + Disable raid tasks (or coalition) when entered an event, + to be foolproof if user forgot to disable raid tasks when raid is over and another event is ongoing + """ + command = self.config.Scheduler_Command + if command not in EVENTS + GEMS_FARMINGS: + return False + if command in GEMS_FARMINGS and self.stage_is_main(self.config.Campaign_Name): + return False + + tasks = RAIDS + COALITIONS + MARITIME_ESCORTS + tasks = [t for t in tasks if self.config.is_task_enabled(t)] + if tasks: + logger.info('New event ongoing, disable old event tasks') + self._disable_tasks(tasks) + return True + else: + return False + @staticmethod def stage_is_main(name) -> bool: """ diff --git a/module/campaign/run.py b/module/campaign/run.py index 2f124a861..729bebf30 100644 --- a/module/campaign/run.py +++ b/module/campaign/run.py @@ -385,6 +385,7 @@ class CampaignRun(CampaignEvent, ShopStatus): self.campaign.ensure_campaign_ui(name=self.stage, mode=mode) else: self.campaign.ensure_campaign_ui(name=self.stage, mode=mode) + self.disable_raid_on_event() self.handle_commission_notice() # if in hard mode, check remain times diff --git a/module/combat/combat.py b/module/combat/combat.py index 5306fb5ac..0b62b6e9b 100644 --- a/module/combat/combat.py +++ b/module/combat/combat.py @@ -101,6 +101,11 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan return PAUSE_Neon if PAUSE_Cyber.match_template_color(self.device.image, offset=(10, 10)): return PAUSE_Cyber + if PAUSE_HolyLight.match_template_color(self.device.image, offset=(10, 10)): + return PAUSE_HolyLight + # PAUSE_Pharaoh has random animation, assets should avoid the area in the middle and use match_luma + if PAUSE_Pharaoh.match_luma(self.device.image, offset=(10, 10)): + return PAUSE_Pharaoh return False def handle_combat_quit(self, offset=(20, 20), interval=3): @@ -125,6 +130,11 @@ class Combat(Level, HPBalancer, Retirement, SubmarineCall, CombatAuto, CombatMan self.device.click(QUIT_Christmas) timer.reset() return True + # Battle UI PAUSE_HolyLight uses QUIT_New + if QUIT_Pharaoh.match_luma(self.device.image, offset=offset): + self.device.click(QUIT_Pharaoh) + timer.reset() + return True return False def ensure_combat_oil_loaded(self): diff --git a/module/combat_ui/assets.py b/module/combat_ui/assets.py index 4228cbe43..4b19da285 100644 --- a/module/combat_ui/assets.py +++ b/module/combat_ui/assets.py @@ -8,10 +8,13 @@ PAUSE = Button(area={'cn': (1158, 40, 1199, 58), 'en': (1155, 38, 1216, 51), 'jp PAUSE_Christmas = Button(area={'cn': (1234, 35, 1250, 56), 'en': (1234, 35, 1250, 56), 'jp': (1234, 35, 1250, 56), 'tw': (1234, 35, 1250, 56)}, color={'cn': (158, 181, 210), 'en': (158, 181, 210), 'jp': (158, 181, 210), 'tw': (158, 181, 210)}, button={'cn': (1234, 35, 1250, 56), 'en': (1234, 35, 1250, 56), 'jp': (1234, 35, 1250, 56), 'tw': (1234, 35, 1250, 56)}, file={'cn': './assets/cn/combat_ui/PAUSE_Christmas.png', 'en': './assets/cn/combat_ui/PAUSE_Christmas.png', 'jp': './assets/cn/combat_ui/PAUSE_Christmas.png', 'tw': './assets/cn/combat_ui/PAUSE_Christmas.png'}) PAUSE_Cyber = Button(area={'cn': (1231, 32, 1253, 59), 'en': (1231, 32, 1253, 59), 'jp': (1231, 32, 1253, 59), 'tw': (1231, 32, 1253, 59)}, color={'cn': (40, 140, 157), 'en': (40, 140, 157), 'jp': (40, 140, 157), 'tw': (40, 140, 157)}, button={'cn': (1231, 32, 1253, 59), 'en': (1231, 32, 1253, 59), 'jp': (1231, 32, 1253, 59), 'tw': (1231, 32, 1253, 59)}, file={'cn': './assets/cn/combat_ui/PAUSE_Cyber.png', 'en': './assets/cn/combat_ui/PAUSE_Cyber.png', 'jp': './assets/cn/combat_ui/PAUSE_Cyber.png', 'tw': './assets/cn/combat_ui/PAUSE_Cyber.png'}) PAUSE_DOUBLE_CHECK = Button(area={'cn': (1226, 35, 1231, 60), 'en': (1226, 35, 1231, 61), 'jp': (1226, 35, 1230, 60), 'tw': (1226, 35, 1231, 60)}, color={'cn': (96, 104, 136), 'en': (83, 98, 118), 'jp': (97, 102, 120), 'tw': (96, 104, 136)}, button={'cn': (1226, 35, 1231, 60), 'en': (1226, 35, 1231, 61), 'jp': (1226, 35, 1230, 60), 'tw': (1226, 35, 1231, 60)}, file={'cn': './assets/cn/combat_ui/PAUSE_DOUBLE_CHECK.png', 'en': './assets/en/combat_ui/PAUSE_DOUBLE_CHECK.png', 'jp': './assets/jp/combat_ui/PAUSE_DOUBLE_CHECK.png', 'tw': './assets/tw/combat_ui/PAUSE_DOUBLE_CHECK.png'}) +PAUSE_HolyLight = Button(area={'cn': (1233, 35, 1250, 57), 'en': (1233, 35, 1250, 57), 'jp': (1233, 35, 1250, 57), 'tw': (1233, 35, 1250, 57)}, color={'cn': (54, 40, 27), 'en': (54, 40, 27), 'jp': (54, 40, 27), 'tw': (54, 40, 27)}, button={'cn': (1233, 35, 1250, 57), 'en': (1233, 35, 1250, 57), 'jp': (1233, 35, 1250, 57), 'tw': (1233, 35, 1250, 57)}, file={'cn': './assets/cn/combat_ui/PAUSE_HolyLight.png', 'en': './assets/cn/combat_ui/PAUSE_HolyLight.png', 'jp': './assets/cn/combat_ui/PAUSE_HolyLight.png', 'tw': './assets/cn/combat_ui/PAUSE_HolyLight.png'}) PAUSE_Iridescent_Fantasy = Button(area={'cn': (1232, 33, 1252, 57), 'en': (1232, 33, 1252, 57), 'jp': (1232, 33, 1252, 57), 'tw': (1232, 33, 1252, 57)}, color={'cn': (124, 139, 190), 'en': (124, 139, 190), 'jp': (124, 139, 190), 'tw': (124, 139, 190)}, button={'cn': (1232, 33, 1252, 57), 'en': (1232, 33, 1252, 57), 'jp': (1232, 33, 1252, 57), 'tw': (1232, 33, 1252, 57)}, file={'cn': './assets/cn/combat_ui/PAUSE_Iridescent_Fantasy.png', 'en': './assets/en/combat_ui/PAUSE_Iridescent_Fantasy.png', 'jp': './assets/jp/combat_ui/PAUSE_Iridescent_Fantasy.png', 'tw': './assets/tw/combat_ui/PAUSE_Iridescent_Fantasy.png'}) PAUSE_Neon = Button(area={'cn': (1228, 32, 1250, 59), 'en': (1228, 32, 1250, 59), 'jp': (1228, 32, 1250, 59), 'tw': (1228, 32, 1250, 59)}, color={'cn': (106, 137, 80), 'en': (106, 137, 80), 'jp': (106, 137, 80), 'tw': (106, 137, 80)}, button={'cn': (1228, 32, 1250, 59), 'en': (1228, 32, 1250, 59), 'jp': (1228, 32, 1250, 59), 'tw': (1228, 32, 1250, 59)}, file={'cn': './assets/cn/combat_ui/PAUSE_Neon.png', 'en': './assets/cn/combat_ui/PAUSE_Neon.png', 'jp': './assets/cn/combat_ui/PAUSE_Neon.png', 'tw': './assets/cn/combat_ui/PAUSE_Neon.png'}) PAUSE_New = Button(area={'cn': (1231, 29, 1253, 56), 'en': (1231, 29, 1253, 56), 'jp': (1231, 29, 1253, 56), 'tw': (1231, 29, 1253, 56)}, color={'cn': (156, 158, 166), 'en': (156, 158, 166), 'jp': (156, 158, 166), 'tw': (156, 158, 166)}, button={'cn': (1231, 29, 1253, 56), 'en': (1231, 29, 1253, 56), 'jp': (1231, 29, 1253, 56), 'tw': (1231, 29, 1253, 56)}, file={'cn': './assets/cn/combat_ui/PAUSE_New.png', 'en': './assets/en/combat_ui/PAUSE_New.png', 'jp': './assets/jp/combat_ui/PAUSE_New.png', 'tw': './assets/tw/combat_ui/PAUSE_New.png'}) +PAUSE_Pharaoh = Button(area={'cn': (1229, 55, 1259, 62), 'en': (1229, 55, 1259, 62), 'jp': (1229, 55, 1259, 62), 'tw': (1229, 55, 1259, 62)}, color={'cn': (164, 119, 78), 'en': (164, 119, 78), 'jp': (164, 119, 78), 'tw': (164, 119, 78)}, button={'cn': (1229, 55, 1259, 62), 'en': (1229, 55, 1259, 62), 'jp': (1229, 55, 1259, 62), 'tw': (1229, 55, 1259, 62)}, file={'cn': './assets/cn/combat_ui/PAUSE_Pharaoh.png', 'en': './assets/cn/combat_ui/PAUSE_Pharaoh.png', 'jp': './assets/cn/combat_ui/PAUSE_Pharaoh.png', 'tw': './assets/cn/combat_ui/PAUSE_Pharaoh.png'}) QUIT = Button(area={'cn': (420, 490, 593, 548), 'en': (473, 508, 567, 532), 'jp': (433, 490, 606, 547), 'tw': (433, 490, 606, 547)}, color={'cn': (199, 122, 114), 'en': (216, 168, 164), 'jp': (196, 120, 113), 'tw': (200, 126, 118)}, button={'cn': (420, 490, 593, 548), 'en': (473, 508, 567, 532), 'jp': (433, 490, 606, 547), 'tw': (433, 490, 606, 547)}, file={'cn': './assets/cn/combat_ui/QUIT.png', 'en': './assets/en/combat_ui/QUIT.png', 'jp': './assets/jp/combat_ui/QUIT.png', 'tw': './assets/tw/combat_ui/QUIT.png'}) QUIT_Christmas = Button(area={'cn': (400, 506, 477, 525), 'en': (410, 507, 469, 524), 'jp': (400, 506, 477, 525), 'tw': (400, 506, 477, 525)}, color={'cn': (195, 139, 166), 'en': (207, 166, 185), 'jp': (195, 139, 166), 'tw': (195, 139, 166)}, button={'cn': (400, 506, 477, 525), 'en': (410, 507, 469, 524), 'jp': (400, 506, 477, 525), 'tw': (400, 506, 477, 525)}, file={'cn': './assets/cn/combat_ui/QUIT_Christmas.png', 'en': './assets/en/combat_ui/QUIT_Christmas.png', 'jp': './assets/cn/combat_ui/QUIT_Christmas.png', 'tw': './assets/cn/combat_ui/QUIT_Christmas.png'}) QUIT_Iridescent_Fantasy = Button(area={'cn': (391, 522, 464, 540), 'en': (402, 507, 460, 523), 'jp': (391, 522, 464, 540), 'tw': (391, 522, 464, 540)}, color={'cn': (121, 73, 79), 'en': (255, 174, 164), 'jp': (108, 60, 70), 'tw': (121, 73, 79)}, button={'cn': (391, 522, 464, 540), 'en': (402, 507, 460, 523), 'jp': (391, 522, 464, 540), 'tw': (391, 522, 464, 540)}, file={'cn': './assets/cn/combat_ui/QUIT_Iridescent_Fantasy.png', 'en': './assets/en/combat_ui/QUIT_Iridescent_Fantasy.png', 'jp': './assets/jp/combat_ui/QUIT_Iridescent_Fantasy.png', 'tw': './assets/cn/combat_ui/QUIT_Iridescent_Fantasy.png'}) QUIT_New = Button(area={'cn': (394, 506, 467, 524), 'en': (404, 506, 463, 523), 'jp': (394, 506, 467, 524), 'tw': (394, 506, 467, 524)}, color={'cn': (255, 180, 171), 'en': (255, 195, 187), 'jp': (255, 180, 171), 'tw': (255, 180, 171)}, button={'cn': (394, 506, 467, 524), 'en': (404, 506, 463, 523), 'jp': (394, 506, 467, 524), 'tw': (394, 506, 467, 524)}, file={'cn': './assets/cn/combat_ui/QUIT_New.png', 'en': './assets/en/combat_ui/QUIT_New.png', 'jp': './assets/cn/combat_ui/QUIT_New.png', 'tw': './assets/cn/combat_ui/QUIT_New.png'}) +QUIT_Pharaoh = Button(area={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, color={'cn': (204, 132, 108), 'en': (204, 132, 108), 'jp': (204, 132, 108), 'tw': (204, 132, 108)}, button={'cn': (400, 507, 477, 525), 'en': (400, 507, 477, 525), 'jp': (400, 507, 477, 525), 'tw': (400, 507, 477, 525)}, file={'cn': './assets/cn/combat_ui/QUIT_Pharaoh.png', 'en': './assets/cn/combat_ui/QUIT_Pharaoh.png', 'jp': './assets/cn/combat_ui/QUIT_Pharaoh.png', 'tw': './assets/cn/combat_ui/QUIT_Pharaoh.png'}) diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 2e4c8e833..20c9a8104 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -1942,17 +1942,18 @@ "event_20240912_cn", "event_20241024_cn", "event_20241121_cn", - "event_20241219_cn" + "event_20241219_cn", + "event_20250227_cn" ], "display": "hide", "option_bold": [ - "event_20221222_cn", - "event_20231221_cn" + "event_20240725_cn", + "event_20250227_cn" ], - "cn": "event_20221222_cn", - "en": "event_20221222_cn", - "jp": "event_20221222_cn", - "tw": "event_20231221_cn" + "cn": "event_20250227_cn", + "en": "event_20250227_cn", + "jp": "event_20250227_cn", + "tw": "event_20240725_cn" }, "Mode": { "type": "select", @@ -5052,16 +5053,17 @@ "event_20240912_cn", "event_20241024_cn", "event_20241121_cn", - "event_20241219_cn" + "event_20241219_cn", + "event_20250227_cn" ], "option_bold": [ - "event_20221222_cn", - "event_20231221_cn" + "event_20240725_cn", + "event_20250227_cn" ], - "cn": "event_20221222_cn", - "en": "event_20221222_cn", - "jp": "event_20221222_cn", - "tw": "event_20231221_cn" + "cn": "event_20250227_cn", + "en": "event_20250227_cn", + "jp": "event_20250227_cn", + "tw": "event_20240725_cn" }, "Mode": { "type": "select", @@ -5966,16 +5968,17 @@ "event_20240912_cn", "event_20241024_cn", "event_20241121_cn", - "event_20241219_cn" + "event_20241219_cn", + "event_20250227_cn" ], "option_bold": [ - "event_20221222_cn", - "event_20231221_cn" + "event_20240725_cn", + "event_20250227_cn" ], - "cn": "event_20221222_cn", - "en": "event_20221222_cn", - "jp": "event_20221222_cn", - "tw": "event_20231221_cn" + "cn": "event_20250227_cn", + "en": "event_20250227_cn", + "jp": "event_20250227_cn", + "tw": "event_20240725_cn" }, "Mode": { "type": "select", @@ -6381,13 +6384,12 @@ "raid_20250116" ], "option_bold": [ - "raid_20240130", "raid_20250116" ], "cn": "raid_20250116", "en": "raid_20250116", "jp": "raid_20250116", - "tw": "raid_20240130" + "tw": "raid_20250116" }, "Mode": { "type": "select", @@ -7388,16 +7390,17 @@ "event_20240912_cn", "event_20241024_cn", "event_20241121_cn", - "event_20241219_cn" + "event_20241219_cn", + "event_20250227_cn" ], "option_bold": [ - "event_20221222_cn", - "event_20231221_cn" + "event_20240725_cn", + "event_20250227_cn" ], - "cn": "event_20221222_cn", - "en": "event_20221222_cn", - "jp": "event_20221222_cn", - "tw": "event_20231221_cn" + "cn": "event_20250227_cn", + "en": "event_20250227_cn", + "jp": "event_20250227_cn", + "tw": "event_20240725_cn" }, "Mode": { "type": "select", @@ -7862,16 +7865,17 @@ "event_20240912_cn", "event_20241024_cn", "event_20241121_cn", - "event_20241219_cn" + "event_20241219_cn", + "event_20250227_cn" ], "option_bold": [ - "event_20221222_cn", - "event_20231221_cn" + "event_20240725_cn", + "event_20250227_cn" ], - "cn": "event_20221222_cn", - "en": "event_20221222_cn", - "jp": "event_20221222_cn", - "tw": "event_20231221_cn" + "cn": "event_20250227_cn", + "en": "event_20250227_cn", + "jp": "event_20250227_cn", + "tw": "event_20240725_cn" }, "Mode": { "type": "select", @@ -8336,16 +8340,17 @@ "event_20240912_cn", "event_20241024_cn", "event_20241121_cn", - "event_20241219_cn" + "event_20241219_cn", + "event_20250227_cn" ], "option_bold": [ - "event_20221222_cn", - "event_20231221_cn" + "event_20240725_cn", + "event_20250227_cn" ], - "cn": "event_20221222_cn", - "en": "event_20221222_cn", - "jp": "event_20221222_cn", - "tw": "event_20231221_cn" + "cn": "event_20250227_cn", + "en": "event_20250227_cn", + "jp": "event_20250227_cn", + "tw": "event_20240725_cn" }, "Mode": { "type": "select", @@ -8810,16 +8815,17 @@ "event_20240912_cn", "event_20241024_cn", "event_20241121_cn", - "event_20241219_cn" + "event_20241219_cn", + "event_20250227_cn" ], "option_bold": [ - "event_20221222_cn", - "event_20231221_cn" + "event_20240725_cn", + "event_20250227_cn" ], - "cn": "event_20221222_cn", - "en": "event_20221222_cn", - "jp": "event_20221222_cn", - "tw": "event_20231221_cn" + "cn": "event_20250227_cn", + "en": "event_20250227_cn", + "jp": "event_20250227_cn", + "tw": "event_20240725_cn" }, "Mode": { "type": "select", @@ -9274,16 +9280,17 @@ "event_20240912_cn", "event_20241024_cn", "event_20241121_cn", - "event_20241219_cn" + "event_20241219_cn", + "event_20250227_cn" ], "option_bold": [ - "event_20221222_cn", - "event_20231221_cn" + "event_20240725_cn", + "event_20250227_cn" ], - "cn": "event_20221222_cn", - "en": "event_20221222_cn", - "jp": "event_20221222_cn", - "tw": "event_20231221_cn" + "cn": "event_20250227_cn", + "en": "event_20250227_cn", + "jp": "event_20250227_cn", + "tw": "event_20240725_cn" }, "Mode": { "type": "select", @@ -9686,13 +9693,12 @@ "raid_20250116" ], "option_bold": [ - "raid_20240130", "raid_20250116" ], "cn": "raid_20250116", "en": "raid_20250116", "jp": "raid_20250116", - "tw": "raid_20240130" + "tw": "raid_20250116" }, "Mode": { "type": "select", diff --git a/module/config/config_manual.py b/module/config/config_manual.py index 1c7d6ec01..bda8d4f59 100644 --- a/module/config/config_manual.py +++ b/module/config/config_manual.py @@ -119,6 +119,11 @@ class ManualConfig: MAP_HAS_MODE_SWITCH = False # event_20240725_cn has mode switch in map preparation # Events from 20240725 to 20241219 introduced new chapter switches MAP_CHAPTER_SWITCH_20241219 = False + # Since event_20241219_cn chapter B unlocks event startup + # which means chapter AB are continuous + STAGE_INCREASE_AB = False + # Insert anything to STAGE_INCREASE + STAGE_INCREASE_CUSTOM = '' MAP_HAS_CLEAR_PERCENTAGE = True MAP_HAS_WALK_SPEEDUP = False MAP_HAS_AMBUSH = True diff --git a/module/config/config_updater.py b/module/config/config_updater.py index 09038d475..93eec4f39 100644 --- a/module/config/config_updater.py +++ b/module/config/config_updater.py @@ -482,7 +482,7 @@ class ConfigGenerator: template = poor_yaml_read(DEPLOY_TEMPLATE) cn = { 'Repository': 'git://git.lyoko.io/AzurLaneAutoScript', - 'PypiMirror': 'https://pypi.tuna.tsinghua.edu.cn/simple', + 'PypiMirror': 'https://mirrors.aliyun.com/pypi/simple', 'Language': 'zh-CN', } aidlux = { diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 94ac91039..af573d539 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -779,7 +779,7 @@ "event_20220428_cn": "Rondo at Rainbows End Rerun", "event_20220526_cn": "Pledge of the Radiant Court Rerun", "event_20220728_cn": "Aquilifers Ballade Rerun", - "event_20220818_cn": "Operation Convergence", + "event_20220818_cn": "Operation Convergence Return", "event_20220915_cn": "Violet Tempest Blooming Lycoris Rerun", "event_20221124_cn": "The Alchemist and the Archipelago of Secrets", "event_20221222_cn": "Parallel Superimposition Rerun", @@ -801,6 +801,7 @@ "event_20241024_cn": "Tempesta and the Sleeping Sea", "event_20241121_cn": "Dangerous Inventions Incoming", "event_20241219_cn": "Substellar Crepuscule", + "event_20250227_cn": "Paradiso of Shackled Light", "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 f4a896b71..50113bced 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -779,7 +779,7 @@ "event_20220428_cn": "吟ずる瑠璃の楽章(復刻)", "event_20220526_cn": "復刻诚閃の剣 搖光の城", "event_20220728_cn": "鋼鷲の冒険譚(復刻)", - "event_20220818_cn": "結像点作戦", + "event_20220818_cn": "結像点作戦(復刻)", "event_20220915_cn": "赫の涙月 菫の暁風(復刻)", "event_20221124_cn": "錬金術士と謎の遺跡群島", "event_20221222_cn": "積重なる事象の幻界(復刻)", @@ -801,6 +801,7 @@ "event_20241024_cn": "テンペスタと眠りし海", "event_20241121_cn": "危険発明接近中", "event_20241219_cn": "星降る夕影の残光", + "event_20250227_cn": "籠檻に囚われし神光", "raid_20200624": "特別演習超空強襲波(復刻)", "raid_20210708": "交錯する新たな波 (復刻)", "raid_20220127": "秘密事件調査", diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 22d285699..9ead855d9 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -779,7 +779,7 @@ "event_20220428_cn": "复刻虹彩的终幕曲", "event_20220526_cn": "复刻泠誓光庭", "event_20220728_cn": "复刻雄鹰的叙事歌", - "event_20220818_cn": "远汇点作战", + "event_20220818_cn": "复刻远汇点作战", "event_20220915_cn": "复刻紫绛槿岚", "event_20221124_cn": "炼金术士与秘密遗迹群岛", "event_20221222_cn": "复刻定向折叠", @@ -801,6 +801,7 @@ "event_20241024_cn": "飓风与沉眠之海", "event_20241121_cn": "危险发明迫近中", "event_20241219_cn": "星光下的余晖", + "event_20250227_cn": "樊笼内的神光", "raid_20200624": "复刻特别演习埃塞克斯级", "raid_20210708": "复刻穿越彼方的水线", "raid_20220127": "演习神秘事件调查", diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index 1855c11d7..975f2dfb5 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -794,13 +794,14 @@ "event_20240229_cn": "雪境迷蹤", "event_20240425_cn": "共鳴的PASSION", "event_20240521_cn": "Light of the Martyrium", - "event_20240725_cn": "Interlude of Illusions", - "event_20240815_cn": "Windborne Steel Wings", + "event_20240725_cn": "幻夢間奏曲", + "event_20240815_cn": "鐵翼擎風", "event_20240829_cn": "埋葬於彼岸之花", "event_20240912_cn": "Ode of Everblooming Crimson", "event_20241024_cn": "Tempesta and the Sleeping Sea", "event_20241121_cn": "危險發明逼近中", "event_20241219_cn": "Substellar Crepuscule", + "event_20250227_cn": "Paradiso of Shackled Light", "raid_20200624": "特別演習埃塞克斯級(復刻)", "raid_20210708": "復刻穿越彼方的水線", "raid_20220127": "演習神秘事件調查", @@ -810,7 +811,7 @@ "raid_20230629": "綠洲往事", "raid_20240130": "寰昌宇定家事忙", "raid_20240328": "從零開始的魔王討伐之旅", - "raid_20250116": "Spring Fashion Festa", + "raid_20250116": "華裳巧展喜事長", "war_archives_20180607_cn": "檔案 墨染的鋼鐵之花", "war_archives_20180726_cn": "檔案 光與影的鳶尾之華", "war_archives_20181020_en": "檔案 努力希望和計劃", diff --git a/module/config/utils.py b/module/config/utils.py index 7e7268763..4b02a8ba8 100644 --- a/module/config/utils.py +++ b/module/config/utils.py @@ -614,6 +614,17 @@ def get_server_weekday(): return result +def get_server_monthday(): + """ + Returns: + int: The server's current day of the month + """ + diff = server_time_offset() + server_now = datetime.now() - diff + result = server_now.day + return result + + def random_id(length=32): """ Args: diff --git a/module/daemon/benchmark.py b/module/daemon/benchmark.py index cce5e37a2..9bc3d6c0b 100644 --- a/module/daemon/benchmark.py +++ b/module/daemon/benchmark.py @@ -165,6 +165,9 @@ class Benchmark(DaemonBase, CampaignUI): if click_result: self.show(test='Control', data=click_result, evaluate_func=self.evaluate_click) fastest = sorted(click_result, key=lambda item: compare(item))[0] + # Prefer MaaTouch if both minitouch and MaaTouch are fastest + if 'MaaTouch' in click and fastest[0] == 'minitouch': + fastest[0] = 'MaaTouch' logger.info(f'Recommend control method: {fastest[0]} ({float2str(fastest[1])})') fastest_click = fastest[0] @@ -174,7 +177,7 @@ class Benchmark(DaemonBase, CampaignUI): device = self.config.Benchmark_DeviceType # device == 'emulator' screenshot = ['ADB', 'ADB_nc', 'uiautomator2', 'aScreenCap', 'aScreenCap_nc', 'DroidCast', 'DroidCast_raw'] - click = ['ADB', 'uiautomator2', 'minitouch'] + click = ['ADB', 'uiautomator2', 'minitouch', 'MaaTouch'] def remove(*args): return [l for l in screenshot if l not in args] @@ -195,6 +198,8 @@ class Benchmark(DaemonBase, CampaignUI): screenshot.append('nemu_ipc') if self.device.ldopengl_available(): screenshot.append('ldopengl') + if self.device.is_bluestacks_air: + screenshot = [l for l in screenshot if 'DroidCast' not in l] scene = self.config.Benchmark_TestScene if 'screenshot' not in scene: diff --git a/module/daily/daily.py b/module/daily/daily.py index 6b2e95bf7..7e6653d95 100644 --- a/module/daily/daily.py +++ b/module/daily/daily.py @@ -1,5 +1,6 @@ import numpy as np +import module.config.server as server from module.base.utils import get_color from module.combat.assets import BATTLE_PREPARATION from module.combat.combat import Combat @@ -11,7 +12,10 @@ from module.ui.assets import BACK_ARROW, DAILY_CHECK from module.ui.page import page_campaign_menu, page_daily DAILY_MISSION_LIST = [DAILY_MISSION_1, DAILY_MISSION_2, DAILY_MISSION_3] -OCR_REMAIN = Digit(OCR_REMAIN, threshold=128, alphabet='01234') +if server.server != 'jp': + OCR_REMAIN = Digit(OCR_REMAIN, threshold=128, alphabet='01234') +else: + OCR_REMAIN = Digit(OCR_REMAIN, letter=(222, 223, 222), threshold=128, alphabet='01234') OCR_DAILY_FLEET_INDEX = Digit(OCR_DAILY_FLEET_INDEX, letter=(90, 154, 255), threshold=128, alphabet='123456') diff --git a/module/device/connection.py b/module/device/connection.py index cbe709598..3c2cc21fd 100644 --- a/module/device/connection.py +++ b/module/device/connection.py @@ -308,6 +308,23 @@ class Connection(ConnectionAttr): logger.attr('ro.product.brand', res) return 'waydroid' in res.lower() + @cached_property + @retry + def is_bluestacks_air(self): + # BlueStacks Air is the Mac version of BlueStacks + if not IS_MACINTOSH: + return False + if not self.is_ldplayer_bluestacks_family: + return False + # [bst.installed_images]: [Tiramisu64] + # [bst.instance]: [Tiramisu64] + # Tiramisu64 is Android 13 and BlueStacks Air is the only BlueStacks version that uses Android 13 + res = self.adb_getprop('bst.installed_images') + logger.attr('bst.installed_images', res) + if 'Tiramisu64' in res: + return True + return False + @cached_property @retry def nemud_app_keep_alive(self) -> str: @@ -401,11 +418,13 @@ class Connection(ConnectionAttr): host = '127.0.0.1' if IS_LINUX and host == '127.0.1.1': host = '127.0.0.1' + if self.is_bluestacks_air: + host = '127.0.0.1' logger.info(f'Connecting to local emulator, using host {host}') port = random_port(self.config.FORWARD_PORT_RANGE) # For AVD instance - if self.is_avd: + if self.is_avd or self.is_bluestacks_air: return host, port, "10.0.2.2", port return host, port, host, port @@ -1006,10 +1025,7 @@ class Connection(ConnectionAttr): self.serial = emu_serial # Redirect MuMu12 from 127.0.0.1:7555 to 127.0.0.1:16xxx - if ( - (IS_WINDOWS and self.serial == '127.0.0.1:7555') - or (IS_MACINTOSH and self.serial == '127.0.0.1:5555') - ): + if self.serial == '127.0.0.1:7555': for _ in range(2): mumu12 = available.select(may_mumu12_family=True) if mumu12.count == 1: diff --git a/module/device/method/ldopengl.py b/module/device/method/ldopengl.py index 25a07a25f..a7a71bd2a 100644 --- a/module/device/method/ldopengl.py +++ b/module/device/method/ldopengl.py @@ -1,6 +1,7 @@ import ctypes import os import subprocess +import time import typing as t from dataclasses import dataclass from functools import wraps @@ -333,13 +334,3 @@ class LDOpenGL(Platform): image = cv2.flip(image, 0) cv2.cvtColor(image, cv2.COLOR_BGR2RGB, dst=image) return image - - -if __name__ == '__main__': - ld = LDOpenGLImpl('E:/ProgramFiles/LDPlayer9', instance_id=1) - for _ in range(5): - import time - - start = time.time() - ld.screenshot() - print(time.time() - start) diff --git a/module/device/platform/platform_windows.py b/module/device/platform/platform_windows.py index 0936d93fe..ef1db201a 100644 --- a/module/device/platform/platform_windows.py +++ b/module/device/platform/platform_windows.py @@ -54,7 +54,9 @@ class PlatformWindows(PlatformBase, EmulatorManager): """ command = command.replace(r"\\", "/").replace("\\", "/").replace('"', '"') logger.info(f'Execute: {command}') - return subprocess.Popen(command, close_fds=True) # only work on Windows + # `close_fds` only work on Windows + # `start_new_session` to avoid emulator getting tree-killed when Alas gets killed + return subprocess.Popen(command, close_fds=True, start_new_session=True) @classmethod def kill_process_by_regex(cls, regex: str) -> int: diff --git a/module/exercise/hp_daemon.py b/module/exercise/hp_daemon.py index 39bf9bbfb..f9f62d6f6 100644 --- a/module/exercise/hp_daemon.py +++ b/module/exercise/hp_daemon.py @@ -1,7 +1,16 @@ from module.base.base import ModuleBase from module.base.timer import Timer from module.base.utils import color_bar_percentage -from module.combat_ui.assets import PAUSE, PAUSE_Christmas, PAUSE_Cyber, PAUSE_Iridescent_Fantasy, PAUSE_Neon, PAUSE_New +from module.combat_ui.assets import ( + PAUSE, + PAUSE_Christmas, + PAUSE_Cyber, + PAUSE_HolyLight, + PAUSE_Iridescent_Fantasy, + PAUSE_Neon, + PAUSE_New, + PAUSE_Pharaoh, +) from module.exercise.assets import * from module.logger import logger @@ -67,6 +76,8 @@ class HpDaemon(ModuleBase): PAUSE_Neon, PAUSE_Christmas, PAUSE_Cyber, + PAUSE_HolyLight, + PAUSE_Pharaoh, ]: self.attacker_hp = self._calculate_hp(image, area=ATTACKER_HP_AREA_New.area, reverse=True) self.defender_hp = self._calculate_hp(image, area=DEFENDER_HP_AREA_New.area, reverse=True) diff --git a/module/guild/assets.py b/module/guild/assets.py index 2d0715cad..910ae66dd 100644 --- a/module/guild/assets.py +++ b/module/guild/assets.py @@ -34,4 +34,4 @@ GUILD_REPORT_CLAIMED = Button(area={'cn': (738, 595, 1078, 637), 'en': (851, 602 GUILD_REPORT_CLOSE = Button(area={'cn': (1059, 93, 1102, 136), 'en': (1059, 93, 1102, 136), 'jp': (1059, 93, 1102, 136), 'tw': (1059, 93, 1102, 136)}, color={'cn': (71, 31, 32), 'en': (71, 31, 32), 'jp': (71, 31, 32), 'tw': (71, 31, 32)}, button={'cn': (1059, 93, 1102, 136), 'en': (1059, 93, 1102, 136), 'jp': (1059, 93, 1102, 136), 'tw': (1059, 93, 1102, 136)}, file={'cn': './assets/cn/guild/GUILD_REPORT_CLOSE.png', 'en': './assets/en/guild/GUILD_REPORT_CLOSE.png', 'jp': './assets/jp/guild/GUILD_REPORT_CLOSE.png', 'tw': './assets/tw/guild/GUILD_REPORT_CLOSE.png'}) GUILD_SUPPLY = Button(area={'cn': (1077, 617, 1233, 658), 'en': (1079, 619, 1231, 656), 'jp': (1077, 614, 1233, 655), 'tw': (1079, 619, 1231, 656)}, color={'cn': (84, 104, 154), 'en': (137, 87, 91), 'jp': (98, 100, 107), 'tw': (137, 87, 91)}, button={'cn': (1077, 617, 1233, 658), 'en': (1079, 619, 1231, 656), 'jp': (1077, 614, 1233, 655), 'tw': (1079, 619, 1231, 656)}, file={'cn': './assets/cn/guild/GUILD_SUPPLY.png', 'en': './assets/en/guild/GUILD_SUPPLY.png', 'jp': './assets/jp/guild/GUILD_SUPPLY.png', 'tw': './assets/tw/guild/GUILD_SUPPLY.png'}) OCR_GUILD_EXCHANGE_LIMIT = Button(area={'cn': (960, 383, 975, 403), 'en': (999, 383, 1018, 402), 'jp': (971, 384, 985, 403), 'tw': (962, 384, 975, 403)}, color={'cn': (59, 46, 47), 'en': (91, 93, 103), 'jp': (95, 101, 107), 'tw': (72, 57, 59)}, button={'cn': (960, 383, 975, 403), 'en': (999, 383, 1018, 402), 'jp': (971, 384, 985, 403), 'tw': (962, 384, 975, 403)}, file={'cn': './assets/cn/guild/OCR_GUILD_EXCHANGE_LIMIT.png', 'en': './assets/en/guild/OCR_GUILD_EXCHANGE_LIMIT.png', 'jp': './assets/jp/guild/OCR_GUILD_EXCHANGE_LIMIT.png', 'tw': './assets/tw/guild/OCR_GUILD_EXCHANGE_LIMIT.png'}) -OCR_GUILD_OPERATIONS_PROGRESS = Button(area={'cn': (928, 501, 978, 521), 'en': (928, 501, 978, 521), 'jp': (928, 501, 978, 521), 'tw': (928, 501, 978, 521)}, color={'cn': (83, 88, 101), 'en': (83, 88, 101), 'jp': (83, 88, 101), 'tw': (83, 88, 101)}, button={'cn': (928, 501, 978, 521), 'en': (928, 501, 978, 521), 'jp': (928, 501, 978, 521), 'tw': (928, 501, 978, 521)}, file={'cn': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'en': './assets/en/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'jp': './assets/jp/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'tw': './assets/tw/guild/OCR_GUILD_OPERATIONS_PROGRESS.png'}) +OCR_GUILD_OPERATIONS_PROGRESS = Button(area={'cn': (1064, 501, 1114, 521), 'en': (1064, 501, 1114, 521), 'jp': (1064, 501, 1114, 521), 'tw': (1064, 501, 1114, 521)}, color={'cn': (89, 94, 107), 'en': (89, 94, 107), 'jp': (89, 94, 107), 'tw': (89, 94, 107)}, button={'cn': (1064, 501, 1114, 521), 'en': (1064, 501, 1114, 521), 'jp': (1064, 501, 1114, 521), 'tw': (1064, 501, 1114, 521)}, file={'cn': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'en': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'jp': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png', 'tw': './assets/cn/guild/OCR_GUILD_OPERATIONS_PROGRESS.png'}) diff --git a/module/guild/operations.py b/module/guild/operations.py index 3ec99c92a..b5ab5043e 100644 --- a/module/guild/operations.py +++ b/module/guild/operations.py @@ -3,6 +3,7 @@ from datetime import datetime from module.base.button import ButtonGrid from module.base.timer import Timer from module.base.utils import * +from module.config.utils import get_server_monthday from module.exception import GameBugError from module.guild.assets import * from module.guild.base import GuildBase @@ -89,7 +90,7 @@ class GuildOperations(GuildBase): if not self.config.GuildOperation_SelectNewOperation: return False - today = datetime.now().day + today = get_server_monthday() limit = self.config.GuildOperation_NewOperationMaxDate if today >= limit: logger.info(f'No new guild operations because, today\'s date {today} >= limit {limit}') diff --git a/module/handler/fast_forward.py b/module/handler/fast_forward.py index d22961d84..ce8b4cf8d 100644 --- a/module/handler/fast_forward.py +++ b/module/handler/fast_forward.py @@ -347,8 +347,23 @@ class FastForwardHandler(AutoSearchHandler): str: Name of next stage in upper case, or origin name if unable to increase. """ + # Copy STAGE_INCREASE to avoid potential duplicate inserting + stage_increase = [r for r in self.STAGE_INCREASE] + # Insert custom increase logic + if self.config.STAGE_INCREASE_AB: + stage_increase = [ + 'A1 > A2 > A3 > B1 > B2 > B3', + 'C1 > C2 > C3 > D1 > D2 > D3', + ] + stage_increase + custom = self.config.STAGE_INCREASE_CUSTOM + if custom: + if isinstance(custom, str): + custom = [custom] + stage_increase = custom + stage_increase + + # Increase stage name = to_map_input_name(name) - for increase in self.STAGE_INCREASE: + for increase in stage_increase: increase = [i.strip(' \t\r\n') for i in increase.split('>')] if name in increase: index = increase.index(name) + 1 diff --git a/module/handler/info_handler.py b/module/handler/info_handler.py index f373722bd..e71cee07d 100644 --- a/module/handler/info_handler.py +++ b/module/handler/info_handler.py @@ -173,6 +173,13 @@ class InfoHandler(ModuleBase): if not self.device.app_is_running(): logger.error('Detected hot fixes from game server, game died') raise GameNotRunningError + # Use template match without color match due to maintenance popup + if self.appear(LOGIN_CHECK, offset=(30, 30)): + logger.error('Account logged out, ' + 'probably because account kicked by server maintenance or another log in') + # Kill game, because game patches after maintenance can only be downloaded at game startup + self.device.app_stop() + raise GameNotRunningError self._hot_fix_check_wait.clear() return appear diff --git a/module/hard/hard.py b/module/hard/hard.py index 3b2da4240..672fa7f5a 100644 --- a/module/hard/hard.py +++ b/module/hard/hard.py @@ -2,6 +2,7 @@ import importlib from campaign.campaign_hard.campaign_hard import Campaign from module.campaign.run import CampaignRun +from module.handler.fast_forward import to_map_file_name from module.hard.assets import * from module.logger import logger from module.ocr.ocr import Digit @@ -15,8 +16,7 @@ class CampaignHard(CampaignRun): def run(self): logger.hr('Campaign hard', level=1) - chapter, stage = self.config.Hard_HardStage.split('-') - name = f'campaign_{chapter}_{stage}' + name = to_map_file_name(self.config.Hard_HardStage) self.config.override( Campaign_Mode='hard', Campaign_UseFleetLock=True, diff --git a/module/meta_reward/assets.py b/module/meta_reward/assets.py index 7909d4162..b83bd797f 100644 --- a/module/meta_reward/assets.py +++ b/module/meta_reward/assets.py @@ -11,3 +11,6 @@ META_REWARD_NOTICE = Button(area={'cn': (1070, 508, 1075, 523), 'en': (1070, 508 REWARD_CHECK = Button(area={'cn': (31, 486, 64, 543), 'en': (35, 487, 62, 541), 'jp': (31, 486, 64, 543), 'tw': (31, 486, 64, 543)}, color={'cn': (199, 164, 165), 'en': (203, 169, 170), 'jp': (206, 172, 174), 'tw': (199, 164, 165)}, button={'cn': (31, 486, 64, 543), 'en': (35, 487, 62, 541), 'jp': (31, 486, 64, 543), 'tw': (31, 486, 64, 543)}, file={'cn': './assets/cn/meta_reward/REWARD_CHECK.png', 'en': './assets/en/meta_reward/REWARD_CHECK.png', 'jp': './assets/jp/meta_reward/REWARD_CHECK.png', 'tw': './assets/cn/meta_reward/REWARD_CHECK.png'}) REWARD_ENTER = Button(area={'cn': (1109, 535, 1187, 554), 'en': (1106, 532, 1199, 544), 'jp': (1108, 535, 1188, 554), 'tw': (1109, 535, 1187, 554)}, color={'cn': (199, 195, 201), 'en': (213, 212, 217), 'jp': (215, 207, 214), 'tw': (199, 195, 201)}, button={'cn': (1109, 535, 1187, 554), 'en': (1106, 532, 1199, 544), 'jp': (1108, 535, 1188, 554), 'tw': (1109, 535, 1187, 554)}, file={'cn': './assets/cn/meta_reward/REWARD_ENTER.png', 'en': './assets/en/meta_reward/REWARD_ENTER.png', 'jp': './assets/jp/meta_reward/REWARD_ENTER.png', 'tw': './assets/cn/meta_reward/REWARD_ENTER.png'}) REWARD_RECEIVE = Button(area={'cn': (1031, 601, 1215, 638), 'en': (1067, 608, 1182, 633), 'jp': (1043, 604, 1203, 635), 'tw': (1031, 601, 1215, 638)}, color={'cn': (149, 62, 62), 'en': (164, 92, 93), 'jp': (150, 64, 64), 'tw': (149, 62, 62)}, button={'cn': (1031, 601, 1215, 638), 'en': (1067, 608, 1182, 633), 'jp': (1043, 604, 1203, 635), 'tw': (1031, 601, 1215, 638)}, file={'cn': './assets/cn/meta_reward/REWARD_RECEIVE.png', 'en': './assets/en/meta_reward/REWARD_RECEIVE.png', 'jp': './assets/jp/meta_reward/REWARD_RECEIVE.png', 'tw': './assets/cn/meta_reward/REWARD_RECEIVE.png'}) +SYNC_ENTER = Button(area={'cn': (866, 351, 943, 370), 'en': (866, 351, 943, 370), 'jp': (866, 351, 944, 370), 'tw': (866, 351, 943, 370)}, color={'cn': (183, 175, 177), 'en': (132, 117, 119), 'jp': (185, 176, 179), 'tw': (183, 175, 177)}, button={'cn': (866, 351, 943, 370), 'en': (866, 351, 943, 370), 'jp': (866, 351, 944, 370), 'tw': (866, 351, 943, 370)}, file={'cn': './assets/cn/meta_reward/SYNC_ENTER.png', 'en': './assets/en/meta_reward/SYNC_ENTER.png', 'jp': './assets/jp/meta_reward/SYNC_ENTER.png', 'tw': './assets/cn/meta_reward/SYNC_ENTER.png'}) +SYNC_REWARD_NOTICE = Button(area={'cn': (977, 337, 981, 352), 'en': (977, 337, 981, 352), 'jp': (977, 337, 981, 352), 'tw': (977, 337, 981, 352)}, color={'cn': (250, 182, 57), 'en': (250, 182, 57), 'jp': (250, 182, 57), 'tw': (250, 182, 57)}, button={'cn': (977, 337, 981, 352), 'en': (977, 337, 981, 352), 'jp': (977, 337, 981, 352), 'tw': (977, 337, 981, 352)}, file={'cn': './assets/cn/meta_reward/SYNC_REWARD_NOTICE.png', 'en': './assets/cn/meta_reward/SYNC_REWARD_NOTICE.png', 'jp': './assets/cn/meta_reward/SYNC_REWARD_NOTICE.png', 'tw': './assets/cn/meta_reward/SYNC_REWARD_NOTICE.png'}) +SYNC_TAP = Button(area={'cn': (581, 339, 707, 377), 'en': (581, 339, 707, 377), 'jp': (564, 340, 720, 377), 'tw': (581, 339, 707, 377)}, color={'cn': (168, 112, 111), 'en': (129, 80, 80), 'jp': (176, 119, 119), 'tw': (168, 112, 111)}, button={'cn': (581, 339, 707, 377), 'en': (581, 339, 707, 377), 'jp': (564, 340, 720, 377), 'tw': (581, 339, 707, 377)}, file={'cn': './assets/cn/meta_reward/SYNC_TAP.png', 'en': './assets/en/meta_reward/SYNC_TAP.png', 'jp': './assets/jp/meta_reward/SYNC_TAP.png', 'tw': './assets/cn/meta_reward/SYNC_TAP.png'}) diff --git a/module/meta_reward/meta_reward.py b/module/meta_reward/meta_reward.py index 66512e98b..e96cb9f07 100644 --- a/module/meta_reward/meta_reward.py +++ b/module/meta_reward/meta_reward.py @@ -23,26 +23,6 @@ class BeaconReward(Combat, UI): logger.info('No meta reward red dot') return False - def meta_reward_enter(self, skip_first_screenshot=True): - """ - Pages: - in: page_meta - out: REWARD_CHECK - """ - logger.info('Meta reward enter') - while 1: - if skip_first_screenshot: - skip_first_screenshot = False - else: - self.device.screenshot() - - if self.appear_then_click(REWARD_ENTER, offset=(20, 20), interval=3): - continue - - # End - if self.appear(REWARD_CHECK, offset=(20, 20)): - break - def meta_reward_receive(self, skip_first_screenshot=True): """ Args: @@ -52,7 +32,7 @@ class BeaconReward(Combat, UI): bool: If received. Pages: - in: REWARD_CHECK + in: page_meta or REWARD_CHECK out: REWARD_CHECK """ logger.hr('Meta reward receive', level=1) @@ -64,6 +44,14 @@ class BeaconReward(Combat, UI): else: self.device.screenshot() + # End + # REWARD_CHECK appears and REWARD_RECEIVE gets gray + if self.appear(REWARD_CHECK, offset=(20, 20)) and \ + self.image_color_count(REWARD_RECEIVE, color=(49, 52, 49), threshold=221, count=400): + break + + if self.appear_then_click(REWARD_ENTER, offset=(20, 20), interval=3): + continue if self.match_template_color(REWARD_RECEIVE, offset=(20, 20), interval=3): self.device.click(REWARD_RECEIVE) confirm_timer.reset() @@ -81,17 +69,74 @@ class BeaconReward(Combat, UI): confirm_timer.reset() continue - # End - if self.appear(REWARD_CHECK, offset=(20, 20)) and \ - self.image_color_count(REWARD_RECEIVE, color=(49, 52, 49), threshold=221, count=400): - if confirm_timer.reached(): - break - else: - confirm_timer.reset() - logger.info(f'Meta reward receive finished, received={received}') return received + def meta_sync_notice_appear(self): + """ + "sync" is the period that you gather meta points to 100% and get a meta ship + + Returns: + bool: If appear. + + Page: + in: page_meta + """ + if self.appear(SYNC_REWARD_NOTICE, threshold=30): + logger.info('Found meta sync red dot') + return True + else: + logger.info('No meta sync red dot') + return False + + def meta_sync_receive(self, skip_first_screenshot=True): + """ + Args: + skip_first_screenshot: + + Returns: + bool: If received. + + Pages: + in: SYNC_ENTER + out: SYNC_ENTER if meta ship synced < 100% + REWARD_ENTER if meta ship synced >= 100% + """ + logger.hr('Meta sync receive', level=1) + received = False + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + # End + # Sync progress >= 100% + if self.appear(REWARD_ENTER, offset=(20, 20)): + logger.info('meta_sync_receive ends at REWARD_ENTER') + break + if self.appear(SYNC_ENTER, offset=(20, 20)): + if not self.meta_sync_notice_appear(): + logger.info('meta_sync_receive ends at SYNC_ENTER') + break + + # Click + if self.handle_popup_confirm('META_REWARD'): + # Lock new META ships + continue + if self.handle_get_items(): + received = True + continue + if self.handle_get_ship(): + received = True + continue + if self.appear_then_click(SYNC_TAP, offset=(20, 20), interval=3): + received = True + continue + + logger.info(f'Meta sync receive finished, received={received}') + return received + def run(self): if self.config.SERVER in ['cn', 'en', 'jp']: pass @@ -101,8 +146,10 @@ class BeaconReward(Combat, UI): self.ui_ensure(page_meta) + if self.meta_sync_notice_appear(): + self.meta_sync_receive() + if self.meta_reward_notice_appear(): - self.meta_reward_enter() self.meta_reward_receive() diff --git a/module/os/map.py b/module/os/map.py index 939f78280..29de04b9b 100644 --- a/module/os/map.py +++ b/module/os/map.py @@ -555,6 +555,7 @@ class OSMap(OSFleet, Map, GlobeCamera, StrategicSearchHandler): logger.info('Interrupting auto search') is_loading = False pause_interval = Timer(0.5, count=1) + in_main_timer = Timer(3, count=6) while 1: if skip_first_screenshot: skip_first_screenshot = False @@ -573,24 +574,31 @@ class OSMap(OSFleet, Map, GlobeCamera, StrategicSearchHandler): self.config.task_stop() if self.appear_then_click(AUTO_SEARCH_REWARD, offset=(50, 50), interval=3): + self.interval_clear(GOTO_MAIN) + in_main_timer.reset() continue if pause_interval.reached(): pause = self.is_combat_executing() if pause: self.device.click(pause) self.interval_reset(MAINTENANCE_ANNOUNCE) + is_loading = False pause_interval.reset() + in_main_timer.reset() continue if self.handle_combat_quit(): self.interval_reset(MAINTENANCE_ANNOUNCE) pause_interval.reset() + in_main_timer.reset() continue if self.appear_then_click(QUIT_RECONFIRM, offset=True, interval=5): self.interval_reset(MAINTENANCE_ANNOUNCE) pause_interval.reset() + in_main_timer.reset() continue if self.appear_then_click(GOTO_MAIN, offset=(20, 20), interval=3): + in_main_timer.reset() continue if self.ui_additional(): continue @@ -600,13 +608,18 @@ class OSMap(OSFleet, Map, GlobeCamera, StrategicSearchHandler): if not is_loading: if self.is_combat_loading(): is_loading = True + in_main_timer.clear() continue - if self.handle_battle_status(): - continue - if self.handle_exp_info(): - continue + # Random background from page_main may trigger EXP_INFO_*, don't check them + if in_main_timer.reached(): + logger.info('handle_exp_info') + if self.handle_battle_status(): + continue + if self.handle_exp_info(): + continue elif self.is_combat_executing(): is_loading = False + in_main_timer.clear() continue def os_auto_search_run(self, drop=None, strategic=False): diff --git a/module/os/map_operation.py b/module/os/map_operation.py index ab11e7bfb..635452168 100644 --- a/module/os/map_operation.py +++ b/module/os/map_operation.py @@ -77,7 +77,7 @@ class OSMapOperation(MapOrderHandler, MissionHandler, PortHandler, StorageHandle @Config.when(SERVER='jp') def get_zone_name(self): # For JP only - ocr = Ocr(MAP_NAME, lang='jp', letter=(185, 192, 201), threshold=127, name='OCR_OS_MAP_NAME') + ocr = Ocr(MAP_NAME, lang='jp', letter=(201, 218, 239), threshold=220, name='OCR_OS_MAP_NAME') name = ocr.ocr(self.device.image) self.is_zone_name_hidden = '安全' in name # Remove punctuations diff --git a/module/os_ash/ash.py b/module/os_ash/ash.py index 7360700bb..fcb25b8e1 100644 --- a/module/os_ash/ash.py +++ b/module/os_ash/ash.py @@ -148,7 +148,6 @@ class AshCombat(Combat): class OSAsh(UI, MapEventHandler): - _ash_fully_collected = False def ash_collect_status(self): @@ -160,16 +159,10 @@ class OSAsh(UI, MapEventHandler): return 0 if self.image_color_count(ASH_COLLECT_STATUS, color=(235, 235, 235), threshold=221, count=20): logger.info('Ash beacon status: light') - if server.server != 'jp': - ocr_collect = DigitCounter( - ASH_COLLECT_STATUS, letter=(235, 235, 235), threshold=160, name='OCR_ASH_COLLECT_STATUS') - ocr_daily = DailyDigitCounter( - ASH_DAILY_STATUS, letter=(235, 235, 235), threshold=160, name='OCR_ASH_DAILY_STATUS') - else: - ocr_collect = DigitCounter( - ASH_COLLECT_STATUS, letter=(201, 201, 201), threshold=128, name='OCR_ASH_COLLECT_STATUS') - ocr_daily = DailyDigitCounter( - ASH_DAILY_STATUS, letter=(193, 193, 193), threshold=160, name='OCR_ASH_DAILY_STATUS') + ocr_collect = DigitCounter( + ASH_COLLECT_STATUS, letter=(235, 235, 235), threshold=160, name='OCR_ASH_COLLECT_STATUS') + ocr_daily = DailyDigitCounter( + ASH_DAILY_STATUS, letter=(235, 235, 235), threshold=160, name='OCR_ASH_DAILY_STATUS') elif self.image_color_count(ASH_COLLECT_STATUS, color=(140, 142, 140), threshold=221, count=20): logger.info('Ash beacon status: gray') ocr_collect = DigitCounter( diff --git a/module/os_ash/assets.py b/module/os_ash/assets.py index 0d86d32ac..0323b4685 100644 --- a/module/os_ash/assets.py +++ b/module/os_ash/assets.py @@ -5,7 +5,7 @@ from module.base.template import Template # Don't modify it manually. ASH_COLLECT_STATUS = Button(area={'cn': (640, 27, 720, 49), 'en': (640, 27, 720, 49), 'jp': (640, 27, 720, 49), 'tw': (640, 27, 720, 49)}, color={'cn': (82, 92, 99), 'en': (82, 92, 99), 'jp': (82, 92, 99), 'tw': (82, 92, 99)}, button={'cn': (640, 27, 720, 49), 'en': (640, 27, 720, 49), 'jp': (640, 27, 720, 49), 'tw': (640, 27, 720, 49)}, file={'cn': './assets/cn/os_ash/ASH_COLLECT_STATUS.png', 'en': './assets/en/os_ash/ASH_COLLECT_STATUS.png', 'jp': './assets/jp/os_ash/ASH_COLLECT_STATUS.png', 'tw': './assets/tw/os_ash/ASH_COLLECT_STATUS.png'}) -ASH_DAILY_STATUS = Button(area={'cn': (637, 0, 741, 19), 'en': (637, 0, 741, 19), 'jp': (637, 0, 741, 19), 'tw': (637, 0, 741, 19)}, color={'cn': (104, 112, 121), 'en': (104, 112, 121), 'jp': (104, 112, 121), 'tw': (104, 112, 121)}, button={'cn': (637, 0, 741, 19), 'en': (637, 0, 741, 19), 'jp': (637, 0, 741, 19), 'tw': (637, 0, 741, 19)}, file={'cn': './assets/cn/os_ash/ASH_DAILY_STATUS.png', 'en': './assets/en/os_ash/ASH_DAILY_STATUS.png', 'jp': './assets/jp/os_ash/ASH_DAILY_STATUS.png', 'tw': './assets/tw/os_ash/ASH_DAILY_STATUS.png'}) +ASH_DAILY_STATUS = Button(area={'cn': (637, 0, 741, 19), 'en': (637, 0, 741, 19), 'jp': (637, 2, 741, 18), 'tw': (637, 0, 741, 19)}, color={'cn': (104, 112, 121), 'en': (104, 112, 121), 'jp': (90, 109, 125), 'tw': (104, 112, 121)}, button={'cn': (637, 0, 741, 19), 'en': (637, 0, 741, 19), 'jp': (637, 2, 741, 18), 'tw': (637, 0, 741, 19)}, file={'cn': './assets/cn/os_ash/ASH_DAILY_STATUS.png', 'en': './assets/en/os_ash/ASH_DAILY_STATUS.png', 'jp': './assets/jp/os_ash/ASH_DAILY_STATUS.png', 'tw': './assets/tw/os_ash/ASH_DAILY_STATUS.png'}) ASH_QUIT = Button(area={'cn': (29, 25, 59, 49), 'en': (29, 25, 59, 49), 'jp': (22, 25, 54, 48), 'tw': (29, 25, 59, 49)}, color={'cn': (115, 63, 68), 'en': (115, 63, 68), 'jp': (114, 61, 67), 'tw': (115, 63, 68)}, button={'cn': (29, 25, 59, 49), 'en': (29, 25, 59, 49), 'jp': (22, 25, 54, 48), 'tw': (29, 25, 59, 49)}, file={'cn': './assets/cn/os_ash/ASH_QUIT.png', 'en': './assets/en/os_ash/ASH_QUIT.png', 'jp': './assets/jp/os_ash/ASH_QUIT.png', 'tw': './assets/tw/os_ash/ASH_QUIT.png'}) ASH_SHOWDOWN = Button(area={'cn': (120, 14, 262, 38), 'en': (120, 16, 319, 35), 'jp': (122, 15, 246, 39), 'tw': (120, 14, 262, 38)}, color={'cn': (153, 131, 131), 'en': (130, 106, 106), 'jp': (164, 144, 145), 'tw': (153, 131, 131)}, button={'cn': (120, 14, 262, 38), 'en': (120, 16, 319, 35), 'jp': (122, 15, 246, 39), 'tw': (120, 14, 262, 38)}, file={'cn': './assets/cn/os_ash/ASH_SHOWDOWN.png', 'en': './assets/en/os_ash/ASH_SHOWDOWN.png', 'jp': './assets/jp/os_ash/ASH_SHOWDOWN.png', 'tw': './assets/tw/os_ash/ASH_SHOWDOWN.png'}) ASH_START = Button(area={'cn': (1061, 635, 1259, 695), 'en': (1063, 637, 1255, 693), 'jp': (1058, 634, 1260, 697), 'tw': (1043, 637, 1242, 698)}, color={'cn': (236, 187, 115), 'en': (223, 184, 124), 'jp': (222, 185, 126), 'tw': (223, 186, 131)}, button={'cn': (1061, 635, 1259, 695), 'en': (1063, 637, 1255, 693), 'jp': (1058, 634, 1260, 697), 'tw': (1043, 637, 1242, 698)}, file={'cn': './assets/cn/os_ash/ASH_START.png', 'en': './assets/en/os_ash/ASH_START.png', 'jp': './assets/jp/os_ash/ASH_START.png', 'tw': './assets/tw/os_ash/ASH_START.png'}) diff --git a/module/os_shop/item.py b/module/os_shop/item.py index ec82e7b5a..468deebfb 100644 --- a/module/os_shop/item.py +++ b/module/os_shop/item.py @@ -1,4 +1,5 @@ from typing import List +import module.config.server as server from module.logger import logger from module.ocr.ocr import DigitYuv, Ocr from module.statistics.item import Item, ItemGrid @@ -48,7 +49,10 @@ class CounterOcr(Ocr): COUNTER_OCR = CounterOcr([], threshold=96, name='Counter_ocr') -PRICE_OCR = PriceOcr([], letter=(255, 223, 57), threshold=32, name='Price_ocr') +if server.server in ['jp']: + PRICE_OCR = PriceOcr([], letter=(245, 214, 58), threshold=32, name='Price_ocr') +else: + PRICE_OCR = PriceOcr([], letter=(255, 223, 57), threshold=32, name='Price_ocr') class OSShopItem(Item): diff --git a/module/os_shop/port_shop.py b/module/os_shop/port_shop.py index 96d108f4d..0f765bfef 100644 --- a/module/os_shop/port_shop.py +++ b/module/os_shop/port_shop.py @@ -151,7 +151,7 @@ class PortShop(OSStatus, OSShopUI, Selector, MapEventHandler): logger.info('OS shop reach bottom, stop') break else: - OS_SHOP_SCROLL.next_page(main=self, page=0.5) + OS_SHOP_SCROLL.next_page(main=self, page=0.5, skip_first_screenshot=False) cur_pos = OS_SHOP_SCROLL.cal_position(main=self) continue self.device.click_record.clear() diff --git a/module/os_shop/ui.py b/module/os_shop/ui.py index 9716177b8..cbbc5ec4c 100644 --- a/module/os_shop/ui.py +++ b/module/os_shop/ui.py @@ -13,7 +13,8 @@ from module.ui.ui import UI OS_SHOP_SCROLL = AdaptiveScroll( OS_SHOP_SCROLL_AREA.button, parameters={ - 'height': 255 - 99 + 'height': 255 - 99, + 'prominence': 40, }, name="OS_SHOP_SCROLL" ) @@ -149,7 +150,7 @@ class OSShopUI(UI): retry.start() while True: logger.warning('Scroll does not drag success, retrying scroll') - OS_SHOP_SCROLL.next_page(main=self, page=0.5) + OS_SHOP_SCROLL.next_page(main=self, page=0.5, skip_first_screenshot=False) cur_pos = OS_SHOP_SCROLL.cal_position(main=self) if pre_pos != cur_pos: logger.info(f'Scroll success drag page to {cur_pos}') diff --git a/module/raid/assets.py b/module/raid/assets.py index 7a23c63b6..7965f5718 100644 --- a/module/raid/assets.py +++ b/module/raid/assets.py @@ -22,10 +22,10 @@ CHIENWU_OCR_REMAIN_EASY = Button(area={'cn': (1111, 528, 1163, 549), 'en': (1111 CHIENWU_OCR_REMAIN_EX = Button(area={'cn': (1086, 16, 1152, 44), 'en': (1086, 16, 1152, 44), 'jp': (1086, 16, 1152, 44), 'tw': (1086, 16, 1152, 44)}, color={'cn': (90, 39, 34), 'en': (90, 39, 34), 'jp': (90, 39, 34), 'tw': (90, 39, 34)}, button={'cn': (1086, 16, 1152, 44), 'en': (1086, 16, 1152, 44), 'jp': (1086, 16, 1152, 44), 'tw': (1086, 16, 1152, 44)}, file={'cn': './assets/cn/raid/CHIENWU_OCR_REMAIN_EX.png', 'en': './assets/cn/raid/CHIENWU_OCR_REMAIN_EX.png', 'jp': './assets/cn/raid/CHIENWU_OCR_REMAIN_EX.png', 'tw': './assets/cn/raid/CHIENWU_OCR_REMAIN_EX.png'}) CHIENWU_OCR_REMAIN_HARD = Button(area={'cn': (1106, 390, 1158, 411), 'en': (1106, 390, 1158, 411), 'jp': (1106, 390, 1158, 411), 'tw': (1106, 390, 1158, 411)}, color={'cn': (174, 153, 133), 'en': (174, 153, 133), 'jp': (174, 153, 133), 'tw': (174, 153, 133)}, button={'cn': (1106, 390, 1158, 411), 'en': (1106, 390, 1158, 411), 'jp': (1106, 390, 1158, 411), 'tw': (1106, 390, 1158, 411)}, file={'cn': './assets/cn/raid/CHIENWU_OCR_REMAIN_HARD.png', 'en': './assets/cn/raid/CHIENWU_OCR_REMAIN_HARD.png', 'jp': './assets/cn/raid/CHIENWU_OCR_REMAIN_HARD.png', 'tw': './assets/cn/raid/CHIENWU_OCR_REMAIN_HARD.png'}) CHIENWU_OCR_REMAIN_NORMAL = Button(area={'cn': (1144, 459, 1196, 480), 'en': (1144, 459, 1196, 480), 'jp': (1144, 459, 1196, 480), 'tw': (1144, 459, 1196, 480)}, color={'cn': (174, 153, 133), 'en': (174, 153, 133), 'jp': (174, 153, 133), 'tw': (174, 153, 133)}, button={'cn': (1144, 459, 1196, 480), 'en': (1144, 459, 1196, 480), 'jp': (1144, 459, 1196, 480), 'tw': (1144, 459, 1196, 480)}, file={'cn': './assets/cn/raid/CHIENWU_OCR_REMAIN_NORMAL.png', 'en': './assets/cn/raid/CHIENWU_OCR_REMAIN_NORMAL.png', 'jp': './assets/cn/raid/CHIENWU_OCR_REMAIN_NORMAL.png', 'tw': './assets/cn/raid/CHIENWU_OCR_REMAIN_NORMAL.png'}) -CHIENWU_RAID_EASY = Button(area={'cn': (1056, 528, 1095, 550), 'en': (1056, 527, 1096, 550), 'jp': (1055, 527, 1096, 551), 'tw': (1056, 528, 1095, 550)}, color={'cn': (221, 142, 125), 'en': (227, 166, 144), 'jp': (216, 134, 118), 'tw': (221, 142, 125)}, button={'cn': (1056, 528, 1095, 550), 'en': (1056, 527, 1096, 550), 'jp': (1055, 527, 1096, 551), 'tw': (1056, 528, 1095, 550)}, file={'cn': './assets/cn/raid/CHIENWU_RAID_EASY.png', 'en': './assets/en/raid/CHIENWU_RAID_EASY.png', 'jp': './assets/jp/raid/CHIENWU_RAID_EASY.png', 'tw': './assets/cn/raid/CHIENWU_RAID_EASY.png'}) +CHIENWU_RAID_EASY = Button(area={'cn': (1056, 528, 1095, 550), 'en': (1056, 527, 1096, 550), 'jp': (1055, 527, 1096, 551), 'tw': (1054, 527, 1097, 550)}, color={'cn': (221, 142, 125), 'en': (227, 166, 144), 'jp': (216, 134, 118), 'tw': (220, 147, 130)}, button={'cn': (1056, 528, 1095, 550), 'en': (1056, 527, 1096, 550), 'jp': (1055, 527, 1096, 551), 'tw': (1054, 527, 1097, 550)}, file={'cn': './assets/cn/raid/CHIENWU_RAID_EASY.png', 'en': './assets/en/raid/CHIENWU_RAID_EASY.png', 'jp': './assets/jp/raid/CHIENWU_RAID_EASY.png', 'tw': './assets/tw/raid/CHIENWU_RAID_EASY.png'}) CHIENWU_RAID_EX = Button(area={'cn': (1068, 251, 1125, 286), 'en': (1068, 251, 1125, 286), 'jp': (1068, 251, 1125, 286), 'tw': (1068, 251, 1125, 286)}, color={'cn': (199, 97, 95), 'en': (199, 97, 95), 'jp': (199, 97, 95), 'tw': (199, 97, 95)}, button={'cn': (1068, 251, 1125, 286), 'en': (1068, 251, 1125, 286), 'jp': (1068, 251, 1125, 286), 'tw': (1068, 251, 1125, 286)}, file={'cn': './assets/cn/raid/CHIENWU_RAID_EX.png', 'en': './assets/cn/raid/CHIENWU_RAID_EX.png', 'jp': './assets/cn/raid/CHIENWU_RAID_EX.png', 'tw': './assets/cn/raid/CHIENWU_RAID_EX.png'}) -CHIENWU_RAID_HARD = Button(area={'cn': (1049, 390, 1091, 411), 'en': (1047, 393, 1093, 408), 'jp': (1042, 390, 1098, 412), 'tw': (1049, 390, 1091, 411)}, color={'cn': (219, 135, 119), 'en': (220, 149, 130), 'jp': (220, 147, 128), 'tw': (219, 135, 119)}, button={'cn': (1049, 390, 1091, 411), 'en': (1047, 393, 1093, 408), 'jp': (1042, 390, 1098, 412), 'tw': (1049, 390, 1091, 411)}, file={'cn': './assets/cn/raid/CHIENWU_RAID_HARD.png', 'en': './assets/en/raid/CHIENWU_RAID_HARD.png', 'jp': './assets/jp/raid/CHIENWU_RAID_HARD.png', 'tw': './assets/cn/raid/CHIENWU_RAID_HARD.png'}) -CHIENWU_RAID_NORMAL = Button(area={'cn': (1087, 459, 1129, 481), 'en': (1078, 463, 1137, 478), 'jp': (1088, 459, 1128, 481), 'tw': (1087, 459, 1129, 481)}, color={'cn': (221, 143, 126), 'en': (220, 151, 132), 'jp': (214, 129, 115), 'tw': (221, 143, 126)}, button={'cn': (1087, 459, 1129, 481), 'en': (1078, 463, 1137, 478), 'jp': (1088, 459, 1128, 481), 'tw': (1087, 459, 1129, 481)}, file={'cn': './assets/cn/raid/CHIENWU_RAID_NORMAL.png', 'en': './assets/en/raid/CHIENWU_RAID_NORMAL.png', 'jp': './assets/jp/raid/CHIENWU_RAID_NORMAL.png', 'tw': './assets/cn/raid/CHIENWU_RAID_NORMAL.png'}) +CHIENWU_RAID_HARD = Button(area={'cn': (1049, 390, 1091, 411), 'en': (1047, 393, 1093, 408), 'jp': (1042, 390, 1098, 412), 'tw': (1048, 389, 1092, 413)}, color={'cn': (219, 135, 119), 'en': (220, 149, 130), 'jp': (220, 147, 128), 'tw': (216, 135, 120)}, button={'cn': (1049, 390, 1091, 411), 'en': (1047, 393, 1093, 408), 'jp': (1042, 390, 1098, 412), 'tw': (1048, 389, 1092, 413)}, file={'cn': './assets/cn/raid/CHIENWU_RAID_HARD.png', 'en': './assets/en/raid/CHIENWU_RAID_HARD.png', 'jp': './assets/jp/raid/CHIENWU_RAID_HARD.png', 'tw': './assets/tw/raid/CHIENWU_RAID_HARD.png'}) +CHIENWU_RAID_NORMAL = Button(area={'cn': (1087, 459, 1129, 481), 'en': (1078, 463, 1137, 478), 'jp': (1088, 459, 1128, 481), 'tw': (1086, 458, 1130, 482)}, color={'cn': (221, 143, 126), 'en': (220, 151, 132), 'jp': (214, 129, 115), 'tw': (221, 151, 133)}, button={'cn': (1087, 459, 1129, 481), 'en': (1078, 463, 1137, 478), 'jp': (1088, 459, 1128, 481), 'tw': (1086, 458, 1130, 482)}, file={'cn': './assets/cn/raid/CHIENWU_RAID_NORMAL.png', 'en': './assets/en/raid/CHIENWU_RAID_NORMAL.png', 'jp': './assets/jp/raid/CHIENWU_RAID_NORMAL.png', 'tw': './assets/tw/raid/CHIENWU_RAID_NORMAL.png'}) ESSEX_OCR_REMAIN_EASY = Button(area={'cn': (1071, 503, 1121, 518), 'en': (1071, 503, 1121, 518), 'jp': (1071, 503, 1121, 518), 'tw': (1071, 503, 1121, 518)}, color={'cn': (97, 130, 255), 'en': (97, 130, 255), 'jp': (97, 130, 255), 'tw': (97, 130, 255)}, button={'cn': (1071, 503, 1121, 518), 'en': (1071, 503, 1121, 518), 'jp': (1071, 503, 1121, 518), 'tw': (1071, 503, 1121, 518)}, file={'cn': './assets/cn/raid/ESSEX_OCR_REMAIN_EASY.png', 'en': './assets/en/raid/ESSEX_OCR_REMAIN_EASY.png', 'jp': './assets/jp/raid/ESSEX_OCR_REMAIN_EASY.png', 'tw': './assets/tw/raid/ESSEX_OCR_REMAIN_EASY.png'}) ESSEX_OCR_REMAIN_HARD = Button(area={'cn': (1088, 273, 1138, 287), 'en': (1088, 273, 1138, 287), 'jp': (1088, 273, 1138, 287), 'tw': (1088, 273, 1138, 287)}, color={'cn': (96, 126, 255), 'en': (96, 126, 255), 'jp': (96, 126, 255), 'tw': (96, 126, 255)}, button={'cn': (1088, 273, 1138, 287), 'en': (1088, 273, 1138, 287), 'jp': (1088, 273, 1138, 287), 'tw': (1088, 273, 1138, 287)}, file={'cn': './assets/cn/raid/ESSEX_OCR_REMAIN_HARD.png', 'en': './assets/en/raid/ESSEX_OCR_REMAIN_HARD.png', 'jp': './assets/jp/raid/ESSEX_OCR_REMAIN_HARD.png', 'tw': './assets/tw/raid/ESSEX_OCR_REMAIN_HARD.png'}) ESSEX_OCR_REMAIN_NORMAL = Button(area={'cn': (1049, 387, 1099, 402), 'en': (1049, 387, 1099, 402), 'jp': (1049, 387, 1099, 402), 'tw': (1049, 387, 1099, 402)}, color={'cn': (97, 129, 255), 'en': (97, 129, 255), 'jp': (97, 129, 255), 'tw': (97, 129, 255)}, button={'cn': (1049, 387, 1099, 402), 'en': (1049, 387, 1099, 402), 'jp': (1049, 387, 1099, 402), 'tw': (1049, 387, 1099, 402)}, file={'cn': './assets/cn/raid/ESSEX_OCR_REMAIN_NORMAL.png', 'en': './assets/en/raid/ESSEX_OCR_REMAIN_NORMAL.png', 'jp': './assets/jp/raid/ESSEX_OCR_REMAIN_NORMAL.png', 'tw': './assets/tw/raid/ESSEX_OCR_REMAIN_NORMAL.png'}) diff --git a/module/research/project.py b/module/research/project.py index 4b8377b1f..b77424c2f 100644 --- a/module/research/project.py +++ b/module/research/project.py @@ -4,6 +4,7 @@ from scipy import signal from module.base.decorator import cached_property from module.base.utils import * +from module.device.method.utils import remove_suffix from module.logger import logger from module.ocr.ocr import Duration, Ocr from module.research.assets import * @@ -364,7 +365,9 @@ def research_jp_detect(image): """ project = ResearchProjectJp() project.series = get_research_series_jp(image) - project.duration = str(get_research_duration_jp(image) / 3600).rstrip('.0') + project.duration = remove_suffix(str(get_research_duration_jp(image) / 3600), '.0') + if project.duration == '': + project.duration = '0' project.genre = get_research_genre_jp(image) costs = get_research_cost_jp(image) for cost in costs: diff --git a/module/shop/shop_voucher.py b/module/shop/shop_voucher.py index 3b0045cc8..1cdb2b27f 100644 --- a/module/shop/shop_voucher.py +++ b/module/shop/shop_voucher.py @@ -204,8 +204,7 @@ class VoucherShop(ShopClerk, ShopStatus): logger.info('Voucher Shop reach bottom, stop') break else: - # Only 4 rows of items at max, goto bottom directly - VOUCHER_SHOP_SCROLL.set_bottom(main=self) + VOUCHER_SHOP_SCROLL.next_page(main=self) del_cached_property(self, 'shop_grid') del_cached_property(self, 'shop_voucher_items') continue diff --git a/module/template/assets.py b/module/template/assets.py index e4f19d3a4..3db698482 100644 --- a/module/template/assets.py +++ b/module/template/assets.py @@ -64,6 +64,7 @@ TEMPLATE_SIREN_BBpurple = Template(file={'cn': './assets/cn/template/TEMPLATE_SI TEMPLATE_SIREN_BBred = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_BBred.gif', 'en': './assets/en/template/TEMPLATE_SIREN_BBred.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_BBred.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_BBred.gif'}) TEMPLATE_SIREN_BaltimoreIdol = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_BaltimoreIdol.gif', 'en': './assets/en/template/TEMPLATE_SIREN_BaltimoreIdol.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_BaltimoreIdol.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_BaltimoreIdol.gif'}) TEMPLATE_SIREN_Bellona = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Bellona.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Bellona.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Bellona.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Bellona.gif'}) +TEMPLATE_SIREN_BolzanoAlter = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_BolzanoAlter.gif', 'en': './assets/en/template/TEMPLATE_SIREN_BolzanoAlter.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_BolzanoAlter.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_BolzanoAlter.gif'}) TEMPLATE_SIREN_CA = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_CA.gif', 'en': './assets/en/template/TEMPLATE_SIREN_CA.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_CA.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_CA.gif'}) TEMPLATE_SIREN_CAalchemist = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_CAalchemist.gif', 'en': './assets/en/template/TEMPLATE_SIREN_CAalchemist.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_CAalchemist.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_CAalchemist.gif'}) TEMPLATE_SIREN_CAgreen = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_CAgreen.gif', 'en': './assets/en/template/TEMPLATE_SIREN_CAgreen.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_CAgreen.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_CAgreen.gif'}) @@ -78,6 +79,7 @@ TEMPLATE_SIREN_CValchemist = Template(file={'cn': './assets/cn/template/TEMPLATE TEMPLATE_SIREN_CVlightning = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_CVlightning.gif', 'en': './assets/en/template/TEMPLATE_SIREN_CVlightning.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_CVlightning.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_CVlightning.gif'}) TEMPLATE_SIREN_CVpurple = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_CVpurple.gif', 'en': './assets/en/template/TEMPLATE_SIREN_CVpurple.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_CVpurple.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_CVpurple.gif'}) TEMPLATE_SIREN_Carabiniere = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Carabiniere.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Carabiniere.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Carabiniere.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Carabiniere.gif'}) +TEMPLATE_SIREN_CesareAlter = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_CesareAlter.gif', 'en': './assets/en/template/TEMPLATE_SIREN_CesareAlter.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_CesareAlter.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_CesareAlter.gif'}) TEMPLATE_SIREN_Champagne = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Champagne.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Champagne.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Champagne.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Champagne.gif'}) TEMPLATE_SIREN_ChihayaKisaragi = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_ChihayaKisaragi.gif', 'en': './assets/en/template/TEMPLATE_SIREN_ChihayaKisaragi.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_ChihayaKisaragi.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_ChihayaKisaragi.gif'}) TEMPLATE_SIREN_Chitose = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Chitose.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Chitose.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Chitose.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Chitose.gif'}) @@ -199,6 +201,8 @@ TEMPLATE_SIREN_SirenBoss16 = Template(file={'cn': './assets/cn/template/TEMPLATE TEMPLATE_SIREN_SirenBoss18 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_SirenBoss18.gif', 'en': './assets/en/template/TEMPLATE_SIREN_SirenBoss18.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_SirenBoss18.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_SirenBoss18.gif'}) TEMPLATE_SIREN_SirenBoss182 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_SirenBoss182.gif', 'en': './assets/en/template/TEMPLATE_SIREN_SirenBoss182.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_SirenBoss182.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_SirenBoss182.gif'}) TEMPLATE_SIREN_SirenBoss19 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_SirenBoss19.gif', 'en': './assets/en/template/TEMPLATE_SIREN_SirenBoss19.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_SirenBoss19.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_SirenBoss19.gif'}) +TEMPLATE_SIREN_SirenBoss25 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_SirenBoss25.gif', 'en': './assets/en/template/TEMPLATE_SIREN_SirenBoss25.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_SirenBoss25.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_SirenBoss25.gif'}) +TEMPLATE_SIREN_SirenBoss26 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_SirenBoss26.gif', 'en': './assets/en/template/TEMPLATE_SIREN_SirenBoss26.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_SirenBoss26.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_SirenBoss26.gif'}) TEMPLATE_SIREN_Sirenboss10 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Sirenboss10.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Sirenboss10.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Sirenboss10.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Sirenboss10.gif'}) TEMPLATE_SIREN_Sirius = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Sirius.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Sirius.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Sirius.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Sirius.gif'}) TEMPLATE_SIREN_Soobrazitelny = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Soobrazitelny.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Soobrazitelny.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Soobrazitelny.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Soobrazitelny.gif'}) @@ -215,6 +219,7 @@ TEMPLATE_SIREN_ToLoveNana01 = Template(file={'cn': './assets/cn/template/TEMPLAT TEMPLATE_SIREN_ToLoveNana03 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_ToLoveNana03.gif', 'en': './assets/en/template/TEMPLATE_SIREN_ToLoveNana03.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_ToLoveNana03.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_ToLoveNana03.gif'}) TEMPLATE_SIREN_ToLoveYui02 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_ToLoveYui02.gif', 'en': './assets/en/template/TEMPLATE_SIREN_ToLoveYui02.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_ToLoveYui02.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_ToLoveYui02.gif'}) TEMPLATE_SIREN_Trento = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_Trento.gif', 'en': './assets/en/template/TEMPLATE_SIREN_Trento.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_Trento.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_Trento.gif'}) +TEMPLATE_SIREN_TrentoAlter = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_TrentoAlter.gif', 'en': './assets/en/template/TEMPLATE_SIREN_TrentoAlter.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_TrentoAlter.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_TrentoAlter.gif'}) TEMPLATE_SIREN_U101 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_U101.gif', 'en': './assets/en/template/TEMPLATE_SIREN_U101.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_U101.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_U101.gif'}) TEMPLATE_SIREN_U522 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_U522.gif', 'en': './assets/en/template/TEMPLATE_SIREN_U522.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_U522.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_U522.gif'}) TEMPLATE_SIREN_U73 = Template(file={'cn': './assets/cn/template/TEMPLATE_SIREN_U73.gif', 'en': './assets/en/template/TEMPLATE_SIREN_U73.gif', 'jp': './assets/jp/template/TEMPLATE_SIREN_U73.gif', 'tw': './assets/tw/template/TEMPLATE_SIREN_U73.gif'}) diff --git a/module/ui/assets.py b/module/ui/assets.py index d54700e30..bc2409db2 100644 --- a/module/ui/assets.py +++ b/module/ui/assets.py @@ -23,6 +23,7 @@ CAMPAIGN_MENU_GOTO_EXERCISE = Button(area={'cn': (1011, 586, 1106, 638), 'en': ( CAMPAIGN_MENU_GOTO_OS = Button(area={'cn': (355, 439, 501, 506), 'en': (374, 449, 503, 483), 'jp': (383, 442, 508, 507), 'tw': (355, 436, 503, 507)}, color={'cn': (103, 106, 113), 'en': (112, 115, 124), 'jp': (68, 72, 82), 'tw': (101, 104, 112)}, button={'cn': (543, 345, 967, 552), 'en': (543, 345, 967, 552), 'jp': (543, 345, 967, 552), 'tw': (543, 345, 967, 552)}, file={'cn': './assets/cn/ui/CAMPAIGN_MENU_GOTO_OS.png', 'en': './assets/en/ui/CAMPAIGN_MENU_GOTO_OS.png', 'jp': './assets/jp/ui/CAMPAIGN_MENU_GOTO_OS.png', 'tw': './assets/tw/ui/CAMPAIGN_MENU_GOTO_OS.png'}) CAMPAIGN_MENU_GOTO_WAR_ARCHIVES = Button(area={'cn': (210, 586, 310, 638), 'en': (222, 590, 308, 634), 'jp': (196, 591, 307, 632), 'tw': (211, 584, 311, 638)}, color={'cn': (62, 65, 66), 'en': (53, 55, 54), 'jp': (78, 80, 81), 'tw': (64, 67, 69)}, button={'cn': (210, 586, 310, 638), 'en': (222, 590, 308, 634), 'jp': (196, 591, 307, 632), 'tw': (211, 584, 311, 638)}, file={'cn': './assets/cn/ui/CAMPAIGN_MENU_GOTO_WAR_ARCHIVES.png', 'en': './assets/en/ui/CAMPAIGN_MENU_GOTO_WAR_ARCHIVES.png', 'jp': './assets/jp/ui/CAMPAIGN_MENU_GOTO_WAR_ARCHIVES.png', 'tw': './assets/tw/ui/CAMPAIGN_MENU_GOTO_WAR_ARCHIVES.png'}) CAMPAIGN_MENU_NO_EVENT = Button(area={'cn': (681, 204, 831, 233), 'en': (616, 206, 897, 233), 'jp': (575, 209, 940, 235), 'tw': (681, 204, 831, 233)}, color={'cn': (65, 75, 84), 'en': (57, 67, 77), 'jp': (49, 60, 70), 'tw': (65, 75, 84)}, button={'cn': (681, 204, 831, 233), 'en': (616, 206, 897, 233), 'jp': (575, 209, 940, 235), 'tw': (681, 204, 831, 233)}, file={'cn': './assets/cn/ui/CAMPAIGN_MENU_NO_EVENT.png', 'en': './assets/en/ui/CAMPAIGN_MENU_NO_EVENT.png', 'jp': './assets/jp/ui/CAMPAIGN_MENU_NO_EVENT.png', 'tw': './assets/cn/ui/CAMPAIGN_MENU_NO_EVENT.png'}) +CHANNEL_CHECK = Button(area={'cn': (785, 661, 824, 699), 'en': (785, 661, 824, 699), 'jp': (785, 661, 824, 699), 'tw': (785, 661, 824, 699)}, color={'cn': (88, 90, 89), 'en': (88, 90, 89), 'jp': (88, 90, 89), 'tw': (88, 90, 89)}, button={'cn': (785, 661, 824, 699), 'en': (785, 661, 824, 699), 'jp': (785, 661, 824, 699), 'tw': (785, 661, 824, 699)}, file={'cn': './assets/cn/ui/CHANNEL_CHECK.png', 'en': './assets/cn/ui/CHANNEL_CHECK.png', 'jp': './assets/cn/ui/CHANNEL_CHECK.png', 'tw': './assets/cn/ui/CHANNEL_CHECK.png'}) COALITION_CHECK = Button(area={'cn': (118, 14, 227, 39), 'en': (118, 16, 221, 36), 'jp': (118, 14, 227, 39), 'tw': (118, 14, 227, 39)}, color={'cn': (145, 161, 200), 'en': (116, 130, 168), 'jp': (150, 166, 204), 'tw': (152, 168, 206)}, button={'cn': (118, 14, 227, 39), 'en': (118, 16, 221, 36), 'jp': (118, 14, 227, 39), 'tw': (118, 14, 227, 39)}, file={'cn': './assets/cn/ui/COALITION_CHECK.png', 'en': './assets/en/ui/COALITION_CHECK.png', 'jp': './assets/jp/ui/COALITION_CHECK.png', 'tw': './assets/tw/ui/COALITION_CHECK.png'}) COMMISSION_CHECK = Button(area={'cn': (122, 16, 175, 39), 'en': (120, 14, 301, 41), 'jp': (121, 14, 176, 39), 'tw': (121, 14, 176, 41)}, color={'cn': (157, 173, 210), 'en': (98, 112, 150), 'jp': (159, 175, 211), 'tw': (141, 155, 193)}, button={'cn': (122, 16, 175, 39), 'en': (120, 14, 301, 41), 'jp': (121, 14, 176, 39), 'tw': (121, 14, 176, 41)}, file={'cn': './assets/cn/ui/COMMISSION_CHECK.png', 'en': './assets/en/ui/COMMISSION_CHECK.png', 'jp': './assets/jp/ui/COMMISSION_CHECK.png', 'tw': './assets/tw/ui/COMMISSION_CHECK.png'}) DAILY_CHECK = Button(area={'cn': (23, 656, 67, 698), 'en': (23, 656, 67, 698), 'jp': (23, 656, 67, 698), 'tw': (23, 656, 67, 698)}, color={'cn': (84, 139, 210), 'en': (84, 139, 210), 'jp': (84, 139, 210), 'tw': (84, 139, 210)}, button={'cn': (23, 656, 67, 698), 'en': (23, 656, 67, 698), 'jp': (23, 656, 67, 698), 'tw': (23, 656, 67, 698)}, file={'cn': './assets/cn/ui/DAILY_CHECK.png', 'en': './assets/en/ui/DAILY_CHECK.png', 'jp': './assets/jp/ui/DAILY_CHECK.png', 'tw': './assets/tw/ui/DAILY_CHECK.png'}) diff --git a/module/ui/page.py b/module/ui/page.py index 033a7f698..5db05b628 100644 --- a/module/ui/page.py +++ b/module/ui/page.py @@ -294,6 +294,12 @@ page_mail.link(button=GOTO_MAIN_WHITE, destination=page_main) # Mail enter varies from different UI page_main_white.link(button=MAIL_ENTER_WHITE, destination=page_mail) +# World channel +# Both old and new UI have CHANNEL_CHECK +# Click somewhere left to leave +page_channel = Page(CHANNEL_CHECK) +page_channel.link(button=CAMPAIGN_MENU_GOTO_CAMPAIGN, destination=page_main) + # RPG event (raid_20240328) page_rpg_stage = Page(RPG_GOTO_STORY) page_rpg_story = Page(RPG_GOTO_STAGE) diff --git a/module/war_archives/war_archives.py b/module/war_archives/war_archives.py index d4943c1db..24d7b3262 100644 --- a/module/war_archives/war_archives.py +++ b/module/war_archives/war_archives.py @@ -1,3 +1,5 @@ +import re + from campaign.campaign_war_archives.campaign_base import CampaignBase from module.campaign.run import CampaignRun from module.logger import logger @@ -5,7 +7,15 @@ from module.ocr.ocr import DigitCounter from module.war_archives.assets import (OCR_DATA_KEY_CAMPAIGN, WAR_ARCHIVES_CAMPAIGN_CHECK) -DATA_KEY_CAMPAIGN = DigitCounter(OCR_DATA_KEY_CAMPAIGN, letter=(255, 247, 247), threshold=64) + +class OcrDataKey(DigitCounter): + def after_process(self, result): + result = super().after_process(result) + result = re.sub(r'(\d{1,2})60$', r'\1/60', result) + return result + + +DATA_KEY_CAMPAIGN = OcrDataKey(OCR_DATA_KEY_CAMPAIGN, letter=(255, 247, 247), threshold=64) class CampaignWarArchives(CampaignRun, CampaignBase): diff --git a/module/webui/config.py b/module/webui/config.py index 0df5f0527..64e48c69f 100644 --- a/module/webui/config.py +++ b/module/webui/config.py @@ -1,3 +1,5 @@ +import copy + from filelock import FileLock from deploy.Windows.config import DeployConfig as _DeployConfig @@ -31,7 +33,9 @@ class DeployConfig(_DeployConfig): Read and update deploy config, copy `self.configs` to properties. """ self.config = poor_yaml_read_with_lock(DEPLOY_TEMPLATE) - self.config.update(poor_yaml_read_with_lock(self.file)) + self.config_template = copy.deepcopy(self.config) + origin = poor_yaml_read_with_lock(self.file) + self.config.update(origin) for key, value in self.config.items(): if hasattr(self, key): @@ -39,6 +43,9 @@ class DeployConfig(_DeployConfig): self.config_redirect() + if self.config != origin: + self.write() + def write(self): """ Write `self.config` into deploy config. @@ -51,5 +58,11 @@ class DeployConfig(_DeployConfig): """ super().__setattr__(key, value) if key[0].isupper() and key in self.config: - self.config[key] = value - self.write() + if key in self.config: + before = self.config[key] + if before != value: + self.config[key] = value + self.write() + else: + self.config[key] = value + self.write() diff --git a/requirements-in.txt b/requirements-in.txt index fd69eec2e..3dedf41d9 100644 --- a/requirements-in.txt +++ b/requirements-in.txt @@ -27,7 +27,7 @@ prettytable==2.2.1 anyio==1.3.1 # Pushing -onepush==1.3.0 +onepush==1.4.0 pycryptodome==3.9.9 pypresence==4.2.1 diff --git a/requirements.txt b/requirements.txt index 87d8e66de..72ed5c457 100644 --- a/requirements.txt +++ b/requirements.txt @@ -47,7 +47,7 @@ matplotlib==3.4.3 # via gluoncv msgpack==1.0.3 # via zerorpc mxnet==1.6.0 # via -r requirements-in.txt, cnocr numpy==1.16.6 # via -r requirements-in.txt, cnocr, gluoncv, imageio, matplotlib, mxnet, opencv-python, scipy -onepush==1.3.0 # via -r requirements-in.txt +onepush==1.4.0 # via -r requirements-in.txt opencv-python==4.5.3.56 # via -r requirements-in.txt packaging==20.9 # via deprecation, uiautomator2 pillow==8.3.2 # via -r requirements-in.txt, cnocr, gluoncv, imageio, matplotlib, uiautomator2