diff --git a/.github/ISSUE_TEMPLATE/bug_report_cn.yaml b/.github/ISSUE_TEMPLATE/bug_report_cn.yaml index 798d57aad..bf03959c6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_cn.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report_cn.yaml @@ -1,6 +1,6 @@ name: 上报 Bug description: 使用中文进行 Bug 报告 -labels: ['bug'] +labels: ['bug / 缺陷'] body: - type: checkboxes id: checks diff --git a/.github/ISSUE_TEMPLATE/bug_report_en.yaml b/.github/ISSUE_TEMPLATE/bug_report_en.yaml index 5f667dd4e..dcdafbbc2 100644 --- a/.github/ISSUE_TEMPLATE/bug_report_en.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report_en.yaml @@ -1,6 +1,6 @@ name: Bug Report description: Create a report to help us improve -labels: ['bug'] +labels: ['bug / 缺陷'] body: - type: checkboxes id: checks diff --git a/.github/ISSUE_TEMPLATE/feature_request_cn.yaml b/.github/ISSUE_TEMPLATE/feature_request_cn.yaml index c19b7207a..004aaaf41 100644 --- a/.github/ISSUE_TEMPLATE/feature_request_cn.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request_cn.yaml @@ -1,6 +1,6 @@ name: 功能请求 description: 使用中文进行功能请求 -labels: ['feature'] +labels: ['feature request / 功能请求'] body: - type: textarea id: describe diff --git a/.github/ISSUE_TEMPLATE/feature_request_en.yaml b/.github/ISSUE_TEMPLATE/feature_request_en.yaml index 7b5dc3e0d..8a3c618d0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request_en.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request_en.yaml @@ -1,6 +1,6 @@ name: Feature request description: Suggest an idea for this project -labels: ['feature'] +labels: ['feature request / 功能请求'] body: - type: textarea id: describe diff --git a/alas.py b/alas.py index 8a723d495..cacd4eafd 100644 --- a/alas.py +++ b/alas.py @@ -288,6 +288,10 @@ class AzurLaneAutoScript: from module.campaign.os_run import OSCampaignRun OSCampaignRun(config=self.config, device=self.device).opsi_shop() + def opsi_voucher(self): + from module.campaign.os_run import OSCampaignRun + OSCampaignRun(config=self.config, device=self.device).opsi_voucher() + def opsi_daily(self): from module.campaign.os_run import OSCampaignRun OSCampaignRun(config=self.config, device=self.device).opsi_daily() @@ -300,6 +304,10 @@ class AzurLaneAutoScript: from module.campaign.os_run import OSCampaignRun OSCampaignRun(config=self.config, device=self.device).opsi_abyssal() + def opsi_archive(self): + from module.campaign.os_run import OSCampaignRun + OSCampaignRun(config=self.config, device=self.device).opsi_archive() + def opsi_stronghold(self): from module.campaign.os_run import OSCampaignRun OSCampaignRun(config=self.config, device=self.device).opsi_stronghold() @@ -308,6 +316,10 @@ class AzurLaneAutoScript: from module.campaign.os_run import OSCampaignRun OSCampaignRun(config=self.config, device=self.device).opsi_meowfficer_farming() + def opsi_hazard1_leveling(self): + from module.campaign.os_run import OSCampaignRun + OSCampaignRun(config=self.config, device=self.device).opsi_hazard1_leveling() + def opsi_cross_month(self): from module.campaign.os_run import OSCampaignRun OSCampaignRun(config=self.config, device=self.device).opsi_cross_month() diff --git a/assets/cn/os_handler/ACTION_POINT_REMAIN_OS.png b/assets/cn/os_handler/ACTION_POINT_REMAIN_OS.png new file mode 100644 index 000000000..9df6717c5 Binary files /dev/null and b/assets/cn/os_handler/ACTION_POINT_REMAIN_OS.png differ diff --git a/assets/cn/os_handler/EXCHANGE_ENTER.png b/assets/cn/os_handler/EXCHANGE_ENTER.png new file mode 100644 index 000000000..6e2536ed4 Binary files /dev/null and b/assets/cn/os_handler/EXCHANGE_ENTER.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CHECK.png b/assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CHECK.png new file mode 100644 index 000000000..6493c4a1d Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CHECK.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CONTINUE.png b/assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CONTINUE.png new file mode 100644 index 000000000..1dcdef5a8 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CONTINUE.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_STOP.png b/assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_STOP.png new file mode 100644 index 000000000..ae87d7e47 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_STOP.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_MAP_OPTION_OFF.png b/assets/cn/os_handler/STRATEGIC_SEARCH_MAP_OPTION_OFF.png new file mode 100644 index 000000000..17807d07c Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_MAP_OPTION_OFF.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_CONTINUE.png b/assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_CONTINUE.png new file mode 100644 index 000000000..1dcdef5a8 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_CONTINUE.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_STOP.png b/assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_STOP.png new file mode 100644 index 000000000..ae87d7e47 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_STOP.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_POPUP_CHECK.png b/assets/cn/os_handler/STRATEGIC_SEARCH_POPUP_CHECK.png new file mode 100644 index 000000000..08d7ec861 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_POPUP_CHECK.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_SCROLL_AREA.png b/assets/cn/os_handler/STRATEGIC_SEARCH_SCROLL_AREA.png new file mode 100644 index 000000000..adf935a00 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_SCROLL_AREA.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.BUTTON.png b/assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.BUTTON.png new file mode 100644 index 000000000..ea4150287 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.BUTTON.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.png b/assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.png new file mode 100644 index 000000000..a4b7c3649 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_ON.png b/assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_ON.png new file mode 100644 index 000000000..c75475368 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_ON.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_OFF.png b/assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_OFF.png new file mode 100644 index 000000000..56a7ea7e8 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_OFF.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_ON.png b/assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_ON.png new file mode 100644 index 000000000..eb57cacbc Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_ON.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_RANDOM.png b/assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_RANDOM.png new file mode 100644 index 000000000..bdb3a465c Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_RANDOM.png differ diff --git a/assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_REPEAT.png b/assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_REPEAT.png new file mode 100644 index 000000000..c2516b843 Binary files /dev/null and b/assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_REPEAT.png differ diff --git a/assets/cn/shop/SHOP_BUY_CONFIRM_AMOUNT.png b/assets/cn/shop/SHOP_BUY_CONFIRM_AMOUNT.png index d2d7aa50b..11a98fbff 100644 Binary files a/assets/cn/shop/SHOP_BUY_CONFIRM_AMOUNT.png and b/assets/cn/shop/SHOP_BUY_CONFIRM_AMOUNT.png differ diff --git a/assets/cn/shop/SHOP_VOUCHER.png b/assets/cn/shop/SHOP_VOUCHER.png new file mode 100644 index 000000000..fed0fa51f Binary files /dev/null and b/assets/cn/shop/SHOP_VOUCHER.png differ diff --git a/assets/cn/shop/VOUCHER_SHOP_SCROLL_AREA.png b/assets/cn/shop/VOUCHER_SHOP_SCROLL_AREA.png new file mode 100644 index 000000000..314675bcd Binary files /dev/null and b/assets/cn/shop/VOUCHER_SHOP_SCROLL_AREA.png differ diff --git a/assets/cn/war_archives/TEMPLATE_MICROLAYER_MEDLEY.png b/assets/cn/war_archives/TEMPLATE_MICROLAYER_MEDLEY.png new file mode 100644 index 000000000..e1a53d09b Binary files /dev/null and b/assets/cn/war_archives/TEMPLATE_MICROLAYER_MEDLEY.png differ diff --git a/assets/cn/war_archives/TEMPLATE_STARS_OF_THE_SHIMMERING_FJORD.png b/assets/cn/war_archives/TEMPLATE_STARS_OF_THE_SHIMMERING_FJORD.png new file mode 100644 index 000000000..0cb2c8f97 Binary files /dev/null and b/assets/cn/war_archives/TEMPLATE_STARS_OF_THE_SHIMMERING_FJORD.png differ diff --git a/assets/en/os_handler/EXCHANGE_ENTER.png b/assets/en/os_handler/EXCHANGE_ENTER.png new file mode 100644 index 000000000..813ca440d Binary files /dev/null and b/assets/en/os_handler/EXCHANGE_ENTER.png differ diff --git a/assets/en/shop/SHOP_VOUCHER.png b/assets/en/shop/SHOP_VOUCHER.png new file mode 100644 index 000000000..fed0fa51f Binary files /dev/null and b/assets/en/shop/SHOP_VOUCHER.png differ diff --git a/assets/en/shop/VOUCHER_SHOP_SCROLL_AREA.png b/assets/en/shop/VOUCHER_SHOP_SCROLL_AREA.png new file mode 100644 index 000000000..314675bcd Binary files /dev/null and b/assets/en/shop/VOUCHER_SHOP_SCROLL_AREA.png differ diff --git a/assets/gui/css/alas-mobile.css b/assets/gui/css/alas-mobile.css index 4e0abe865..9acad6eb7 100644 --- a/assets/gui/css/alas-mobile.css +++ b/assets/gui/css/alas-mobile.css @@ -30,6 +30,12 @@ grid-template-rows: unset; } +[id^="pywebio-scope-arg_container-storage-"] { + grid-auto-flow: column; + grid-template-columns: 1fr auto; + grid-template-rows: 1fr auto; +} + #pywebio-scope-_groups { grid-template-columns: 0fr 1fr; } diff --git a/assets/gui/css/alas-pc.css b/assets/gui/css/alas-pc.css index c6fb15716..57d95088e 100644 --- a/assets/gui/css/alas-pc.css +++ b/assets/gui/css/alas-pc.css @@ -3,6 +3,12 @@ grid-template-columns: 1fr 13rem; } +[id^="pywebio-scope-arg_container-storage-"] { + grid-auto-flow: column; + grid-template-columns: 1fr auto; + grid-template-rows: 1fr auto; +} + #pywebio-scope-overview { grid-auto-flow: column; grid-template-columns: minmax(16rem, 20rem) minmax(24rem, 1fr); diff --git a/assets/gui/css/alas.css b/assets/gui/css/alas.css index 421c167cb..a379a9c12 100644 --- a/assets/gui/css/alas.css +++ b/assets/gui/css/alas.css @@ -445,7 +445,8 @@ pre.rich-traceback-code { margin: .125rem 0; } -[id^="pywebio-scope-arg_container-checkbox-"] { +[id^="pywebio-scope-arg_container-checkbox-"], +[id^="pywebio-scope-arg_container-storage-"] { display: grid; margin: .375rem 0; } diff --git a/assets/jp/os_handler/EXCHANGE_ENTER.png b/assets/jp/os_handler/EXCHANGE_ENTER.png new file mode 100644 index 000000000..f264d4547 Binary files /dev/null and b/assets/jp/os_handler/EXCHANGE_ENTER.png differ diff --git a/assets/jp/retire/TEMPLATE_FLEET_1.png b/assets/jp/retire/TEMPLATE_FLEET_1.png new file mode 100644 index 000000000..142a378a4 Binary files /dev/null and b/assets/jp/retire/TEMPLATE_FLEET_1.png differ diff --git a/assets/jp/retire/TEMPLATE_FLEET_2.png b/assets/jp/retire/TEMPLATE_FLEET_2.png new file mode 100644 index 000000000..177bfe97e Binary files /dev/null and b/assets/jp/retire/TEMPLATE_FLEET_2.png differ diff --git a/assets/jp/retire/TEMPLATE_FLEET_3.png b/assets/jp/retire/TEMPLATE_FLEET_3.png new file mode 100644 index 000000000..123fe9788 Binary files /dev/null and b/assets/jp/retire/TEMPLATE_FLEET_3.png differ diff --git a/assets/jp/retire/TEMPLATE_FLEET_4.png b/assets/jp/retire/TEMPLATE_FLEET_4.png new file mode 100644 index 000000000..fdfa7f780 Binary files /dev/null and b/assets/jp/retire/TEMPLATE_FLEET_4.png differ diff --git a/assets/jp/retire/TEMPLATE_FLEET_5.png b/assets/jp/retire/TEMPLATE_FLEET_5.png new file mode 100644 index 000000000..0402ba319 Binary files /dev/null and b/assets/jp/retire/TEMPLATE_FLEET_5.png differ diff --git a/assets/jp/retire/TEMPLATE_FLEET_6.png b/assets/jp/retire/TEMPLATE_FLEET_6.png new file mode 100644 index 000000000..6c82d6b33 Binary files /dev/null and b/assets/jp/retire/TEMPLATE_FLEET_6.png differ diff --git a/assets/jp/retire/TEMPLATE_IN_BATTLE.png b/assets/jp/retire/TEMPLATE_IN_BATTLE.png new file mode 100644 index 000000000..ef2566a4e Binary files /dev/null and b/assets/jp/retire/TEMPLATE_IN_BATTLE.png differ diff --git a/assets/jp/retire/TEMPLATE_IN_COMMISSION.png b/assets/jp/retire/TEMPLATE_IN_COMMISSION.png new file mode 100644 index 000000000..c41ff9b1a Binary files /dev/null and b/assets/jp/retire/TEMPLATE_IN_COMMISSION.png differ diff --git a/assets/jp/shop/SHOP_VOUCHER.png b/assets/jp/shop/SHOP_VOUCHER.png new file mode 100644 index 000000000..fed0fa51f Binary files /dev/null and b/assets/jp/shop/SHOP_VOUCHER.png differ diff --git a/assets/jp/shop/VOUCHER_SHOP_SCROLL_AREA.png b/assets/jp/shop/VOUCHER_SHOP_SCROLL_AREA.png new file mode 100644 index 000000000..314675bcd Binary files /dev/null and b/assets/jp/shop/VOUCHER_SHOP_SCROLL_AREA.png differ diff --git a/assets/shop/cost/Voucher.png b/assets/shop/cost/Voucher.png new file mode 100644 index 000000000..3c7ae2315 Binary files /dev/null and b/assets/shop/cost/Voucher.png differ diff --git a/assets/shop/merit/Albacore.png b/assets/shop/merit/Albacore.png new file mode 100644 index 000000000..65c91b5d4 Binary files /dev/null and b/assets/shop/merit/Albacore.png differ diff --git a/assets/shop/merit/Bataan.png b/assets/shop/merit/Bataan.png new file mode 100644 index 000000000..50547d09d Binary files /dev/null and b/assets/shop/merit/Bataan.png differ diff --git a/assets/shop/merit/Bluegill.png b/assets/shop/merit/Bluegill.png new file mode 100644 index 000000000..20d3ac7d6 Binary files /dev/null and b/assets/shop/merit/Bluegill.png differ diff --git a/assets/shop/merit/Carabiniere.png b/assets/shop/merit/Carabiniere.png new file mode 100644 index 000000000..2b46e5373 Binary files /dev/null and b/assets/shop/merit/Carabiniere.png differ diff --git a/assets/shop/merit/Casablanca.png b/assets/shop/merit/Casablanca.png new file mode 100644 index 000000000..a0601a0bf Binary files /dev/null and b/assets/shop/merit/Casablanca.png differ diff --git a/assets/shop/merit/ConteDiCavour.png b/assets/shop/merit/ConteDiCavour.png new file mode 100644 index 000000000..06a1a3baa Binary files /dev/null and b/assets/shop/merit/ConteDiCavour.png differ diff --git a/assets/shop/merit/DukeOfYork.png b/assets/shop/merit/DukeOfYork.png new file mode 100644 index 000000000..25000247c Binary files /dev/null and b/assets/shop/merit/DukeOfYork.png differ diff --git a/assets/shop/merit/Echo.png b/assets/shop/merit/Echo.png new file mode 100644 index 000000000..823e29e98 Binary files /dev/null and b/assets/shop/merit/Echo.png differ diff --git a/assets/shop/merit/Eldridge.png b/assets/shop/merit/Eldridge.png new file mode 100644 index 000000000..421a56ad6 Binary files /dev/null and b/assets/shop/merit/Eldridge.png differ diff --git a/assets/shop/merit/Grenville.png b/assets/shop/merit/Grenville.png new file mode 100644 index 000000000..59dd4cc72 Binary files /dev/null and b/assets/shop/merit/Grenville.png differ diff --git a/assets/shop/merit/Hibiki.png b/assets/shop/merit/Hibiki.png new file mode 100644 index 000000000..e9c80dd6d Binary files /dev/null and b/assets/shop/merit/Hibiki.png differ diff --git a/assets/shop/merit/Hunter.png b/assets/shop/merit/Hunter.png new file mode 100644 index 000000000..740c31fe1 Binary files /dev/null and b/assets/shop/merit/Hunter.png differ diff --git a/assets/shop/merit/KingGeorgeV.png b/assets/shop/merit/KingGeorgeV.png new file mode 100644 index 000000000..8b68bbe88 Binary files /dev/null and b/assets/shop/merit/KingGeorgeV.png differ diff --git a/assets/shop/merit/Kinu.png b/assets/shop/merit/Kinu.png new file mode 100644 index 000000000..fb77c5d2d Binary files /dev/null and b/assets/shop/merit/Kinu.png differ diff --git a/assets/shop/merit/Kuroshio.png b/assets/shop/merit/Kuroshio.png new file mode 100644 index 000000000..ecf721651 Binary files /dev/null and b/assets/shop/merit/Kuroshio.png differ diff --git a/assets/shop/merit/LeMalinMuse.png b/assets/shop/merit/LeMalinMuse.png new file mode 100644 index 000000000..58b3a94c8 Binary files /dev/null and b/assets/shop/merit/LeMalinMuse.png differ diff --git a/assets/shop/merit/LeTemeraire.png b/assets/shop/merit/LeTemeraire.png new file mode 100644 index 000000000..bc1c19552 Binary files /dev/null and b/assets/shop/merit/LeTemeraire.png differ diff --git a/assets/shop/merit/Littorio.png b/assets/shop/merit/Littorio.png new file mode 100644 index 000000000..f4ff2b9b1 Binary files /dev/null and b/assets/shop/merit/Littorio.png differ diff --git a/assets/shop/merit/Newcastle.png b/assets/shop/merit/Newcastle.png new file mode 100644 index 000000000..974efe29f Binary files /dev/null and b/assets/shop/merit/Newcastle.png differ diff --git a/assets/shop/merit/Oyashio.png b/assets/shop/merit/Oyashio.png new file mode 100644 index 000000000..6575df249 Binary files /dev/null and b/assets/shop/merit/Oyashio.png differ diff --git a/assets/shop/merit/Quincy.png b/assets/shop/merit/Quincy.png new file mode 100644 index 000000000..eb5210583 Binary files /dev/null and b/assets/shop/merit/Quincy.png differ diff --git a/assets/shop/merit/Ryuujou.png b/assets/shop/merit/Ryuujou.png new file mode 100644 index 000000000..357ad3f65 Binary files /dev/null and b/assets/shop/merit/Ryuujou.png differ diff --git a/assets/shop/merit/SanJuan.png b/assets/shop/merit/SanJuan.png new file mode 100644 index 000000000..045f63b48 Binary files /dev/null and b/assets/shop/merit/SanJuan.png differ diff --git a/assets/shop/merit/SheffieldMuse.png b/assets/shop/merit/SheffieldMuse.png new file mode 100644 index 000000000..559712f49 Binary files /dev/null and b/assets/shop/merit/SheffieldMuse.png differ diff --git a/assets/shop/merit/Trento.png b/assets/shop/merit/Trento.png new file mode 100644 index 000000000..04ade0bcd Binary files /dev/null and b/assets/shop/merit/Trento.png differ diff --git a/assets/shop/merit/Vincennes.png b/assets/shop/merit/Vincennes.png new file mode 100644 index 000000000..14b2474e7 Binary files /dev/null and b/assets/shop/merit/Vincennes.png differ diff --git a/assets/shop/merit/Z26.png b/assets/shop/merit/Z26.png new file mode 100644 index 000000000..8d3f7422d Binary files /dev/null and b/assets/shop/merit/Z26.png differ diff --git a/assets/shop/merit/Z28.png b/assets/shop/merit/Z28.png new file mode 100644 index 000000000..011e9321f Binary files /dev/null and b/assets/shop/merit/Z28.png differ diff --git a/assets/shop/merit/Z36.png b/assets/shop/merit/Z36.png new file mode 100644 index 000000000..2d434b48e Binary files /dev/null and b/assets/shop/merit/Z36.png differ diff --git a/assets/shop/voucher/BookBlueT4.png b/assets/shop/voucher/BookBlueT4.png new file mode 100644 index 000000000..a217fb90a Binary files /dev/null and b/assets/shop/voucher/BookBlueT4.png differ diff --git a/assets/shop/voucher/BookRedT4.png b/assets/shop/voucher/BookRedT4.png new file mode 100644 index 000000000..0efc2b970 Binary files /dev/null and b/assets/shop/voucher/BookRedT4.png differ diff --git a/assets/shop/voucher/BookYellowT4.png b/assets/shop/voucher/BookYellowT4.png new file mode 100644 index 000000000..d24021977 Binary files /dev/null and b/assets/shop/voucher/BookYellowT4.png differ diff --git a/assets/shop/voucher/Coin.png b/assets/shop/voucher/Coin.png new file mode 100644 index 000000000..a71778068 Binary files /dev/null and b/assets/shop/voucher/Coin.png differ diff --git a/assets/shop/voucher/HECombatPlan.png b/assets/shop/voucher/HECombatPlan.png new file mode 100644 index 000000000..3d6f05fd2 Binary files /dev/null and b/assets/shop/voucher/HECombatPlan.png differ diff --git a/assets/shop/voucher/LoggerAbyssalT5.png b/assets/shop/voucher/LoggerAbyssalT5.png new file mode 100644 index 000000000..c59c1b652 Binary files /dev/null and b/assets/shop/voucher/LoggerAbyssalT5.png differ diff --git a/assets/shop/voucher/LoggerAbyssalT6.png b/assets/shop/voucher/LoggerAbyssalT6.png new file mode 100644 index 000000000..fa30893b9 Binary files /dev/null and b/assets/shop/voucher/LoggerAbyssalT6.png differ diff --git a/assets/shop/voucher/LoggerArchiveT2.png b/assets/shop/voucher/LoggerArchiveT2.png new file mode 100644 index 000000000..e24d90ca8 Binary files /dev/null and b/assets/shop/voucher/LoggerArchiveT2.png differ diff --git a/assets/shop/voucher/LoggerArchiveT3.png b/assets/shop/voucher/LoggerArchiveT3.png new file mode 100644 index 000000000..6cb729a4a Binary files /dev/null and b/assets/shop/voucher/LoggerArchiveT3.png differ diff --git a/assets/shop/voucher/LoggerObscureT5.png b/assets/shop/voucher/LoggerObscureT5.png new file mode 100644 index 000000000..6476f565a Binary files /dev/null and b/assets/shop/voucher/LoggerObscureT5.png differ diff --git a/assets/shop/voucher/LoggerObscureT6.png b/assets/shop/voucher/LoggerObscureT6.png new file mode 100644 index 000000000..6599bf894 Binary files /dev/null and b/assets/shop/voucher/LoggerObscureT6.png differ diff --git a/assets/shop/voucher/TuningCombatT2.png b/assets/shop/voucher/TuningCombatT2.png new file mode 100644 index 000000000..157b8b7ff Binary files /dev/null and b/assets/shop/voucher/TuningCombatT2.png differ diff --git a/assets/shop/voucher/TuningOffenseT2.png b/assets/shop/voucher/TuningOffenseT2.png new file mode 100644 index 000000000..dbb6bebab Binary files /dev/null and b/assets/shop/voucher/TuningOffenseT2.png differ diff --git a/assets/shop/voucher/TuningSurvivalT2.png b/assets/shop/voucher/TuningSurvivalT2.png new file mode 100644 index 000000000..03b886965 Binary files /dev/null and b/assets/shop/voucher/TuningSurvivalT2.png differ diff --git a/assets/tw/shop/SHOP_BUY_CONFIRM_AMOUNT.png b/assets/tw/shop/SHOP_BUY_CONFIRM_AMOUNT.png index d9822ef88..8e94524f9 100644 Binary files a/assets/tw/shop/SHOP_BUY_CONFIRM_AMOUNT.png and b/assets/tw/shop/SHOP_BUY_CONFIRM_AMOUNT.png differ diff --git a/assets/tw/shop/SHOP_VOUCHER.png b/assets/tw/shop/SHOP_VOUCHER.png new file mode 100644 index 000000000..fed0fa51f Binary files /dev/null and b/assets/tw/shop/SHOP_VOUCHER.png differ diff --git a/assets/tw/shop/VOUCHER_SHOP_SCROLL_AREA.png b/assets/tw/shop/VOUCHER_SHOP_SCROLL_AREA.png new file mode 100644 index 000000000..314675bcd Binary files /dev/null and b/assets/tw/shop/VOUCHER_SHOP_SCROLL_AREA.png differ diff --git a/bin/DroidCast/DroidCast-debug-1.1.0.apk b/bin/DroidCast/DroidCast-debug-1.1.0.apk new file mode 100644 index 000000000..a53654fc8 Binary files /dev/null and b/bin/DroidCast/DroidCast-debug-1.1.0.apk differ diff --git a/campaign/Readme.md b/campaign/Readme.md index 826e4e519..d5eae4897 100644 --- a/campaign/Readme.md +++ b/campaign/Readme.md @@ -29,6 +29,8 @@ To add a new event, add a new row in here, and run `python -m module.config.conf | 20220721 | war archives 20210624 cn | Swirling Cherry Blossoms | 浮樱影华 | Swirling Cherry Blossoms | 翳りし満ちる影の華 | - | | 20220901 | war archives 20200806 cn | The Enigma and the Shark | 最重要的宝物 | The Enigma and the Shark | 鉄血鮫とエニグマ | - | | 20221013 | war archives 20201029 cn | Universe in Unison | 激唱的UNIVERSE | Universe in Unison | 激唱のユニバース | - | +| 20221117 | war archives 20200903 cn | Stars of the Shimmering Fjord | 峡湾间的星辰 | Stars of the Shimmering Fjord | 輝ける峡湾の星 | - | +| 20221117 | war archives 20210819 cn | Microlayer Medley | 微层混合 | Microlayer Medley | 闇靄払う銀翼 | - | | 20200227 | event 20200227 cn | Northern Overture | 北境序曲 | Northern Overture | 凍絶の北海 | - | | 20200312 | event 20200312 cn | The Solomon Ranger | 复刻斯图尔特的硝烟 | The Solomon Ranger Rerun | 南洋に靡く硝煙(復刻) | - | | 20200326 | event 20200326 cn | Microlayer Medley | 微层混合 | Microlayer Medley | 闇靄払う銀翼 | - | diff --git a/campaign/war_archives_20200903_cn/campaign_base.py b/campaign/war_archives_20200903_cn/campaign_base.py new file mode 100644 index 000000000..11419eac9 --- /dev/null +++ b/campaign/war_archives_20200903_cn/campaign_base.py @@ -0,0 +1,10 @@ +from ..campaign_war_archives.campaign_base import CampaignBase as CampaignBase_ +from module.exception import CampaignNameError +from module.logger import logger + + +class CampaignBase(CampaignBase_): + + STAGE_INCREASE = [ + 'SP0 > SP1 > SP2 > SP3', + ] diff --git a/campaign/war_archives_20200903_cn/sp0.py b/campaign/war_archives_20200903_cn/sp0.py new file mode 100644 index 000000000..fc746753b --- /dev/null +++ b/campaign/war_archives_20200903_cn/sp0.py @@ -0,0 +1,65 @@ +from .campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +MAP = CampaignMap('SP0') +MAP.shape = 'J6' +MAP.camera_data = ['D2', 'D4', 'G2', 'G4'] +MAP.camera_data_spawn_point = ['D2', 'D4'] +MAP.map_data = """ + -- -- ++ ++ ++ ++ -- -- ++ ++ + -- ++ -- -- -- ++ -- -- -- ++ + SP -- -- -- -- -- ++ -- -- -- + SP -- -- -- -- -- -- -- -- MB + -- -- -- -- ++ -- -- ++ -- -- + -- -- -- ++ ++ -- ++ ++ ++ -- +""" +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 +""" +MAP.land_based_data = [['D6', 'up'], ['H5', 'up'], ['F2', 'down'], ['C1', 'down']] +MAP.spawn_data = [ + {'battle': 0, 'boss': 1}, + # {'battle': 1, 'enemy': 2}, + # {'battle': 2, 'enemy': 1}, + # {'battle': 3, 'enemy': 1, 'mystery': 1}, + # {'battle': 4, 'enemy': 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, \ + = MAP.flatten() + +mechanism = SelectedGrids([C6, E2, G5]) + + +class Config: + # ===== Start of generated config ===== + MAP_HAS_MAP_STORY = False + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_LAND_BASED = True + # ===== End of generated config ===== + + STAR_REQUIRE_1 = 0 + STAR_REQUIRE_2 = 0 + STAR_REQUIRE_3 = 0 + MAP_IS_ONE_TIME_STAGE = True + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + self.clear_mechanism(mechanism) + + return self.clear_boss() diff --git a/campaign/war_archives_20200903_cn/sp1.py b/campaign/war_archives_20200903_cn/sp1.py new file mode 100644 index 000000000..07c22d0bd --- /dev/null +++ b/campaign/war_archives_20200903_cn/sp1.py @@ -0,0 +1,85 @@ +from .campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +MAP = CampaignMap('SP1') +MAP.shape = 'K7' +MAP.camera_data = ['D3', 'D5', 'H3', 'H5'] +MAP.camera_data_spawn_point = ['D2'] +MAP.map_data = """ + ++ ++ ++ -- MS -- -- -- ME ++ -- + -- ME -- ++ -- ++ ++ -- ME ++ MB + SP -- ME -- -- ME ++ -- Me ++ Me + SP -- __ -- -- ++ ME -- ME -- -- + ME -- -- -- ME -- -- -- -- -- -- + ME -- -- ++ -- -- -- -- -- -- -- + -- -- -- ++ -- -- -- ++ ++ ++ ++ +""" +MAP.map_data_loop = """ + ++ ++ ++ -- MS -- -- -- ME ++ -- + -- ME -- -- -- ++ ++ -- ME ++ MB + SP -- ME -- -- ME ++ -- Me -- Me + SP -- __ -- -- -- 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 55 50 50 50 50 30 + 50 50 50 50 50 50 50 50 50 50 40 + 50 50 50 50 50 50 50 50 50 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.land_based_data = [['H7', 'up'], ['F4', 'down'], ['J3', 'down'], ['D2', 'down']] +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, '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, \ + = MAP.flatten() + +road_main = RoadGrids([K3]) + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['Z18'] + MOVABLE_ENEMY_TURN = (3,) + 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_LAND_BASED = True + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + + self.clear_mechanism() + + if self.clear_roadblocks([road_main]): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/war_archives_20200903_cn/sp2.py b/campaign/war_archives_20200903_cn/sp2.py new file mode 100644 index 000000000..06b68f89c --- /dev/null +++ b/campaign/war_archives_20200903_cn/sp2.py @@ -0,0 +1,95 @@ +from .campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .sp1 import Config as ConfigBase + +MAP = CampaignMap('SP2') +MAP.shape = 'K7' +MAP.camera_data = ['D3', 'D5', 'H3', 'H5'] +MAP.camera_data_spawn_point = ['D2', 'D5'] +MAP.map_data = """ + ++ ++ ++ -- -- -- -- ME ++ ++ -- + ME -- ++ -- -- ++ -- -- MS ++ -- + SP -- -- MS -- ++ -- -- -- Me -- + -- ME -- ME ++ ++ ME __ -- -- ++ + SP -- -- ME ++ ++ ME -- ME Me -- + ME ++ ++ -- -- -- -- -- ++ ME MB + -- ++ -- ME ME -- -- ME ++ ++ ++ +""" +MAP.map_data_loop = """ + ++ ++ ++ -- -- -- -- ME ++ ++ -- + ME -- -- -- -- ++ -- -- MS ++ -- + SP -- -- MS -- -- -- -- -- Me -- + -- ME -- ME ++ ++ ME __ -- -- ++ + SP -- -- ME ++ ++ ME -- ME Me -- + ME ++ -- -- -- -- -- -- -- ME MB + -- ++ -- 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 40 30 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 +""" +MAP.land_based_data = [['I6', 'up'], ['C6', 'right'], ['F3', 'right'], ['C2', 'down']] +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 2}, + # {'battle': 2, 'enemy': 1, 'mystery': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4}, + {'battle': 5, '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, \ + = MAP.flatten() + +road_main = RoadGrids([J5]) + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['Z18'] + MOVABLE_ENEMY_TURN = (3,) + 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_LAND_BASED = True + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if not self.config.MAP_HAS_MOVABLE_ENEMY: + self.fleet_2_push_forward() + + if self.clear_siren(): + return True + + self.clear_mechanism() + + if self.config.MAP_HAS_MOVABLE_ENEMY: + self.fleet_2_push_forward() + + if self.clear_roadblocks([road_main]): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/war_archives_20200903_cn/sp3.py b/campaign/war_archives_20200903_cn/sp3.py new file mode 100644 index 000000000..a1a29af82 --- /dev/null +++ b/campaign/war_archives_20200903_cn/sp3.py @@ -0,0 +1,95 @@ +from .campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .sp1 import Config as ConfigBase + +MAP = CampaignMap('SP3') +MAP.shape = 'K7' +MAP.camera_data = ['D2', 'D5', 'H2', 'H5'] +MAP.camera_data_spawn_point = ['D2'] +MAP.map_data = """ + ME -- -- MS -- ME ME -- ME ME -- + -- -- ++ -- -- -- ++ -- ME ME ME + SP -- ++ ++ ME -- ++ -- ++ ++ ++ + SP -- ++ ++ ME -- -- -- Me Me MB + -- -- -- ++ ME ME -- -- ++ ++ ++ + ME ME -- __ -- -- -- -- -- ME -- + ME ME ++ ME ME ME -- ++ -- MS ME +""" +MAP.map_data_loop = """ + ME -- -- MS -- ME ME -- ME ME -- + -- -- -- -- -- -- ++ -- ME ME ME + SP -- ++ ++ ME -- -- -- ++ ++ ++ + SP -- ++ ++ ME -- -- -- Me Me MB + -- -- -- -- ME ME -- -- ++ ++ ++ + ME ME -- __ -- -- -- -- -- ME -- + ME ME ++ ME ME ME -- -- -- MS ME +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 40 30 50 + 50 50 50 50 50 50 50 50 50 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.land_based_data = [['H7', 'up'], ['D5', 'left'], ['G3', 'down'], ['C2', 'right']] +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 2}, + {'battle': 2, 'enemy': 1}, + # {'battle': 3, 'enemy': 1, 'mystery': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1}, + {'battle': 5, '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, \ + = MAP.flatten() + +road_main = RoadGrids([I4, J4]) + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['Z18'] + MOVABLE_ENEMY_TURN = (3,) + 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_LAND_BASED = True + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if not self.config.MAP_HAS_MOVABLE_ENEMY: + self.fleet_2_push_forward() + + if self.clear_siren(): + return True + + self.clear_mechanism() + + if self.config.MAP_HAS_MOVABLE_ENEMY: + self.fleet_2_push_forward() + + if self.clear_roadblocks([road_main]): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/war_archives_20210819_cn/a1.py b/campaign/war_archives_20210819_cn/a1.py new file mode 100644 index 000000000..09036b04c --- /dev/null +++ b/campaign/war_archives_20210819_cn/a1.py @@ -0,0 +1,83 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +MAP = CampaignMap('A1') +MAP.shape = 'F7' +MAP.camera_data = ['C2', 'C5'] +MAP.camera_data_spawn_point = ['C5', 'C2'] +MAP.map_data = """ + -- -- ME -- -- ++ + ME ++ ++ ME -- ME + -- ++ MS -- SP -- + MB MB __ -- -- ME + ME MB MS -- SP -- + -- ++ 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 +""" +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, \ +A2, B2, C2, D2, E2, F2, \ +A3, B3, C3, D3, E3, F3, \ +A4, B4, C4, D4, E4, F4, \ +A5, B5, C5, D5, E5, F5, \ +A6, B6, C6, D6, E6, F6, \ +A7, B7, C7, D7, E7, F7, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['DD'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + DETECTION_BACKEND = 'perspective' + COINCIDENT_POINT_ENCOURAGE_DISTANCE = 5 + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (50, 255 - 80), + 'width': (0, 7), + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 80, 255), + 'prominence': 10, + 'distance': 50, + # 'width': (0, 7), + 'wlen': 1000 + } + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + + return self.battle_default() + + def battle_3(self): + return self.clear_boss() diff --git a/campaign/war_archives_20210819_cn/a2.py b/campaign/war_archives_20210819_cn/a2.py new file mode 100644 index 000000000..65e70d820 --- /dev/null +++ b/campaign/war_archives_20210819_cn/a2.py @@ -0,0 +1,72 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .a1 import Config as ConfigBase + +MAP = CampaignMap('A2') +MAP.shape = 'F8' +MAP.camera_data = ['C2', 'C6'] +MAP.camera_data_spawn_point = ['C2'] +MAP.map_data = """ + -- ME -- ME Me ME + -- ME -- -- MS -- + ++ ++ ++ ME -- -- + SP -- -- -- -- SP + ME -- -- -- ++ ++ + -- MS ME __ ++ ++ + Me -- ++ -- -- MB + -- Me -- ME MB MB +""" +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 +""" +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, \ +A2, B2, C2, D2, E2, F2, \ +A3, B3, C3, D3, E3, F3, \ +A4, B4, C4, D4, E4, F4, \ +A5, B5, C5, D5, E5, F5, \ +A6, B6, C6, D6, E6, F6, \ +A7, B7, C7, D7, E7, F7, \ +A8, B8, C8, D8, E8, F8, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['CL'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/war_archives_20210819_cn/a3.py b/campaign/war_archives_20210819_cn/a3.py new file mode 100644 index 000000000..328f408e5 --- /dev/null +++ b/campaign/war_archives_20210819_cn/a3.py @@ -0,0 +1,69 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .a1 import Config as ConfigBase + +MAP = CampaignMap('A3') +MAP.shape = 'G7' +MAP.camera_data = ['D2', 'D5'] +MAP.camera_data_spawn_point = ['D5'] +MAP.map_data = """ + ++ ++ ME Me ME ++ -- + ++ MS -- ME -- MS ++ + ME -- -- __ -- -- ME + Me -- SP MB SP -- Me + ME -- -- MB -- -- ME + -- MS -- MB ++ ++ ++ + ++ -- 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 +""" +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, \ +A2, B2, C2, D2, E2, F2, G2, \ +A3, B3, C3, D3, E3, F3, G3, \ +A4, B4, C4, D4, E4, F4, G4, \ +A5, B5, C5, D5, E5, F5, G5, \ +A6, B6, C6, D6, E6, F6, G6, \ +A7, B7, C7, D7, E7, F7, G7, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['CA'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/war_archives_20210819_cn/b1.py b/campaign/war_archives_20210819_cn/b1.py new file mode 100644 index 000000000..4c113fcb6 --- /dev/null +++ b/campaign/war_archives_20210819_cn/b1.py @@ -0,0 +1,86 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +MAP = CampaignMap('B1') +MAP.shape = 'H7' +MAP.camera_data = ['D2', 'D5', 'E2', 'E5'] +MAP.camera_data_spawn_point = ['E2'] +MAP.map_data = """ + -- Me -- ME ++ ++ SP SP + Me -- MS -- ME -- -- SP + ++ ME -- -- -- -- -- -- + ++ ME -- -- MS -- ME ++ + Me -- __ ME ++ -- -- -- + MB -- -- MS ++ -- ME ME + MB MB 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 +""" +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, \ +A2, B2, C2, D2, E2, F2, G2, H2, \ +A3, B3, C3, D3, E3, F3, G3, H3, \ +A4, B4, C4, D4, E4, F4, G4, H4, \ +A5, B5, C5, D5, E5, F5, G5, H5, \ +A6, B6, C6, D6, E6, F6, G6, H6, \ +A7, B7, C7, D7, E7, F7, G7, H7, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['SS', 'CL'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + DETECTION_BACKEND = 'perspective' + INTERNAL_LINES_HOUGHLINES_THRESHOLD = 40 + EDGE_LINES_HOUGHLINES_THRESHOLD = 40 + COINCIDENT_POINT_ENCOURAGE_DISTANCE = 5 + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (50, 255 - 80), + 'width': (0, 7), + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 80, 255), + 'prominence': 10, + 'distance': 50, + # 'width': (0, 7), + 'wlen': 1000 + } + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/war_archives_20210819_cn/b2.py b/campaign/war_archives_20210819_cn/b2.py new file mode 100644 index 000000000..a12c97577 --- /dev/null +++ b/campaign/war_archives_20210819_cn/b2.py @@ -0,0 +1,76 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .b1 import Config as ConfigBase + +MAP = CampaignMap('B2') +MAP.shape = 'F9' +MAP.camera_data = ['C2', 'C6', 'C7'] +MAP.camera_data_spawn_point = ['C2'] +MAP.map_data = """ + Me ME MS MS ME Me + ME -- -- -- -- ME + ++ ++ -- -- ++ ++ + ++ ++ SP SP Me ++ + MS -- -- -- -- MS + ME -- MB 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 +""" +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, \ +A2, B2, C2, D2, E2, F2, \ +A3, B3, C3, D3, E3, F3, \ +A4, B4, C4, D4, E4, F4, \ +A5, B5, C5, D5, E5, F5, \ +A6, B6, C6, D6, E6, F6, \ +A7, B7, C7, D7, E7, F7, \ +A8, B8, C8, D8, E8, F8, \ +A9, B9, C9, D9, E9, F9, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['SS', 'CL'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/war_archives_20210819_cn/b3.py b/campaign/war_archives_20210819_cn/b3.py new file mode 100644 index 000000000..4f27494bd --- /dev/null +++ b/campaign/war_archives_20210819_cn/b3.py @@ -0,0 +1,76 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .b1 import Config as ConfigBase + +MAP = CampaignMap('B3') +MAP.shape = 'G9' +MAP.camera_data = ['D2', 'D6', 'D7'] +MAP.camera_data_spawn_point = ['D6'] +MAP.map_data = """ + ME -- ME -- ME -- ME + ++ -- -- Me -- -- ++ + ++ -- ++ ++ ++ -- ++ + MS -- MB MB MB -- MS + Me ME -- __ -- ME Me + ++ ++ SP -- SP ++ ++ + ++ ME -- -- -- ME ++ + Me -- -- ME -- -- Me + -- MS ME -- ME MS -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 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, \ +A2, B2, C2, D2, E2, F2, G2, \ +A3, B3, C3, D3, E3, F3, G3, \ +A4, B4, C4, D4, E4, F4, G4, \ +A5, B5, C5, D5, E5, F5, G5, \ +A6, B6, C6, D6, E6, F6, G6, \ +A7, B7, C7, D7, E7, F7, G7, \ +A8, B8, C8, D8, E8, F8, G8, \ +A9, B9, C9, D9, E9, F9, G9, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['SS', 'CA'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/war_archives_20210819_cn/c1.py b/campaign/war_archives_20210819_cn/c1.py new file mode 100644 index 000000000..28e102a69 --- /dev/null +++ b/campaign/war_archives_20210819_cn/c1.py @@ -0,0 +1,89 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +MAP = CampaignMap('C1') +MAP.shape = 'F7' +MAP.camera_data = ['C2', 'C5'] +MAP.camera_data_spawn_point = ['C5', 'C2'] +MAP.map_data = """ + -- -- ME -- -- ++ + ME ++ ++ ME -- ME + -- ++ MS -- SP -- + MB MB __ -- -- ME + ME MB MS -- SP -- + -- ++ 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 +""" +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, \ +A2, B2, C2, D2, E2, F2, \ +A3, B3, C3, D3, E3, F3, \ +A4, B4, C4, D4, E4, F4, \ +A5, B5, C5, D5, E5, F5, \ +A6, B6, C6, D6, E6, F6, \ +A7, B7, C7, D7, E7, F7, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['CL', 'CA'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + DETECTION_BACKEND = 'perspective' + INTERNAL_LINES_HOUGHLINES_THRESHOLD = 40 + EDGE_LINES_HOUGHLINES_THRESHOLD = 40 + COINCIDENT_POINT_ENCOURAGE_DISTANCE = 5 + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (100, 255 - 60), + 'width': (0, 7), + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 60, 255), + 'prominence': 2, + 'distance': 50, + # 'width': (0, 7), + 'wlen': 1000 + } + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_enemy(scale=(2,)): + return True + if self.clear_enemy(scale=(3,)): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/war_archives_20210819_cn/c2.py b/campaign/war_archives_20210819_cn/c2.py new file mode 100644 index 000000000..7b346a216 --- /dev/null +++ b/campaign/war_archives_20210819_cn/c2.py @@ -0,0 +1,78 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .c1 import Config as ConfigBase + +MAP = CampaignMap('C2') +MAP.shape = 'F8' +MAP.camera_data = ['C2', 'C6'] +MAP.camera_data_spawn_point = ['C2'] +MAP.map_data = """ + -- ME -- ME Me ME + -- ME -- -- MS -- + ++ ++ ++ ME -- -- + SP -- -- -- -- SP + ME -- -- -- ++ ++ + -- MS ME __ ++ ++ + Me -- ++ -- -- MB + -- Me -- ME MB MB +""" +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 +""" +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, \ +A2, B2, C2, D2, E2, F2, \ +A3, B3, C3, D3, E3, F3, \ +A4, B4, C4, D4, E4, F4, \ +A5, B5, C5, D5, E5, F5, \ +A6, B6, C6, D6, E6, F6, \ +A7, B7, C7, D7, E7, F7, \ +A8, B8, C8, D8, E8, F8, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['CL', 'CA'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_enemy(scale=(1,)): + return True + if self.clear_enemy(scale=(2,), genre=['light', 'main', 'enemy', 'carrier']): + return True + if self.clear_enemy(genre=['light', 'main']): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/war_archives_20210819_cn/c3.py b/campaign/war_archives_20210819_cn/c3.py new file mode 100644 index 000000000..6279ce432 --- /dev/null +++ b/campaign/war_archives_20210819_cn/c3.py @@ -0,0 +1,74 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .c1 import Config as ConfigBase + +MAP = CampaignMap('C3') +MAP.shape = 'G7' +MAP.camera_data = ['D2', 'D5'] +MAP.camera_data_spawn_point = ['D5'] +MAP.map_data = """ + ++ ++ ME Me ME ++ -- + ++ MS -- ME -- MS ++ + ME -- -- __ -- -- ME + Me -- SP MB SP -- Me + ME -- -- MB -- -- ME + -- MS -- MB ++ ++ ++ + ++ -- 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 +""" +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, \ +A2, B2, C2, D2, E2, F2, G2, \ +A3, B3, C3, D3, E3, F3, G3, \ +A4, B4, C4, D4, E4, F4, G4, \ +A5, B5, C5, D5, E5, F5, G5, \ +A6, B6, C6, D6, E6, F6, G6, \ +A7, B7, C7, D7, E7, F7, G7, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['CLpurple', 'CApurple', 'BBpurple'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_enemy(scale=(2,)): + return True + if self.clear_enemy(scale=(3,)): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/war_archives_20210819_cn/d1.py b/campaign/war_archives_20210819_cn/d1.py new file mode 100644 index 000000000..bebe0eeb9 --- /dev/null +++ b/campaign/war_archives_20210819_cn/d1.py @@ -0,0 +1,90 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +MAP = CampaignMap('D1') +MAP.shape = 'H7' +MAP.camera_data = ['D2', 'D5', 'E2', 'E5'] +MAP.camera_data_spawn_point = ['E2'] +MAP.map_data = """ + -- Me -- ME ++ ++ SP SP + Me -- MS -- ME -- -- SP + ++ ME -- -- -- -- -- -- + ++ ME -- -- MS -- ME ++ + Me -- __ ME ++ -- -- -- + MB -- -- MS ++ -- ME ME + MB MB 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 +""" +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, \ +A2, B2, C2, D2, E2, F2, G2, H2, \ +A3, B3, C3, D3, E3, F3, G3, H3, \ +A4, B4, C4, D4, E4, F4, G4, H4, \ +A5, B5, C5, D5, E5, F5, G5, H5, \ +A6, B6, C6, D6, E6, F6, G6, H6, \ +A7, B7, C7, D7, E7, F7, G7, H7, \ + = MAP.flatten() + + +class Config: + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['SS', 'CA', 'BB'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + DETECTION_BACKEND = 'perspective' + INTERNAL_LINES_HOUGHLINES_THRESHOLD = 40 + EDGE_LINES_HOUGHLINES_THRESHOLD = 40 + COINCIDENT_POINT_ENCOURAGE_DISTANCE = 5 + INTERNAL_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (100, 255 - 60), + 'width': (0, 7), + 'prominence': 10, + 'distance': 35, + } + EDGE_LINES_FIND_PEAKS_PARAMETERS = { + 'height': (255 - 60, 255), + 'prominence': 2, + 'distance': 50, + # 'width': (0, 7), + 'wlen': 1000 + } + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_enemy(scale=(2,)): + return True + if self.clear_enemy(scale=(3,)): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/war_archives_20210819_cn/d2.py b/campaign/war_archives_20210819_cn/d2.py new file mode 100644 index 000000000..776216fb0 --- /dev/null +++ b/campaign/war_archives_20210819_cn/d2.py @@ -0,0 +1,91 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .d1 import Config as ConfigBase + +MAP = CampaignMap('D2') +MAP.shape = 'F9' +MAP.camera_data = ['C2', 'C6', 'C7'] +MAP.camera_data_spawn_point = ['C2'] +MAP.map_data = """ + Me ME MS MS ME Me + ME -- -- -- -- ME + ++ ++ -- -- ++ ++ + ++ ++ SP SP Me ++ + MS -- -- -- -- MS + ME -- MB 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 +""" +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, \ +A2, B2, C2, D2, E2, F2, \ +A3, B3, C3, D3, E3, F3, \ +A4, B4, C4, D4, E4, F4, \ +A5, B5, C5, D5, E5, F5, \ +A6, B6, C6, D6, E6, F6, \ +A7, B7, C7, D7, E7, F7, \ +A8, B8, C8, D8, E8, F8, \ +A9, B9, C9, D9, E9, F9, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['SS', 'BB', 'CV'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_enemy(scale=(2,)): + return True + if self.clear_enemy(scale=(3,)): + return True + + return self.battle_default() + + def battle_5(self): + if self.clear_siren(): + return True + if self.clear_enemy(scale=(1,)): + return True + if self.clear_enemy(scale=(2,)): + return True + if self.clear_enemy(scale=(3,)): + return True + + def battle_6(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/war_archives_20210819_cn/d3.py b/campaign/war_archives_20210819_cn/d3.py new file mode 100644 index 000000000..882da73ba --- /dev/null +++ b/campaign/war_archives_20210819_cn/d3.py @@ -0,0 +1,91 @@ +from ..campaign_war_archives.campaign_base import CampaignBase +from module.logger import logger +from module.map.map_base import CampaignMap +from module.map.map_grids import RoadGrids, SelectedGrids + +from .d1 import Config as ConfigBase + +MAP = CampaignMap('D3') +MAP.shape = 'G9' +MAP.camera_data = ['D2', 'D6', 'D7'] +MAP.camera_data_spawn_point = ['D6'] +MAP.map_data = """ + ME -- ME -- ME -- ME + ++ -- -- Me -- -- ++ + ++ -- ++ ++ ++ -- ++ + MS -- MB MB MB -- MS + Me ME -- __ -- ME Me + ++ ++ SP -- SP ++ ++ + ++ ME -- -- -- ME ++ + Me -- -- ME -- -- Me + -- MS ME -- ME MS -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 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, \ +A2, B2, C2, D2, E2, F2, G2, \ +A3, B3, C3, D3, E3, F3, G3, \ +A4, B4, C4, D4, E4, F4, G4, \ +A5, B5, C5, D5, E5, F5, G5, \ +A6, B6, C6, D6, E6, F6, G6, \ +A7, B7, C7, D7, E7, F7, G7, \ +A8, B8, C8, D8, E8, F8, G8, \ +A9, B9, C9, D9, E9, F9, G9, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['SS', 'BBpurple', 'CVpurple'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + # ===== End of generated config ===== + + +class Campaign(CampaignBase): + MAP = MAP + + def battle_0(self): + if self.clear_siren(): + return True + if self.clear_enemy(scale=(2,)): + return True + if self.clear_enemy(scale=(3,)): + return True + + return self.battle_default() + + def battle_5(self): + if self.clear_siren(): + return True + if self.clear_enemy(scale=(1,)): + return True + if self.clear_enemy(scale=(2,)): + return True + if self.clear_enemy(scale=(3,)): + return True + + def battle_6(self): + return self.fleet_boss.clear_boss() diff --git a/config/deploy.template-AidLux-cn.yaml b/config/deploy.template-AidLux-cn.yaml index acae8880c..c0707076d 100644 --- a/config/deploy.template-AidLux-cn.yaml +++ b/config/deploy.template-AidLux-cn.yaml @@ -16,6 +16,10 @@ Deploy: # [CN user] Use your local http proxy (http://127.0.0.1:{port}) or socks5 proxy (socks5://127.0.0.1:{port}) # [Other] Use null GitProxy: null + # Set SSL Verify + # [In most cases] Use true + # [Other] Use false to when connected to an untrusted network + SSLVerify: true # Update Alas at startup # [In most cases] Use true AutoUpdate: true diff --git a/config/deploy.template-AidLux.yaml b/config/deploy.template-AidLux.yaml index 86c4b91aa..dbd3f34b8 100644 --- a/config/deploy.template-AidLux.yaml +++ b/config/deploy.template-AidLux.yaml @@ -16,6 +16,10 @@ Deploy: # [CN user] Use your local http proxy (http://127.0.0.1:{port}) or socks5 proxy (socks5://127.0.0.1:{port}) # [Other] Use null GitProxy: null + # Set SSL Verify + # [In most cases] Use true + # [Other] Use false to when connected to an untrusted network + SSLVerify: true # Update Alas at startup # [In most cases] Use true AutoUpdate: true diff --git a/config/deploy.template-cn.yaml b/config/deploy.template-cn.yaml index f7f709dff..2c2fb24d5 100644 --- a/config/deploy.template-cn.yaml +++ b/config/deploy.template-cn.yaml @@ -16,6 +16,10 @@ Deploy: # [CN user] Use your local http proxy (http://127.0.0.1:{port}) or socks5 proxy (socks5://127.0.0.1:{port}) # [Other] Use null GitProxy: null + # Set SSL Verify + # [In most cases] Use true + # [Other] Use false to when connected to an untrusted network + SSLVerify: true # Update Alas at startup # [In most cases] Use true AutoUpdate: true diff --git a/config/deploy.template-docker-cn.yaml b/config/deploy.template-docker-cn.yaml index 0cb8d5c14..a30cd957b 100644 --- a/config/deploy.template-docker-cn.yaml +++ b/config/deploy.template-docker-cn.yaml @@ -16,6 +16,10 @@ Deploy: # [CN user] Use your local http proxy (http://127.0.0.1:{port}) or socks5 proxy (socks5://127.0.0.1:{port}) # [Other] Use null GitProxy: null + # Set SSL Verify + # [In most cases] Use true + # [Other] Use false to when connected to an untrusted network + SSLVerify: true # Update Alas at startup # [In most cases] Use true AutoUpdate: true diff --git a/config/deploy.template-docker.yaml b/config/deploy.template-docker.yaml index d09e1d52f..e1707fb80 100644 --- a/config/deploy.template-docker.yaml +++ b/config/deploy.template-docker.yaml @@ -16,6 +16,10 @@ Deploy: # [CN user] Use your local http proxy (http://127.0.0.1:{port}) or socks5 proxy (socks5://127.0.0.1:{port}) # [Other] Use null GitProxy: null + # Set SSL Verify + # [In most cases] Use true + # [Other] Use false to when connected to an untrusted network + SSLVerify: true # Update Alas at startup # [In most cases] Use true AutoUpdate: true diff --git a/config/deploy.template.yaml b/config/deploy.template.yaml index 81e05f31e..829e1013b 100644 --- a/config/deploy.template.yaml +++ b/config/deploy.template.yaml @@ -16,6 +16,10 @@ Deploy: # [CN user] Use your local http proxy (http://127.0.0.1:{port}) or socks5 proxy (socks5://127.0.0.1:{port}) # [Other] Use null GitProxy: null + # Set SSL Verify + # [In most cases] Use true + # [Other] Use false to when connected to an untrusted network + SSLVerify: true # Update Alas at startup # [In most cases] Use true AutoUpdate: true diff --git a/config/template.json b/config/template.json index 8aa80a9c5..eec26b9ff 100644 --- a/config/template.json +++ b/config/template.json @@ -10,7 +10,8 @@ "AdbRestart": false }, "RestartEmulator": { - "Enable": false, + "ErrorRestart": false, + "DailyRestart": false, "EmulatorType": "auto" }, "Error": { @@ -35,6 +36,9 @@ "OpsiRecord": "do_not", "MeowfficerBuy": "do_not", "MeowfficerTalent": "do_not" + }, + "Storage": { + "Storage": {} } }, "General": { @@ -49,6 +53,9 @@ "OldRetireR": true, "OldRetireSR": false, "OldRetireSSR": false + }, + "Storage": { + "Storage": {} } }, "Restart": { @@ -59,6 +66,9 @@ "SuccessInterval": 0, "FailureInterval": 0, "ServerUpdate": "00:00" + }, + "Storage": { + "Storage": {} } }, "Main": { @@ -131,6 +141,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "Main2": { @@ -203,6 +216,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "Main3": { @@ -275,6 +291,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "GemsFarming": { @@ -346,6 +365,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "EventGeneral": { @@ -357,6 +379,9 @@ "Enable": false, "CoinLimit": 10000, "TaskCall": "Main" + }, + "Storage": { + "Storage": {} } }, "Event": { @@ -429,6 +454,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "Event2": { @@ -501,6 +529,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "EventA": { @@ -577,6 +608,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "EventB": { @@ -653,6 +687,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "EventC": { @@ -729,6 +766,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "EventD": { @@ -805,6 +845,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "EventSp": { @@ -877,6 +920,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "Raid": { @@ -923,6 +969,9 @@ "Fleet2Control": "prevent_yellow_face", "Fleet2Recover": "not_in_dormitory", "Fleet2Oath": false + }, + "Storage": { + "Storage": {} } }, "RaidDaily": { @@ -968,6 +1017,9 @@ "Fleet2Control": "prevent_yellow_face", "Fleet2Recover": "not_in_dormitory", "Fleet2Oath": false + }, + "Storage": { + "Storage": {} } }, "MaritimeEscort": { @@ -981,6 +1033,9 @@ }, "MaritimeEscort": { "Enable": true + }, + "Storage": { + "Storage": {} } }, "Commission": { @@ -994,7 +1049,11 @@ }, "Commission": { "DoMajorCommission": false, - "CommissionFilter": "DailyEvent\n> Gem-8 > Gem-4 > Gem-2\n> NightDrill-8 > NightDrill-7 > NightDrill-6\n> ExtraDrill-0:20 > ExtraDrill-1 > ExtraDrill-2 > ExtraDrill-2:40 > ExtraDrill-3:20 > ExtraDrill-5:20\n> Box-6 > Box-3 > Box-1\n> DailyCube-0:30 > UrgentCube-1:30 > DailyCube-1:30 > UrgentCube-1:45 > UrgentCube-2:15 > UrgentCube-3\n> Major\n> DailyChip > DailyResource\n> UrgentBook-2:30 > UrgentBook-2 > UrgentBook-1:20 > UrgentBook-1:40\n> Daily-0:20 > Daily-0:30 > Daily-1:00 > Daily-1:30 > Daily-2:00\n> NightOil > NightCube\n> shortest" + "PresetFilter": "cube", + "CustomFilter": "DailyEvent > Gem-4 > Gem-2 > Gem-8 > ExtraCube-0:30\n> UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-3 \n> ExtraDrill-5:20 > ExtraDrill-2 > ExtraDrill-3:20 \n> UrgentCube-2:15 > UrgentCube-4\n> ExtraDrill-1 > UrgentCube-6 > ExtraCube-1:30 \n> ExtraDrill-2:40 > ExtraDrill-0:20 \n> Major > DailyChip > DailyResource\n> ExtraPart-0:30 > ExtraOil-1 > UrgentBox-6 \n> ExtraCube-3 > ExtraPart-1 > UrgentBox-3\n> ExtraCube-4 > ExtraPart-1:30 > ExtraOil-4\n> UrgentBox-1 > ExtraCube-5 > UrgentBox-1\n> ExtraCube-8 > ExtraOil-8\n> UrgentDrill-4 > UrgentDrill-2:40 > UrgentDrill-2 \n> UrgentDrill-1 > UrgentDrill-1:30 > UrgentDrill-1:10\n> Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00\n> shortest" + }, + "Storage": { + "Storage": {} } }, "Tactical": { @@ -1020,6 +1079,9 @@ "AddNewStudent": { "Enable": false, "Favorite": true + }, + "Storage": { + "Storage": {} } }, "Research": { @@ -1038,6 +1100,9 @@ "AllowDelay": true, "PresetFilter": "series_5_blueprint_152", "CustomFilter": "S5-DR0.5 > S5-PRY0.5 > S5-H0.5 > S5-Q0.5 > S5-DR2.5 > 0.5 > S5-G1.5\n> S5-Q1 > S5-DR5 > S5-DR8 > S5-G4 > S5-PRY2.5 > 1 > S5-Q2 > reset\n> S5-G2.5 > S5-PRY5 > S5-PRY8 > 1.5 > 2 > S5-Q4 > 2.5 > 3\n> Q4 > G4 > 4 > 5 > S5-C6 > C6 > 6 > S5-C8 > 8\n> S5-C12 > 12" + }, + "Storage": { + "Storage": {} } }, "Dorm": { @@ -1053,6 +1118,9 @@ "Collect": true, "Feed": true, "FeedFilter": "20000 > 10000 > 5000 > 3000 > 2000 > 1000" + }, + "Storage": { + "Storage": {} } }, "Meowfficer": { @@ -1074,6 +1142,9 @@ "RetainTalentedGold": true, "RetainTalentedPurple": true, "EnhanceIndex": 1 + }, + "Storage": { + "Storage": {} } }, "Guild": { @@ -1097,6 +1168,9 @@ "JoinThreshold": 1, "AttackBoss": true, "BossFleetRecommend": false + }, + "Storage": { + "Storage": {} } }, "Reward": { @@ -1114,6 +1188,9 @@ "CollectExp": true, "CollectMission": true, "CollectWeeklyMission": true + }, + "Storage": { + "Storage": {} } }, "ShopFrequent": { @@ -1130,6 +1207,9 @@ "Refresh": false, "BuySkinBox": false, "Filter": "BookRedT3 > BookYellowT3 > BookBlueT3 > BookRedT2\n> Cube\n> FoodT6 > FoodT5" + }, + "Storage": { + "Storage": {} } }, "ShopOnce": { @@ -1172,6 +1252,9 @@ }, "CoreShop": { "Filter": "Array" + }, + "Storage": { + "Storage": {} } }, "Shipyard": { @@ -1187,6 +1270,9 @@ "ResearchSeries": 1, "ShipIndex": 0, "BuyAmount": 2 + }, + "Storage": { + "Storage": {} } }, "Gacha": { @@ -1203,6 +1289,9 @@ "Amount": 1, "UseTicket": true, "UseDrill": false + }, + "Storage": { + "Storage": {} } }, "Freebies": { @@ -1228,6 +1317,9 @@ }, "SupplyPack": { "Collect": true + }, + "Storage": { + "Storage": {} } }, "Daily": { @@ -1252,6 +1344,9 @@ "SupplyLineDisruption": "second", "ModuleDevelopment": "first", "ModuleDevelopmentFleet": 5 + }, + "Storage": { + "Storage": {} } }, "Hard": { @@ -1266,6 +1361,9 @@ "Hard": { "HardStage": "11-4", "HardFleet": 1 + }, + "Storage": { + "Storage": {} } }, "Exercise": { @@ -1285,6 +1383,9 @@ "LowHpConfirmWait": 0.1, "OpponentRefreshValue": 0, "OpponentRefreshRecord": "2020-01-01 00:00:00" + }, + "Storage": { + "Storage": {} } }, "Sos": { @@ -1360,6 +1461,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "WarArchives": { @@ -1373,7 +1477,7 @@ }, "Campaign": { "Name": "12-4", - "Event": "war_archives_20201029_cn", + "Event": "war_archives_20210819_cn", "Mode": "normal", "UseClearMode": true, "UseFleetLock": true, @@ -1432,6 +1536,9 @@ }, "EnemyPriority": { "EnemyScaleBalanceWeight": "default_mode" + }, + "Storage": { + "Storage": {} } }, "OpsiGeneral": { @@ -1442,6 +1549,9 @@ "RepairThreshold": 0.4, "DoRandomMapEvent": true, "AkashiShopFilter": "ActionPoint > PurpleCoins" + }, + "Storage": { + "Storage": {} } }, "OpsiAshBeacon": { @@ -1461,6 +1571,9 @@ }, "OpsiDossierBeacon": { "Enable": true + }, + "Storage": { + "Storage": {} } }, "OpsiAshAssist": { @@ -1474,6 +1587,9 @@ }, "OpsiAshAssist": { "Tier": 15 + }, + "Storage": { + "Storage": {} } }, "OpsiExplore": { @@ -1493,6 +1609,9 @@ "OpsiFleet": { "Fleet": 1, "Submarine": false + }, + "Storage": { + "Storage": {} } }, "OpsiShop": { @@ -1506,6 +1625,25 @@ }, "OpsiShop": { "BuySupply": true + }, + "Storage": { + "Storage": {} + } + }, + "OpsiVoucher": { + "Scheduler": { + "Enable": false, + "NextRun": "2020-01-01 00:00:00", + "Command": "OpsiVoucher", + "SuccessInterval": 30, + "FailureInterval": 30, + "ServerUpdate": "00:00" + }, + "OpsiVoucher": { + "Filter": "LoggerAbyssal > LoggerObscure > HECombatPlan > Book > Coin" + }, + "Storage": { + "Storage": {} } }, "OpsiDaily": { @@ -1524,6 +1662,9 @@ "OpsiFleet": { "Fleet": 1, "Submarine": false + }, + "Storage": { + "Storage": {} } }, "OpsiObscure": { @@ -1541,6 +1682,9 @@ "OpsiFleet": { "Fleet": 1, "Submarine": false + }, + "Storage": { + "Storage": {} } }, "OpsiAbyssal": { @@ -1550,13 +1694,16 @@ "Command": "OpsiAbyssal", "SuccessInterval": 60, "FailureInterval": 60, - "ServerUpdate": "00:00" + "ServerUpdate": "00:00, 12:00" }, "OpsiAbyssal": { "ForceRun": false }, "OpsiFleetFilter": { "Filter": "Fleet-4 > CallSubmarine > Fleet-2 > Fleet-3 > Fleet-1" + }, + "Storage": { + "Storage": {} } }, "OpsiStronghold": { @@ -1573,6 +1720,9 @@ }, "OpsiFleetFilter": { "Filter": "Fleet-4 > CallSubmarine > Fleet-2 > Fleet-3 > Fleet-1" + }, + "Storage": { + "Storage": {} } }, "OpsiMeowfficerFarming": { @@ -1585,13 +1735,36 @@ "ServerUpdate": "00:00" }, "OpsiMeowfficerFarming": { - "ActionPointPreserve": 500, + "ActionPointPreserve": 1000, "HazardLevel": 5, "TargetZone": 0 }, "OpsiFleet": { "Fleet": 1, "Submarine": false + }, + "Storage": { + "Storage": {} + } + }, + "OpsiHazard1Leveling": { + "Scheduler": { + "Enable": false, + "NextRun": "2020-01-01 00:00:00", + "Command": "OpsiHazard1Leveling", + "SuccessInterval": 30, + "FailureInterval": 60, + "ServerUpdate": "00:00" + }, + "OpsiHazard1Leveling": { + "TargetZone": 0 + }, + "OpsiFleet": { + "Fleet": 1, + "Submarine": false + }, + "Storage": { + "Storage": {} } }, "OpsiCrossMonth": { @@ -1602,17 +1775,26 @@ "SuccessInterval": 0, "FailureInterval": 120, "ServerUpdate": "00:00" + }, + "Storage": { + "Storage": {} } }, "Daemon": { "Daemon": { "EnterMap": true + }, + "Storage": { + "Storage": {} } }, "OpsiDaemon": { "OpsiDaemon": { "RepairShip": true, "SelectEnemy": true + }, + "Storage": { + "Storage": {} } }, "Benchmark": { @@ -1626,16 +1808,25 @@ "Uiautomator2Click": true, "MinitouchClick": true, "HermitClick": false + }, + "Storage": { + "Storage": {} } }, "AzurLaneUncensored": { "AzurLaneUncensored": { "Repository": "https://gitee.com/LmeSzinc/AzurLaneUncensored" + }, + "Storage": { + "Storage": {} } }, "GameManager": { "GameManager": { "AutoRestart": true + }, + "Storage": { + "Storage": {} } } } \ No newline at end of file diff --git a/deploy/adb.py b/deploy/adb.py index 4bed52dd7..719222bfb 100644 --- a/deploy/adb.py +++ b/deploy/adb.py @@ -37,7 +37,7 @@ class AdbManager(DeployConfig): logger.hr('Uiautomator2 Init', 1) try: import adbutils - from uiautomator2.init import Initer + from uiautomator2 import init except ModuleNotFoundError as e: message = str(e) for module in ['apkutils2', 'progress']: @@ -54,14 +54,22 @@ class AdbManager(DeployConfig): del os.environ[k] for device in adbutils.adb.iter_device(): - init = Initer(device, loglevel=logging.DEBUG) - init.set_atx_agent_addr('127.0.0.1:7912') - try: - init.install() - except AssertionError: - logger.info(f'AssertionError when installing uiautomator2 on device {device.serial}') - logger.info('If you are using BlueStacks or LD player or WSA, ' - 'please enable ADB in the settings of your emulator') - exit(1) - init._device.shell(["rm", "/data/local/tmp/minicap"]) - init._device.shell(["rm", "/data/local/tmp/minicap.so"]) + initer = init.Initer(device, loglevel=logging.DEBUG) + initer.set_atx_agent_addr('127.0.0.1:7912') + + for _ in range(2): + try: + initer.install() + break + except AssertionError: + logger.info(f'AssertionError when installing uiautomator2 on device {device.serial}') + logger.info('If you are using BlueStacks or LD player or WSA, ' + 'please enable ADB in the settings of your emulator') + exit(1) + except ConnectionError: + if _ == 1: + raise + init.GITHUB_BASEURL = 'http://tool.appetizer.io/openatx' + + initer._device.shell(["rm", "/data/local/tmp/minicap"]) + initer._device.shell(["rm", "/data/local/tmp/minicap.so"]) diff --git a/deploy/config.py b/deploy/config.py index 727c16850..e510e9004 100644 --- a/deploy/config.py +++ b/deploy/config.py @@ -14,13 +14,14 @@ class ConfigModel: Repository: str = "https://github.com/LmeSzinc/AzurLaneAutoScript" Branch: str = "master" GitExecutable: str = "./toolkit/Git/mingw64/bin/git.exe" - GitProxy: Optional[bool] = None + GitProxy: Optional[str] = None + SSLVerify: bool = False AutoUpdate: bool = True KeepLocalChanges: bool = False # Python PythonExecutable: str = "./toolkit/python.exe" - PypiMirror: Optional[bool] = None + PypiMirror: Optional[str] = None InstallDependencies: bool = True RequirementsFile: str = "requirements.txt" diff --git a/deploy/git.py b/deploy/git.py index c94253048..14d2c9528 100644 --- a/deploy/git.py +++ b/deploy/git.py @@ -18,7 +18,10 @@ class GitManager(DeployConfig): except FileNotFoundError: logger.info(f'File not found: {file}') - def git_repository_init(self, repo, source='origin', branch='master', proxy='', keep_changes=False): + def git_repository_init( + self, repo, source='origin', branch='master', + proxy='', ssl_verify=True, keep_changes=False + ): logger.hr('Git Init', 1) if not self.execute(f'"{self.git}" init', allow_failure=True): self.remove('./.git/config') @@ -34,6 +37,11 @@ class GitManager(DeployConfig): self.execute(f'"{self.git}" config --local --unset http.proxy', allow_failure=True) self.execute(f'"{self.git}" config --local --unset https.proxy', allow_failure=True) + if ssl_verify: + self.execute(f'"{self.git}" config --local http.sslVerify true') + else: + self.execute(f'"{self.git}" config --local http.sslVerify false') + logger.hr('Set Git Repository', 1) if not self.execute(f'"{self.git}" remote set-url {source} {repo}', allow_failure=True): self.execute(f'"{self.git}" remote add {source} {repo}') @@ -78,5 +86,6 @@ class GitManager(DeployConfig): source='origin', branch=self.Branch, proxy=self.GitProxy, + ssl_verify=self.SSLVerify, keep_changes=self.KeepLocalChanges, ) diff --git a/deploy/pip.py b/deploy/pip.py index 6d0a9dc8d..7f0dcfe0d 100644 --- a/deploy/pip.py +++ b/deploy/pip.py @@ -35,9 +35,12 @@ class PipManager(DeployConfig): if self.PypiMirror: mirror = self.PypiMirror arg += ['-i', mirror] - # Trust http mirror - if 'http:' in mirror: + # Trust http mirror or skip ssl verify + if 'http:' in mirror or not self.SSLVerify: arg += ['--trusted-host', urlparse(mirror).hostname] + elif not self.SSLVerify: + arg += ['--trusted-host', 'pypi.org'] + arg += ['--trusted-host', 'files.pythonhosted.org'] # Don't update pip, just leave it. # logger.hr('Update pip', 1) diff --git a/deploy/template b/deploy/template index 8e15d6ecf..2e313e9df 100644 --- a/deploy/template +++ b/deploy/template @@ -16,6 +16,10 @@ Deploy: # [CN user] Use your local http proxy (http://127.0.0.1:{port}) or socks5 proxy (socks5://127.0.0.1:{port}) # [Other] Use null GitProxy: null + # Set SSL Verify + # [In most cases] Use true + # [Other] Use false to when connected to an untrusted network + SSLVerify: true # Update Alas at startup # [In most cases] Use true AutoUpdate: true diff --git a/module/campaign/gems_farming.py b/module/campaign/gems_farming.py index b5df4eb41..3be0072e3 100644 --- a/module/campaign/gems_farming.py +++ b/module/campaign/gems_farming.py @@ -362,4 +362,4 @@ class GemsFarming(CampaignRun, Dock, EquipmentChange): break def server_support_status_fleet_scan(self) -> bool: - return self.config.SERVER in ['cn', 'en'] + return self.config.SERVER in ['cn', 'en', 'jp'] diff --git a/module/campaign/os_run.py b/module/campaign/os_run.py index e244182b4..b01a343f4 100644 --- a/module/campaign/os_run.py +++ b/module/campaign/os_run.py @@ -35,6 +35,18 @@ class OSCampaignRun(OSMapOperation): except ActionPointLimit: self.config.opsi_task_delay(ap_limit=True) + def opsi_voucher(self): + if self.config.SERVER in ['tw']: + logger.info(f'Opsi Voucher is not supported in {self.config.SERVER},' + ' please contact server maintainers') + self.config.task_delay(server_update=True) + return + try: + self.load_campaign() + self.campaign.os_voucher() + except ActionPointLimit: + self.config.opsi_task_delay(ap_limit=True) + def opsi_daily(self): try: self.load_campaign() @@ -54,6 +66,31 @@ class OSCampaignRun(OSMapOperation): logger.info('Just less than 1 day to OpSi reset, delay 2.5 hours') self.config.task_delay(minute=150, server_update=True) + def opsi_hazard1_leveling(self): + if self.config.SERVER in ['en', 'jp', 'tw']: + logger.info(f'Opsi CL1 Leveling is not supported in {self.config.SERVER},' + ' please contact server maintainers') + self.config.task_delay(server_update=True) + return + self.config.override( + OpsiGeneral_AkashiShopFilter='ActionPoint' + ) + self.config.cross_set(keys='OpsiMeowfficerFarming.Scheduler.Enable', value=True) + if self.config.cross_get( + keys='OpsiMeowfficerFarming.OpsiMeowfficerFarming.ActionPointPreserve', + default=0 + ) < 1000: + self.config.cross_set( + keys='OpsiMeowfficerFarming.OpsiMeowfficerFarming.ActionPointPreserve', + value=1000 + ) + + self.load_campaign() + try: + self.campaign.os_hazard1_leveling() + except ActionPointLimit: + self.config.task_delay(server_update=True) + def opsi_obscure(self): try: self.load_campaign() @@ -68,6 +105,13 @@ class OSCampaignRun(OSMapOperation): except ActionPointLimit: self.config.opsi_task_delay(ap_limit=True) + def opsi_archive(self): + try: + self.load_campaign() + self.campaign.os_archive() + except ActionPointLimit: + self.config.opsi_task_delay(ap_limit=True) + def opsi_stronghold(self): try: self.load_campaign() diff --git a/module/combat/submarine.py b/module/combat/submarine.py index 4606fb574..cceb38139 100644 --- a/module/combat/submarine.py +++ b/module/combat/submarine.py @@ -23,7 +23,7 @@ class SubmarineCall(ModuleBase): """ if self.submarine_call_flag: return False - if submarine in ['do_not_use', 'hunt_only']: + if submarine in ['do_not_use', 'hunt_only', 'hunt_and_boss']: self.submarine_call_flag = True return False if self.submarine_call_timer.reached(): diff --git a/module/commission/commission.py b/module/commission/commission.py index f4491e8e3..5e3975be0 100644 --- a/module/commission/commission.py +++ b/module/commission/commission.py @@ -1,13 +1,16 @@ import copy +from datetime import datetime from scipy import signal from module.base.timer import Timer from module.base.utils import * from module.combat.assets import * from module.commission.assets import * -from module.commission.project import (COMMISSION_FILTER, SHORTEST_FILTER, - Commission) +from module.commission.preset import DICT_FILTER_PRESET, SHORTEST_FILTER +from module.commission.project import COMMISSION_FILTER, Commission +from module.config.config_generated import GeneratedConfig +from module.config.utils import get_server_last_update from module.exception import GameStuckError from module.handler.info_handler import InfoHandler from module.logger import logger @@ -127,8 +130,24 @@ class RewardCommission(UI, InfoHandler): np.sum([1 for c in total if c.status == 'running'])) logger.attr('Running', f'{running_count}/{self.max_commission}') + # Load filter string + preset = self.config.Commission_PresetFilter + if preset == 'custom': + string = self.config.Commission_CustomFilter + else: + if f'{preset}_night' in DICT_FILTER_PRESET: + start_time = get_server_last_update('02:00') + end_time = get_server_last_update('21:00') + if start_time < end_time: + preset = f'{preset}_night' + if preset not in DICT_FILTER_PRESET: + logger.warning(f'Preset not found: {preset}, use default preset') + preset = GeneratedConfig.Commission_PresetFilter + string = DICT_FILTER_PRESET[preset] + logger.attr('Commission Filter', preset) + # Filter - COMMISSION_FILTER.load(self.config.Commission_CommissionFilter) + COMMISSION_FILTER.load(string) run = COMMISSION_FILTER.apply(total.grids, func=self._commission_check) logger.attr('Filter_sort', ' > '.join([str(c) for c in run])) run = SelectedGrids(run) diff --git a/module/commission/preset.py b/module/commission/preset.py new file mode 100644 index 000000000..fe338b894 --- /dev/null +++ b/module/commission/preset.py @@ -0,0 +1,144 @@ +SHORTEST_FILTER = """ +0:20 > 0:30 +> 1 > 1:10 > 1:20 > 1:30 > 1:40 > 1:45 +> 2 > 2:15 > 2:30 > 2:40 +> 3 > 3:20 +> 4 > 5 > 5:20 +> 6 > 7 > 8 > 9 > 10 > 12 +""" + +DICT_FILTER_PRESET = { + 'chip': """ + DailyEvent > Gem-8 > Gem-4 > Gem-2 > ExtraCube-0:30 + > UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-3 + > ExtraDrill-5:20 > ExtraDrill-2 > ExtraDrill-3:20 + > UrgentCube-2:15 > UrgentCube-4 + > ExtraDrill-1 > UrgentCube-6 > ExtraCube-1:30 + > ExtraDrill-2:40 > ExtraDrill-0:20 + > Major > DailyChip > DailyResource + > ExtraPart-0:30 > ExtraOil-1 > UrgentBox-6 + > ExtraCube-3 > ExtraPart-1 > UrgentBox-3 + > ExtraCube-4 > ExtraPart-1:30 > ExtraOil-4 + > UrgentBox-1 > ExtraCube-5 + > ExtraCube-8 > ExtraOil-8 + > UrgentDrill-4 > UrgentDrill-2:40 > UrgentDrill-2 + > UrgentDrill-1 > UrgentDrill-1:30 > UrgentDrill-1:10 + > Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00 + > shortest + """, + 'chip_night': """ + DailyEvent > Gem-8 > Gem-4 > Gem-2 + > Major > DailyChip > DailyResource + > NightDrill-8 > NightDrill-7 > NightDrill-6 + > ExtraDrill-5:20 > UrgentCube-6 > UrgentCube-4 + > ExtraDrill-3:20 > UrgentCube-3 > UrgentBox-6 + > UrgentCube-1:30 > UrgentCube-1:45 > NightCube-8 > ExtraCube-8 > UrgentCube-2:15 + > NightOil-8 > ExtraOil-8 > ExtraDrill-2 > ExtraDrill-2:40 + > ExtraCube-0:30 > UrgentBox-3 > ExtraCube-4 > NightCube-6 + > ExtraCube-1:30 > ExtraCube-5 > NightCube-7 > ExtraCube-3 + > ExtraOil-4 > UrgentDrill-4 > ExtraDrill-1 + > UrgentDrill-2:40 > UrgentDrill-2 + > UrgentBox-1 > UrgentDrill-1:30 > ExtraDrill-0:20 + > UrgentDrill-1:10 > UrgentDrill-1 + > ExtraOil-1 + > shortest + """, + 'chip_24h': """ + DailyEvent > Gem-8 > Gem-4 > Gem-2 > ExtraCube-0:30 + > UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-3 + > ExtraDrill-5:20 > ExtraDrill-2 > ExtraDrill-3:20 + > UrgentCube-2:15 > UrgentCube-4 + > ExtraDrill-1 > UrgentCube-6 > ExtraCube-1:30 + > ExtraDrill-2:40 > ExtraDrill-0:20 + > Major > DailyChip > DailyResource + > NightDrill-8 > NightDrill-7 > NightDrill-6 + > ExtraPart-0:30 > ExtraOil-1 > UrgentBox-6 + > ExtraCube-3 > ExtraPart-1 > UrgentBox-3 + > ExtraCube-4 > ExtraPart-1:30 > ExtraOil-4 + > UrgentBox-1 > ExtraCube-5 + > Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00 + > shortest + """, + 'cube': """ + DailyEvent > Gem-8 > Gem-4 > Gem-2 > ExtraCube-0:30 + > UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-3 + > UrgentCube-2:15 > UrgentCube-4 > UrgentCube-6 + > ExtraCube-1:30 > ExtraCube-3 > ExtraCube-4 + > ExtraCube-8 > UrgentBox-6 > UrgentBox-3 > ExtraCube-5 > UrgentBox-1 + > Major > DailyChip > DailyResource + > ExtraOil-8 > UrgentDrill-4 > ExtraOil-4 > ExtraOil-1 + > ExtraDrill-0:20 > UrgentDrill-2:40 > ExtraPart-0:30 + > UrgentDrill-2 > UrgentDrill-1 > UrgentDrill-1:30 + > ExtraPart-1 > ExtraDrill-1 > UrgentDrill-1:10 + > ExtraPart-1:30 > ExtraDrill-2 + > ExtraDrill-2:40 > ExtraDrill-3:20 > ExtraDrill-5:20 + > Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00 + > shortest + """, + 'cube_night': """ + DailyEvent > Gem-8 > Gem-4 > Gem-2 + > Major > DailyChip > DailyResource + > UrgentCube-6 > UrgentCube-4 > UrgentCube-3 + > UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-2:15 + > NightCube-8 > ExtraCube-8 > UrgentBox-6 + > ExtraCube-0:30 > NightCube-6 > ExtraCube-4 + > NightOil-8 > ExtraOil-8 + > ExtraCube-1:30 > NightCube-7 > ExtraCube-5 > ExtraCube-3 + > UrgentBox-3 > NightDrill-8 > UrgentDrill-4 + > ExtraOil-4 > NightDrill-7 + > UrgentDrill-2:40 > NightDrill-6 > UrgentDrill-2 + > UrgentBox-1 > UrgentDrill-1:30 > ExtraDrill-5:20 + > UrgentDrill-1:10 > UrgentDrill-1 > ExtraOil-1 + > ExtraDrill-3:20 > ExtraDrill-2:40 > ExtraDrill-2 + > Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00 + > shortest + """, + 'cube_24h': """ + DailyEvent > Gem-8 > Gem-4 > Gem-2 > ExtraCube-0:30 + > UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-3 + > UrgentCube-2:15 > UrgentCube-4 > UrgentCube-6 + > ExtraCube-1:30 > ExtraCube-3 > ExtraCube-4 + > ExtraCube-8 > UrgentBox-6 > UrgentBox-3 > ExtraCube-5 > UrgentBox-1 + > Major > DailyChip > DailyResource + > ExtraOil-1 > ExtraOil-4 > ExtraOil-8 + > ExtraPart-0:30 > ExtraPart-1 > ExtraDrill-1 + > ExtraPart-1:30 > ExtraDrill-2 + > Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00 + > shortest + """, + 'oil': """ + DailyEvent > Gem-8 > Gem-4 > Gem-2 > ExtraCube-0:30 + > UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-3 + > UrgentCube-2:15 > UrgentCube-4 + > UrgentBox-6 > ExtraCube-1:30 > UrgentCube-6 + > UrgentBox-3 > UrgentBox-1 + > UrgentDrill-4 > ExtraOil-8 > UrgentDrill-2:40 > ExtraOil-4 + > Major > DailyChip > DailyResource + > ExtraCube-3 > UrgentDrill-2 > UrgentDrill-1 + > UrgentDrill-1:30 > UrgentDrill-1:10 + > ExtraCube-4 > ExtraOil-1 > ExtraCube-8 > ExtraDrill-0:20 + > ExtraCube-5 > ExtraPart-0:30 > ExtraPart-1 + > ExtraDrill-1 > ExtraPart-1:30 > ExtraDrill-2 + > ExtraDrill-2:40 > ExtraDrill-3:20 > ExtraDrill-5:20 + > Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00 + > shortest + """, + 'oil_night': """ + DailyEvent > Gem-8 > Gem-4 > Gem-2 + > Major > DailyChip > DailyResource + > UrgentBox-6 > UrgentCube-6 + > NightOil-8 > ExtraOil-8 > UrgentCube-3 + > UrgentCube-4 > UrgentCube-1:30 > UrgentCube-1:45 + > NightCube-8 > ExtraCube-8 > UrgentCube-2:15 + > UrgentBox-3 > NightDrill-8 > UrgentDrill-4 > ExtraOil-4 + > ExtraCube-0:30 > NightDrill-7 > NightCube-6 > ExtraCube-4 + > ExtraCube-1:30 > NightCube-7 > ExtraCube-5 > ExtraCube-3 + > UrgentDrill-2:40 > NightDrill-6 > UrgentDrill-2 + > UrgentBox-1 > UrgentDrill-1:30 > ExtraDrill-5:20 + > UrgentDrill-1:10 > UrgentDrill-1 + > ExtraOil-1 > ExtraDrill-3:20 > ExtraDrill-2:40 + > ExtraPart-1:30 > ExtraDrill-2 + > Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00 + > shortest + """ +} diff --git a/module/commission/project.py b/module/commission/project.py index 8c3d37ed5..b770fbbce 100644 --- a/module/commission/project.py +++ b/module/commission/project.py @@ -22,14 +22,6 @@ COMMISSION_FILTER = Filter( attr=('category_str', 'genre_str', 'duration_hm', 'duration_hour'), preset=('shortest',) ) -SHORTEST_FILTER = """ -0:20 > 0:30 -> 1 > 1:10 > 1:20 > 1:30 > 1:40 > 1:45 -> 2 > 2:15 > 2:30 > 2:40 -> 3 > 3:20 -> 4 > 5 > 5:20 -> 6 > 7 > 8 > 9 > 10 > 12 -""" class SuffixOcr(Ocr): diff --git a/module/config/argument/args.json b/module/config/argument/args.json index ad29c4845..6d721d223 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -106,7 +106,8 @@ "ADB_nc", "uiautomator2", "aScreenCap", - "aScreenCap_nc" + "aScreenCap_nc", + "DroidCast" ] }, "ControlMethod": { @@ -129,7 +130,11 @@ } }, "RestartEmulator": { - "Enable": { + "ErrorRestart": { + "type": "checkbox", + "value": false + }, + "DailyRestart": { "type": "checkbox", "value": false }, @@ -260,6 +265,14 @@ "save_and_upload" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "General": { @@ -313,6 +326,14 @@ "type": "checkbox", "value": false } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Restart": { @@ -347,6 +368,14 @@ "value": "00:00", "display": "hide" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Main": { @@ -568,6 +597,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -710,6 +740,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Main2": { @@ -931,6 +969,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -1073,6 +1112,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Main3": { @@ -1294,6 +1341,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -1436,6 +1484,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "GemsFarming": { @@ -1755,6 +1811,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -1863,6 +1920,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "EventGeneral": { @@ -1895,6 +1960,14 @@ "Main3" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Event": { @@ -2174,6 +2247,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -2316,6 +2390,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Event2": { @@ -2595,6 +2677,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -2737,6 +2820,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "EventA": { @@ -3033,6 +3124,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -3175,6 +3267,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "EventB": { @@ -3471,6 +3571,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -3613,6 +3714,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "EventC": { @@ -3909,6 +4018,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -4051,6 +4161,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "EventD": { @@ -4347,6 +4465,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -4489,6 +4608,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "EventSp": { @@ -4775,6 +4902,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -4918,6 +5046,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Raid": { @@ -5140,6 +5276,14 @@ "value": false, "display": "hide" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "RaidDaily": { @@ -5354,6 +5498,14 @@ "value": false, "display": "hide" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "MaritimeEscort": { @@ -5393,6 +5545,14 @@ "type": "checkbox", "value": true } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Commission": { @@ -5433,9 +5593,29 @@ "type": "checkbox", "value": false }, - "CommissionFilter": { + "PresetFilter": { + "type": "select", + "value": "cube", + "option": [ + "chip", + "chip_24h", + "cube", + "cube_24h", + "oil", + "custom" + ] + }, + "CustomFilter": { "type": "textarea", - "value": "DailyEvent\n> Gem-8 > Gem-4 > Gem-2\n> NightDrill-8 > NightDrill-7 > NightDrill-6\n> ExtraDrill-0:20 > ExtraDrill-1 > ExtraDrill-2 > ExtraDrill-2:40 > ExtraDrill-3:20 > ExtraDrill-5:20\n> Box-6 > Box-3 > Box-1\n> DailyCube-0:30 > UrgentCube-1:30 > DailyCube-1:30 > UrgentCube-1:45 > UrgentCube-2:15 > UrgentCube-3\n> Major\n> DailyChip > DailyResource\n> UrgentBook-2:30 > UrgentBook-2 > UrgentBook-1:20 > UrgentBook-1:40\n> Daily-0:20 > Daily-0:30 > Daily-1:00 > Daily-1:30 > Daily-2:00\n> NightOil > NightCube\n> shortest" + "value": "DailyEvent > Gem-4 > Gem-2 > Gem-8 > ExtraCube-0:30\n> UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-3 \n> ExtraDrill-5:20 > ExtraDrill-2 > ExtraDrill-3:20 \n> UrgentCube-2:15 > UrgentCube-4\n> ExtraDrill-1 > UrgentCube-6 > ExtraCube-1:30 \n> ExtraDrill-2:40 > ExtraDrill-0:20 \n> Major > DailyChip > DailyResource\n> ExtraPart-0:30 > ExtraOil-1 > UrgentBox-6 \n> ExtraCube-3 > ExtraPart-1 > UrgentBox-3\n> ExtraCube-4 > ExtraPart-1:30 > ExtraOil-4\n> UrgentBox-1 > ExtraCube-5 > UrgentBox-1\n> ExtraCube-8 > ExtraOil-8\n> UrgentDrill-4 > UrgentDrill-2:40 > UrgentDrill-2 \n> UrgentDrill-1 > UrgentDrill-1:30 > UrgentDrill-1:10\n> Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00\n> shortest" + } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" } } }, @@ -5519,6 +5699,14 @@ "type": "checkbox", "value": true } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Research": { @@ -5613,6 +5801,14 @@ "type": "textarea", "value": "S5-DR0.5 > S5-PRY0.5 > S5-H0.5 > S5-Q0.5 > S5-DR2.5 > 0.5 > S5-G1.5\n> S5-Q1 > S5-DR5 > S5-DR8 > S5-G4 > S5-PRY2.5 > 1 > S5-Q2 > reset\n> S5-G2.5 > S5-PRY5 > S5-PRY8 > 1.5 > 2 > S5-Q4 > 2.5 > 3\n> Q4 > G4 > 4 > 5 > S5-C6 > C6 > 6 > S5-C8 > 8\n> S5-C12 > 12" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Dorm": { @@ -5660,6 +5856,14 @@ "type": "textarea", "value": "20000 > 10000 > 5000 > 3000 > 2000 > 1000" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Meowfficer": { @@ -5729,6 +5933,14 @@ "type": "input", "value": 1 } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Guild": { @@ -5802,6 +6014,14 @@ "type": "checkbox", "value": false } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Reward": { @@ -5858,6 +6078,14 @@ "type": "checkbox", "value": true } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "ShopFrequent": { @@ -5909,6 +6137,14 @@ "type": "textarea", "value": "BookRedT3 > BookYellowT3 > BookBlueT3 > BookRedT2\n> Cube\n> FoodT6 > FoodT5" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "ShopOnce": { @@ -6160,6 +6396,14 @@ "type": "textarea", "value": "Array" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Shipyard": { @@ -6207,6 +6451,14 @@ "type": "input", "value": 2 } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Gacha": { @@ -6277,6 +6529,14 @@ "type": "checkbox", "value": false } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Freebies": { @@ -6346,6 +6606,14 @@ "type": "checkbox", "value": true } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Daily": { @@ -6504,6 +6772,14 @@ 6 ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Hard": { @@ -6551,6 +6827,14 @@ 2 ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Exercise": { @@ -6621,6 +6905,14 @@ "value": "2020-01-01 00:00:00", "validate": "datetime" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Sos": { @@ -6870,6 +7162,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -7013,6 +7306,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "WarArchives": { @@ -7056,6 +7357,8 @@ "type": "select", "value": "campaign_main", "option": [ + "war_archives_20210819_cn", + "war_archives_20200903_cn", "war_archives_20201029_cn", "war_archives_20200806_cn", "war_archives_20210624_cn", @@ -7074,9 +7377,9 @@ "war_archives_20191031_en", "war_archives_20181020_en" ], - "cn": "war_archives_20201029_cn", - "en": "war_archives_20201029_cn", - "jp": "war_archives_20201029_cn", + "cn": "war_archives_20210819_cn", + "en": "war_archives_20210819_cn", + "jp": "war_archives_20210819_cn", "tw": "war_archives_20181227_cn" }, "Mode": { @@ -7255,6 +7558,7 @@ "do_not_use", "hunt_only", "boss_only", + "hunt_and_boss", "every_combat" ] }, @@ -7397,6 +7701,14 @@ "S1_enemy_first" ] } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiGeneral": { @@ -7433,6 +7745,14 @@ "type": "textarea", "value": "ActionPoint > PurpleCoins" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiAshBeacon": { @@ -7490,6 +7810,14 @@ "type": "checkbox", "value": true } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiAshAssist": { @@ -7529,6 +7857,14 @@ "type": "input", "value": 15 } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiExplore": { @@ -7592,6 +7928,14 @@ "type": "checkbox", "value": false } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiShop": { @@ -7631,6 +7975,61 @@ "type": "checkbox", "value": true } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } + } + }, + "OpsiVoucher": { + "Scheduler": { + "Enable": { + "type": "checkbox", + "value": false + }, + "NextRun": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + }, + "Command": { + "type": "input", + "value": "OpsiVoucher", + "display": "hide" + }, + "SuccessInterval": { + "type": "input", + "value": 30, + "display": "hide" + }, + "FailureInterval": { + "type": "input", + "value": 30, + "display": "hide" + }, + "ServerUpdate": { + "type": "input", + "value": "00:00", + "display": "hide" + } + }, + "OpsiVoucher": { + "Filter": { + "type": "textarea", + "value": "LoggerAbyssal > LoggerObscure > HECombatPlan > Book > Coin" + } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiDaily": { @@ -7690,6 +8089,14 @@ "type": "checkbox", "value": false } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiObscure": { @@ -7745,6 +8152,14 @@ "type": "checkbox", "value": false } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiAbyssal": { @@ -7775,7 +8190,7 @@ }, "ServerUpdate": { "type": "input", - "value": "00:00", + "value": "00:00, 12:00", "display": "hide" } }, @@ -7790,6 +8205,14 @@ "type": "textarea", "value": "Fleet-4 > CallSubmarine > Fleet-2 > Fleet-3 > Fleet-1" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiStronghold": { @@ -7835,6 +8258,14 @@ "type": "textarea", "value": "Fleet-4 > CallSubmarine > Fleet-2 > Fleet-3 > Fleet-1" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiMeowfficerFarming": { @@ -7872,7 +8303,7 @@ "OpsiMeowfficerFarming": { "ActionPointPreserve": { "type": "input", - "value": 500 + "value": 1000 }, "HazardLevel": { "type": "select", @@ -7905,6 +8336,83 @@ "type": "checkbox", "value": false } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } + } + }, + "OpsiHazard1Leveling": { + "Scheduler": { + "Enable": { + "type": "checkbox", + "value": false + }, + "NextRun": { + "type": "datetime", + "value": "2020-01-01 00:00:00", + "validate": "datetime" + }, + "Command": { + "type": "input", + "value": "OpsiHazard1Leveling", + "display": "hide" + }, + "SuccessInterval": { + "type": "input", + "value": 30, + "display": "hide" + }, + "FailureInterval": { + "type": "input", + "value": 60, + "display": "hide" + }, + "ServerUpdate": { + "type": "input", + "value": "00:00", + "display": "hide" + } + }, + "OpsiHazard1Leveling": { + "TargetZone": { + "type": "select", + "value": 0, + "option": [ + 0, + 44, + 22 + ] + } + }, + "OpsiFleet": { + "Fleet": { + "type": "select", + "value": 1, + "option": [ + 1, + 2, + 3, + 4 + ] + }, + "Submarine": { + "type": "checkbox", + "value": false, + "display": "hide" + } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiCrossMonth": { @@ -7938,6 +8446,14 @@ "value": "00:00", "display": "hide" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Daemon": { @@ -7946,6 +8462,14 @@ "type": "checkbox", "value": true } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "OpsiDaemon": { @@ -7958,6 +8482,14 @@ "type": "checkbox", "value": true } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "Benchmark": { @@ -7998,6 +8530,14 @@ "type": "checkbox", "value": false } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "AzurLaneUncensored": { @@ -8006,6 +8546,14 @@ "type": "input", "value": "https://gitee.com/LmeSzinc/AzurLaneUncensored" } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } }, "GameManager": { @@ -8014,6 +8562,14 @@ "type": "checkbox", "value": true } + }, + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } } } } \ No newline at end of file diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 50303f060..1e7744deb 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -29,14 +29,15 @@ Emulator: option: [disabled, ] ScreenshotMethod: value: ADB - option: [ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc] + option: [ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast] ControlMethod: value: minitouch option: [ADB, uiautomator2, minitouch, Hermit] ScreenshotDedithering: false AdbRestart: false RestartEmulator: - Enable: False + ErrorRestart: False + DailyRestart: False EmulatorType: value: auto option: [auto, nox_player, bluestacks_5, mumu_player] @@ -153,7 +154,7 @@ Submarine: option: [0, 1, 2] Mode: value: do_not_use - option: [do_not_use, hunt_only, boss_only, every_combat] + option: [do_not_use, hunt_only, boss_only, hunt_and_boss, every_combat] AutoSearchMode: value: sub_standby option: [ sub_standby, sub_auto_call ] @@ -257,18 +258,31 @@ MaritimeEscort: Commission: DoMajorCommission: false - CommissionFilter: |- - DailyEvent - > Gem-8 > Gem-4 > Gem-2 - > NightDrill-8 > NightDrill-7 > NightDrill-6 - > ExtraDrill-0:20 > ExtraDrill-1 > ExtraDrill-2 > ExtraDrill-2:40 > ExtraDrill-3:20 > ExtraDrill-5:20 - > Box-6 > Box-3 > Box-1 - > DailyCube-0:30 > UrgentCube-1:30 > DailyCube-1:30 > UrgentCube-1:45 > UrgentCube-2:15 > UrgentCube-3 - > Major - > DailyChip > DailyResource - > UrgentBook-2:30 > UrgentBook-2 > UrgentBook-1:20 > UrgentBook-1:40 - > Daily-0:20 > Daily-0:30 > Daily-1:00 > Daily-1:30 > Daily-2:00 - > NightOil > NightCube + PresetFilter: + value: cube + option: + - chip + - chip_24h + - cube + - cube_24h + - oil + - custom + CustomFilter: |- + DailyEvent > Gem-4 > Gem-2 > Gem-8 > ExtraCube-0:30 + > UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-3 + > ExtraDrill-5:20 > ExtraDrill-2 > ExtraDrill-3:20 + > UrgentCube-2:15 > UrgentCube-4 + > ExtraDrill-1 > UrgentCube-6 > ExtraCube-1:30 + > ExtraDrill-2:40 > ExtraDrill-0:20 + > Major > DailyChip > DailyResource + > ExtraPart-0:30 > ExtraOil-1 > UrgentBox-6 + > ExtraCube-3 > ExtraPart-1 > UrgentBox-3 + > ExtraCube-4 > ExtraPart-1:30 > ExtraOil-4 + > UrgentBox-1 > ExtraCube-5 > UrgentBox-1 + > ExtraCube-8 > ExtraOil-8 + > UrgentDrill-4 > UrgentDrill-2:40 > UrgentDrill-2 + > UrgentDrill-1 > UrgentDrill-1:30 > UrgentDrill-1:10 + > Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00 > shortest Tactical: TacticalFilter: |- @@ -559,6 +573,9 @@ OpsiExplore: LastZone: 0 OpsiShop: BuySupply: true +OpsiVoucher: + Filter: |- + LoggerAbyssal > LoggerObscure > HECombatPlan > Book > Coin OpsiDaily: DoMission: true UseTuningSample: true @@ -569,12 +586,16 @@ OpsiAbyssal: OpsiStronghold: ForceRun: false OpsiMeowfficerFarming: - ActionPointPreserve: 500 + ActionPointPreserve: 1000 HazardLevel: value: 5 option: [3, 4, 5, 6, 10] TargetZone: value: 0 +OpsiHazard1Leveling: + TargetZone: + value: 0 + option: [0, 44, 22] # ==================== Tools ==================== diff --git a/module/config/argument/gui.yaml b/module/config/argument/gui.yaml index 19f3eceab..d134fb103 100644 --- a/module/config/argument/gui.yaml +++ b/module/config/argument/gui.yaml @@ -87,4 +87,5 @@ Remote: SSHNotInstall: Text: - InvalidFeedBack: \ No newline at end of file + InvalidFeedBack: + Clear: \ No newline at end of file diff --git a/module/config/argument/menu.json b/module/config/argument/menu.json index e9053c902..e1053aade 100644 --- a/module/config/argument/menu.json +++ b/module/config/argument/menu.json @@ -53,11 +53,13 @@ "OpsiAshAssist", "OpsiExplore", "OpsiShop", + "OpsiVoucher", "OpsiDaily", "OpsiObscure", "OpsiAbyssal", "OpsiStronghold", "OpsiMeowfficerFarming", + "OpsiHazard1Leveling", "OpsiCrossMonth" ], "Tool": [ diff --git a/module/config/argument/override.yaml b/module/config/argument/override.yaml index b45557430..ca103fe59 100644 --- a/module/config/argument/override.yaml +++ b/module/config/argument/override.yaml @@ -336,6 +336,11 @@ OpsiShop: SuccessInterval: 30 FailureInterval: 30 ServerUpdate: 00:00 +OpsiVoucher: + Scheduler: + SuccessInterval: 30 + FailureInterval: 30 + ServerUpdate: 00:00 OpsiDaily: Scheduler: SuccessInterval: 30 @@ -350,7 +355,7 @@ OpsiAbyssal: Scheduler: SuccessInterval: 60 FailureInterval: 60 - ServerUpdate: 00:00 + ServerUpdate: 00:00, 12:00 OpsiStronghold: Scheduler: SuccessInterval: 60 @@ -361,3 +366,10 @@ OpsiMeowfficerFarming: SuccessInterval: 30 FailureInterval: 30 ServerUpdate: 00:00 +OpsiHazard1Leveling: + Scheduler: + SuccessInterval: 30 + FailureInterval: 60 + ServerUpdate: 00:00 + OpsiFleet: + Submarine: false diff --git a/module/config/argument/task.yaml b/module/config/argument/task.yaml index 607a8ae52..23e0b0efd 100644 --- a/module/config/argument/task.yaml +++ b/module/config/argument/task.yaml @@ -243,6 +243,9 @@ OpsiExplore: OpsiShop: - Scheduler - OpsiShop +OpsiVoucher: + - Scheduler + - OpsiVoucher OpsiDaily: - Scheduler - OpsiDaily @@ -263,6 +266,10 @@ OpsiMeowfficerFarming: - Scheduler - OpsiMeowfficerFarming - OpsiFleet +OpsiHazard1Leveling: + - Scheduler + - OpsiHazard1Leveling + - OpsiFleet OpsiCrossMonth: - Scheduler diff --git a/module/config/config.py b/module/config/config.py index e05f1f14a..3f1a2f4d8 100644 --- a/module/config/config.py +++ b/module/config/config.py @@ -289,7 +289,7 @@ class AzurLaneConfig(ConfigUpdater, ManualConfig, GeneratedConfig, ConfigWatcher ) limit_next_run(["Commission", "Reward"], limit=now + timedelta(hours=12, seconds=-1)) limit_next_run(["Research"], limit=now + timedelta(hours=24, seconds=-1)) - limit_next_run(["OpsiExplore", "OpsiCrossMonth"], limit=now + timedelta(days=31, seconds=-1)) + limit_next_run(["OpsiExplore", "OpsiCrossMonth", "OpsiVoucher"], limit=now + timedelta(days=31, seconds=-1)) limit_next_run(self.args.keys(), limit=now + timedelta(hours=24, seconds=-1)) def override(self, **kwargs): diff --git a/module/config/config_generated.py b/module/config/config_generated.py index de143ce33..af0f32c44 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -21,13 +21,14 @@ class GeneratedConfig: Emulator_Serial = 'auto' Emulator_PackageName = 'auto' # auto, com.bilibili.azurlane, com.YoStarEN.AzurLane, com.YoStarJP.AzurLane, com.hkmanjuu.azurlane.gp, com.bilibili.blhx.huawei, com.bilibili.blhx.mi, com.tencent.tmgp.bilibili.blhx, com.bilibili.blhx.baidu, com.bilibili.blhx.qihoo, com.bilibili.blhx.nearme.gamecenter, com.bilibili.blhx.vivo, com.bilibili.blhx.mz, com.bilibili.blhx.uc, com.bilibili.blhx.mzw, com.yiwu.blhx.yx15, com.bilibili.blhx.m4399, com.hkmanjuu.azurlane.gp.mc Emulator_ServerName = 'disabled' # disabled, cn_android-0, cn_android-1, cn_android-2, cn_android-3, cn_android-4, cn_android-5, cn_android-6, cn_android-7, cn_android-8, cn_android-9, cn_android-10, cn_android-11, cn_android-12, cn_android-13, cn_android-14, cn_android-15, cn_android-16, cn_android-17, cn_android-18, cn_android-19, cn_android-20, cn_android-21, cn_android-22, cn_ios-0, cn_ios-1, cn_ios-2, cn_ios-3, cn_ios-4, cn_ios-5, cn_ios-6, cn_ios-7, cn_ios-8, cn_ios-9, cn_ios-10, cn_channel-0, cn_channel-1, cn_channel-2, cn_channel-3, en-0, en-1, en-2, en-3, en-4, jp-0, jp-1, jp-2, jp-3, jp-4, jp-5, jp-6, jp-7, jp-8, jp-9, jp-10, jp-11, jp-12, jp-13, jp-14, jp-15, jp-16, jp-17 - Emulator_ScreenshotMethod = 'ADB' # ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc + Emulator_ScreenshotMethod = 'ADB' # ADB, ADB_nc, uiautomator2, aScreenCap, aScreenCap_nc, DroidCast Emulator_ControlMethod = 'minitouch' # ADB, uiautomator2, minitouch, Hermit Emulator_ScreenshotDedithering = False Emulator_AdbRestart = False # Group `RestartEmulator` - RestartEmulator_Enable = False + RestartEmulator_ErrorRestart = False + RestartEmulator_DailyRestart = False RestartEmulator_EmulatorType = 'auto' # auto, nox_player, bluestacks_5, mumu_player # Group `Error` @@ -96,7 +97,7 @@ class GeneratedConfig: # Group `Submarine` Submarine_Fleet = 0 # 0, 1, 2 - Submarine_Mode = 'do_not_use' # do_not_use, hunt_only, boss_only, every_combat + Submarine_Mode = 'do_not_use' # do_not_use, hunt_only, boss_only, hunt_and_boss, every_combat Submarine_AutoSearchMode = 'sub_standby' # sub_standby, sub_auto_call Submarine_DistanceToBoss = '2_grid_to_boss' # to_boss_position, 1_grid_to_boss, 2_grid_to_boss, use_open_ocean_support @@ -175,7 +176,8 @@ class GeneratedConfig: # Group `Commission` Commission_DoMajorCommission = False - Commission_CommissionFilter = 'DailyEvent\n> Gem-8 > Gem-4 > Gem-2\n> NightDrill-8 > NightDrill-7 > NightDrill-6\n> ExtraDrill-0:20 > ExtraDrill-1 > ExtraDrill-2 > ExtraDrill-2:40 > ExtraDrill-3:20 > ExtraDrill-5:20\n> Box-6 > Box-3 > Box-1\n> DailyCube-0:30 > UrgentCube-1:30 > DailyCube-1:30 > UrgentCube-1:45 > UrgentCube-2:15 > UrgentCube-3\n> Major\n> DailyChip > DailyResource\n> UrgentBook-2:30 > UrgentBook-2 > UrgentBook-1:20 > UrgentBook-1:40\n> Daily-0:20 > Daily-0:30 > Daily-1:00 > Daily-1:30 > Daily-2:00\n> NightOil > NightCube\n> shortest' + Commission_PresetFilter = 'cube' # chip, chip_24h, cube, cube_24h, oil, custom + Commission_CustomFilter = 'DailyEvent > Gem-4 > Gem-2 > Gem-8 > ExtraCube-0:30\n> UrgentCube-1:30 > UrgentCube-1:45 > UrgentCube-3 \n> ExtraDrill-5:20 > ExtraDrill-2 > ExtraDrill-3:20 \n> UrgentCube-2:15 > UrgentCube-4\n> ExtraDrill-1 > UrgentCube-6 > ExtraCube-1:30 \n> ExtraDrill-2:40 > ExtraDrill-0:20 \n> Major > DailyChip > DailyResource\n> ExtraPart-0:30 > ExtraOil-1 > UrgentBox-6 \n> ExtraCube-3 > ExtraPart-1 > UrgentBox-3\n> ExtraCube-4 > ExtraPart-1:30 > ExtraOil-4\n> UrgentBox-1 > ExtraCube-5 > UrgentBox-1\n> ExtraCube-8 > ExtraOil-8\n> UrgentDrill-4 > UrgentDrill-2:40 > UrgentDrill-2 \n> UrgentDrill-1 > UrgentDrill-1:30 > UrgentDrill-1:10\n> Extra-0:20 > Extra-0:30 > Extra-1:00 > Extra-1:30 > Extra-2:00\n> shortest' # Group `Tactical` Tactical_TacticalFilter = 'SameT4 > SameT3 > SameT2 > SameT1\n> BlueT2 > YellowT2 > RedT2\n> BlueT3 > YellowT3 > RedT3\n> BlueT4 > YellowT4 > RedT4\n> BlueT1 > YellowT1 > RedT1\n> first' @@ -365,6 +367,9 @@ class GeneratedConfig: # Group `OpsiShop` OpsiShop_BuySupply = True + # Group `OpsiVoucher` + OpsiVoucher_Filter = 'LoggerAbyssal > LoggerObscure > HECombatPlan > Book > Coin' + # Group `OpsiDaily` OpsiDaily_DoMission = True OpsiDaily_UseTuningSample = True @@ -379,10 +384,13 @@ class GeneratedConfig: OpsiStronghold_ForceRun = False # Group `OpsiMeowfficerFarming` - OpsiMeowfficerFarming_ActionPointPreserve = 500 + OpsiMeowfficerFarming_ActionPointPreserve = 1000 OpsiMeowfficerFarming_HazardLevel = 5 # 3, 4, 5, 6, 10 OpsiMeowfficerFarming_TargetZone = 0 + # Group `OpsiHazard1Leveling` + OpsiHazard1Leveling_TargetZone = 0 # 0, 44, 22 + # Group `Daemon` Daemon_EnterMap = True @@ -406,3 +414,6 @@ class GeneratedConfig: # Group `GameManager` GameManager_AutoRestart = True + + # Group `Storage` + Storage_Storage = {} diff --git a/module/config/config_manual.py b/module/config/config_manual.py index 8aeab850d..54227c086 100644 --- a/module/config/config_manual.py +++ b/module/config/config_manual.py @@ -18,13 +18,14 @@ class ManualConfig: > ShopFrequent > ShopOnce > Shipyard > Freebies > OpsiExplore > OpsiAshBeacon - > OpsiDaily > OpsiShop + > OpsiDaily > OpsiShop > OpsiVoucher > OpsiAbyssal > OpsiStronghold > OpsiObscure > Daily > Hard > OpsiAshBeacon > OpsiAshAssist > Sos > EventSp > EventA > EventB > EventC > EventD > RaidDaily > WarArchives > MaritimeEscort > Event > Event2 > Raid > Main > Main2 > Main3 > OpsiMeowfficerFarming > GemsFarming + > OpsiHazard1Leveling """ """ @@ -73,6 +74,8 @@ class ManualConfig: REVERSE_SERVER_PORT = 7903 ASCREENCAP_FILEPATH_LOCAL = './bin/ascreencap' ASCREENCAP_FILEPATH_REMOTE = '/data/local/tmp/ascreencap' + DROIDCAST_FILEPATH_LOCAL = './bin/DroidCast/DroidCast-debug-1.1.0.apk' + DROIDCAST_FILEPATH_REMOTE = '/data/local/tmp/DroidCast.apk' MINITOUCH_FILEPATH_REMOTE = '/data/local/tmp/minitouch' HERMIT_FILEPATH_LOCAL = './bin/hermit/hermit.apk' diff --git a/module/config/config_updater.py b/module/config/config_updater.py index e5c03eb40..0002bf529 100644 --- a/module/config/config_updater.py +++ b/module/config/config_updater.py @@ -88,6 +88,14 @@ class ConfigGenerator: arg.update(value) deep_set(data, keys=path, value=arg) + # Define storage group + arg = { + 'type': 'storage', + 'value': {}, + 'valuetype': 'ignore', + 'display': 'disabled', + } + deep_set(data, keys=['Storage', 'Storage'], value=arg) return data @cached_property @@ -139,6 +147,8 @@ class ConfigGenerator: # Construct args data = {} for task, groups in self.task.items(): + # Add storage to all task + groups.append('Storage') for group in groups: if group not in self.argument: print(f'`{task}.{group}` is not related to any argument group') @@ -476,11 +486,13 @@ class ConfigUpdater: ('Alas.DropRecord.SaveCombat', 'Alas.DropRecord.CombatRecord', upload_redirect), ('Alas.DropRecord.SaveMeowfficer', 'Alas.DropRecord.MeowfficerBuy', upload_redirect), ('Alas.Emulator.PackageName', 'Alas.DropRecord.API', api_redirect), + ('Alas.RestartEmulator.Enable', 'Alas.RestartEmulator.ErrorRestart'), ('OpsiGeneral.OpsiGeneral.BuyActionPoint', 'OpsiGeneral.OpsiGeneral.BuyActionPointLimit', action_point_redirect), ('BattlePass.BattlePass.BattlePassReward', 'Freebies.BattlePass.Collect'), ('DataKey.Scheduler.Enable', 'Freebies.DataKey.Collect'), ('DataKey.DataKey.ForceGet', 'Freebies.DataKey.ForceCollect'), ('SupplyPack.SupplyPack.WeeklyFreeSupplyPack', 'Freebies.SupplyPack.Collect'), + ('Commission.Commission.CommissionFilter', 'Commission.Commission.CustomFilter') ] @cached_property diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 4ea5e26dc..0b5218d25 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -198,6 +198,10 @@ "name": "OpSi Shop", "help": "" }, + "OpsiVoucher": { + "name": "OpSi Voucher", + "help": "" + }, "OpsiDaily": { "name": "OpSi Daily", "help": "" @@ -218,6 +222,10 @@ "name": "Meowfficer Farming", "help": "" }, + "OpsiHazard1Leveling": { + "name": "CL1 Leveling", + "help": "" + }, "OpsiCrossMonth": { "name": "Cross Month Daily", "help": " ALAS will enter OpSi 10min before OpSi reset, wait until OpSi reset but not exit OpSi. Then do the daily, obscure, abyssal and meowfficer farming to get extra gold plates. When running dailies, settings in task \"OpsiDaily\" are used, the rest function are the same.\n IMPORTANT: Please do not touch the game while ALAS is waiting for OpSi reset." @@ -377,7 +385,8 @@ "ADB_nc": "ADB_nc", "uiautomator2": "uiautomator2", "aScreenCap": "aScreenCap", - "aScreenCap_nc": "aScreenCap_nc" + "aScreenCap_nc": "aScreenCap_nc", + "DroidCast": "DroidCast" }, "ControlMethod": { "name": "Control Method", @@ -401,9 +410,13 @@ "name": "Restart Emulator", "help": "" }, - "Enable": { - "name": "Enable Restart Emulator", - "help": "Automatically start the emulator when it is shut down\nRestart the emulator to free memory when the server date is updated" + "ErrorRestart": { + "name": "Restart Emulator on Error", + "help": "Automatically restart the emulator when it cannot be connected" + }, + "DailyRestart": { + "name": "Restart Emulator when Server Refreshes", + "help": "Restart emulator every day to solve the memory leak problem" }, "EmulatorType": { "name": "Emulator Type", @@ -648,6 +661,8 @@ "event_20200423_cn": "Crimson Echoes Rerun", "event_20200326_cn": "Microlayer Medley", "event_20200227_cn": "Northern Overture", + "war_archives_20210819_cn": "archives Microlayer Medley", + "war_archives_20200903_cn": "archives Stars of the Shimmering Fjord", "war_archives_20201029_cn": "archives Universe in Unison", "war_archives_20200806_cn": "archives The Enigma and the Shark", "war_archives_20210624_cn": "archives Swirling Cherry Blossoms", @@ -823,10 +838,11 @@ }, "Mode": { "name": "Submarine Mode", - "help": "", + "help": "Reminder: 'Hunt and Boss' is actually a mix of 'Hunt Only' and 'Boss Only', it does hunt and summon submarines at boss if available.", "do_not_use": "Don't Use", "hunt_only": "Hunt Only", "boss_only": "BOSS Only", + "hunt_and_boss": "Hunt and BOSS", "every_combat": "Every Combat" }, "AutoSearchMode": { @@ -837,7 +853,7 @@ }, "DistanceToBoss": { "name": "Before BOSS battle move the submarine near BOSS", - "help": "Effective only if \"Submarine Mode\" is \"BOSS Only\"\nSelecting \"X Grids To BOSS\" needs to ensure that the submarine hunting range can cover the BOSS. Distance is calculated using the Manhattan Distance. Selecting \"Use Open Ocean Support\" requires U522/Leonardo da Vinci in the submarine fleet.", + "help": "Effective only if \"Submarine Mode\" is \"BOSS Only\" or \"Hunt and BOSS\"\nSelecting \"X Grids To BOSS\" needs to ensure that the submarine hunting range can cover the BOSS. Distance is calculated using the Manhattan Distance. Selecting \"Use Open Ocean Support\" requires U522/Leonardo da Vinci in the submarine fleet.", "to_boss_position": "To BOSS Location", "1_grid_to_boss": "1 Grid To BOSS", "2_grid_to_boss": "2 Grids To BOSS", @@ -1159,9 +1175,19 @@ "name": "Do Major Commissions", "help": "Enable allowance of undertaking 1200/1000 oil cost commissions; Commission Priority requires \"Major\" be included if enabled" }, - "CommissionFilter": { - "name": "Commission Priority", - "help": "Generally does not need to be modified or re-arranged but can do so if desired" + "PresetFilter": { + "name": "Preset Filter Select", + "help": "People without a basic knowledge of commission drops rates and spawn are recommended to use pre-optimized presets instead of writing custom filters\nIf the commission farming is running 24 hours a day, the (7x24 Commission Farming) filter should be used\nIt is not recommended to use the oil priority filter while running commission farming", + "chip": "Gems > Chip > Cube", + "chip_24h": "Gems > Chip > Cube (7x24 Commission Farming)", + "cube": "Gems > Cube > Oil", + "cube_24h": "Gems > Cube > Oil (7x24 Commission Farming)", + "oil": "Gems > Oil > Cube", + "custom": "Custom" + }, + "CustomFilter": { + "name": "Custom Commission Priority", + "help": "To use your own filter, set \"Preset Filter Select\" to \"Custom\". All options have been defined at " } }, "Tactical": { @@ -2028,11 +2054,11 @@ "OpsiExplore": { "_info": { "name": "OpSi Explore Settings", - "help": "At the beginning of each month OpSi is reset\nThe following must be satisfied:\n- OpSi story must be complete\nEach zone will be visited in a clockwise direction every 27 minutes until world has been been completely opened\nNo need to consume 5000 oil for special radar in OpSi shop" + "help": "At the beginning of each month OpSi is reset\nThe following must be satisfied:\n- OpSi story must be complete\nEach zone will be visited in a clockwise direction every 27 minutes until world has been been completely opened\nNo need to consume 5000 oil for special radar in OpSi voucher shop" }, "SpecialRadar": { "name": "SpecialRadar Bought", - "help": "Enable if you have purchased the special radar\nAllows Alas to explore OpSi continously without having to wait 27 minutes between each zone clear\nCannot be selected when not purchased and used" + "help": "Enable if you have purchased the special radar\nAllows Alas to explore OpSi continously without having to wait 27 minutes between each zone clear\nCannot be selected when not purchased and used manually" }, "ForceRun": { "name": "Force Run", @@ -2046,17 +2072,27 @@ "OpsiShop": { "_info": { "name": "OpSi Shop Settings", - "help": "Completes OpSi daily activities\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi shop" + "help": "Completes OpSi daily activities\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi voucher shop" }, "BuySupply": { "name": "Buy From Port Shops", "help": "Buy all items from port shops\nShop inventory consists of a fixed pool that is reset monthly, items not bought during a cycle has the chance of re-appearing and blocking preferable high value items" } }, + "OpsiVoucher": { + "_info": { + "name": "OpSi Voucher Settings", + "help": "Buy monthly items from the OpSi voucher shop\nThe following must be satisfied:\n- OpSi story must be complete" + }, + "Filter": { + "name": "Item Filter", + "help": "All options have been defined at " + } + }, "OpsiDaily": { "_info": { "name": "OpSi Daily Settings", - "help": "Completes OpSi daily activities\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi shop" + "help": "Completes OpSi daily activities\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi voucher shop" }, "DoMission": { "name": "Do Port Mission(s)", @@ -2070,7 +2106,7 @@ "OpsiObscure": { "_info": { "name": "OpSi Obscure Settings", - "help": "Clear obscure zones every 27 minutes\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi shop" + "help": "Clear obscure zones every 27 minutes\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi voucher shop" }, "ForceRun": { "name": "Force Run", @@ -2079,32 +2115,32 @@ }, "OpsiAbyssal": { "_info": { - "name": "Abyssal Zones", - "help": "Clear abyssal zones.\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi shop" + "name": "OpSi Abyssal Settings", + "help": "Clear abyssal zones\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi voucher shop" }, "ForceRun": { "name": "Force Run", - "help": "Ignore interval, clear all obscure zones, and use AP to submit scan orders (and submarine if enabled)\nGenerally not needed to force run, just run in intervals unless in a rush" + "help": "Ignore interval, clear all abyssal zones, and use AP to submit scan orders (and submarine if enabled)\nGenerally not needed to force run, just run in intervals unless in a rush" } }, "OpsiStronghold": { "_info": { - "name": "Siren Strongholds", + "name": "OpSi Siren Stronghold Settings", "help": "Clear the weekly siren strongholds.\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi shop" }, "ForceRun": { "name": "Force Run", - "help": "Ignore interval, clear all obscure zones, and use AP to submit scan orders (and submarine if enabled)\nGenerally not needed to force run, just run in intervals unless in a rush" + "help": "Ignore interval, clear all siren strongholds, and use AP to submit scan orders (and submarine if enabled)\nGenerally not needed to force run, just run in intervals unless in a rush" } }, "OpsiMeowfficerFarming": { "_info": { "name": "OpSi Meowfficer Farm Settings", - "help": "Attack target zones of the specified hazard level in a clockwise direction\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi shop" + "help": "Attack target zones of the specified hazard level in a clockwise direction\nThe following must be satisfied:\n- OpSi story must be complete\n- Task Opsi Explore enabled or consuming 5000 oil for special radar in OpSi voucher shop" }, "ActionPointPreserve": { "name": "Keep X Amount of AP", - "help": "Stops task if total AP (including AP boxes in inventory) is below this number, 500 is recommended\nThis value will be auto reduced to 300 at 3 days before OpSi reset and to 0 at day before reset" + "help": "Stops task if total AP (including AP boxes in inventory) is below this number, 1000 is recommended\nThis value will be auto reduced to 300 at 3 days before OpSi reset, or 500 if CL1 leveling enabled, and to 0 at day before reset" }, "HazardLevel": { "name": "Target Zone Hazard Level", @@ -2120,6 +2156,19 @@ "help": "Supports either Zone ID or Name in CN/EN/JP/TW, i.e. \"51\", \"NA Ocean SE Sector E\"\nIf specified, Alas will loop on this same zone after each clear\nUse default value 0 or clear the field to remove the specification\nZone information can be acquired from <./module/os/map_data.py>" } }, + "OpsiHazard1Leveling": { + "_info": { + "name": "CL1 leveling", + "help": "Warning: CL1 cannot gain unlimited action point. Please do not close other tasks in order to run CL1.\n\nAccording to the latest statistics of LmeSzinc, the action point output of CL1 is slightly higher than that of consumption, and you can gain a lot of experience while consuming operation coins in exchange for meowfficer farming.\nIt is recommended to carry 1 pre-loaded CV to obtain the highest average hourly experience benefit.\nSince buying action point in the store requires a large number of operation coins, this task will automatically enable meowfficer farming to supplement." + }, + "TargetZone": { + "name": "Target Zone ID", + "help": "Only attack target zone, which can be used to obtain world achievement stars or avoid the event that the map cannot be refreshed due to game bug", + "0": "No Specification", + "44": "44 | West Continental Shelf D", + "22": "22 | NA Ocean SW Sector B" + } + }, "Daemon": { "_info": { "name": "Semi-auto Clicking", @@ -2206,6 +2255,16 @@ "help": "Log back into the game when the game has been ended." } }, + "Storage": { + "_info": { + "name": "Task status", + "help": "Store internal status of the task. Clear manually when the task is abnormal" + }, + "Storage": { + "name": "Storage.Storage.name", + "help": "Storage.Storage.help" + } + }, "Gui": { "Aside": { "Install": "Install", @@ -2293,7 +2352,8 @@ "SSHNotInstall": "No SSH command in your system. Please refer to the tutorial to download or install one" }, "Text": { - "InvalidFeedBack": "Invalid format. Example: {0}" + "InvalidFeedBack": "Invalid format. Example: {0}", + "Clear": "Clear" } } } \ No newline at end of file diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 6904f34a1..04dec2fdc 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -198,6 +198,10 @@ "name": "Task.OpsiShop.name", "help": "Task.OpsiShop.help" }, + "OpsiVoucher": { + "name": "Task.OpsiVoucher.name", + "help": "Task.OpsiVoucher.help" + }, "OpsiDaily": { "name": "Task.OpsiDaily.name", "help": "Task.OpsiDaily.help" @@ -218,6 +222,10 @@ "name": "Task.OpsiMeowfficerFarming.name", "help": "Task.OpsiMeowfficerFarming.help" }, + "OpsiHazard1Leveling": { + "name": "Task.OpsiHazard1Leveling.name", + "help": "Task.OpsiHazard1Leveling.help" + }, "OpsiCrossMonth": { "name": "Task.OpsiCrossMonth.name", "help": "Task.OpsiCrossMonth.help" @@ -377,7 +385,8 @@ "ADB_nc": "ADB_nc", "uiautomator2": "uiautomator2", "aScreenCap": "aScreenCap", - "aScreenCap_nc": "aScreenCap_nc" + "aScreenCap_nc": "aScreenCap_nc", + "DroidCast": "DroidCast" }, "ControlMethod": { "name": "Emulator.ControlMethod.name", @@ -401,9 +410,13 @@ "name": "RestartEmulator._info.name", "help": "RestartEmulator._info.help" }, - "Enable": { - "name": "RestartEmulator.Enable.name", - "help": "RestartEmulator.Enable.help" + "ErrorRestart": { + "name": "RestartEmulator.ErrorRestart.name", + "help": "RestartEmulator.ErrorRestart.help" + }, + "DailyRestart": { + "name": "RestartEmulator.DailyRestart.name", + "help": "RestartEmulator.DailyRestart.help" }, "EmulatorType": { "name": "RestartEmulator.EmulatorType.name", @@ -648,6 +661,8 @@ "event_20200423_cn": "縹映る深緋の残響(復刻)", "event_20200326_cn": "闇靄払う銀翼", "event_20200227_cn": "凍絶の北海", + "war_archives_20210819_cn": "檔案 闇靄払う銀翼", + "war_archives_20200903_cn": "檔案 輝ける峡湾の星", "war_archives_20201029_cn": "檔案 激唱のユニバース", "war_archives_20200806_cn": "檔案 鉄血鮫とエニグマ", "war_archives_20210624_cn": "檔案 翳りし満ちる影の華", @@ -827,6 +842,7 @@ "do_not_use": "do_not_use", "hunt_only": "hunt_only", "boss_only": "boss_only", + "hunt_and_boss": "hunt_and_boss", "every_combat": "every_combat" }, "AutoSearchMode": { @@ -1159,9 +1175,19 @@ "name": "Commission.DoMajorCommission.name", "help": "Commission.DoMajorCommission.help" }, - "CommissionFilter": { - "name": "Commission.CommissionFilter.name", - "help": "Commission.CommissionFilter.help" + "PresetFilter": { + "name": "Commission.PresetFilter.name", + "help": "Commission.PresetFilter.help", + "chip": "chip", + "chip_24h": "chip_24h", + "cube": "cube", + "cube_24h": "cube_24h", + "oil": "oil", + "custom": "custom" + }, + "CustomFilter": { + "name": "Commission.CustomFilter.name", + "help": "Commission.CustomFilter.help" } }, "Tactical": { @@ -2053,6 +2079,16 @@ "help": "OpsiShop.BuySupply.help" } }, + "OpsiVoucher": { + "_info": { + "name": "OpsiVoucher._info.name", + "help": "OpsiVoucher._info.help" + }, + "Filter": { + "name": "OpsiVoucher.Filter.name", + "help": "OpsiVoucher.Filter.help" + } + }, "OpsiDaily": { "_info": { "name": "OpsiDaily._info.name", @@ -2120,6 +2156,19 @@ "help": "OpsiMeowfficerFarming.TargetZone.help" } }, + "OpsiHazard1Leveling": { + "_info": { + "name": "OpsiHazard1Leveling._info.name", + "help": "OpsiHazard1Leveling._info.help" + }, + "TargetZone": { + "name": "OpsiHazard1Leveling.TargetZone.name", + "help": "OpsiHazard1Leveling.TargetZone.help", + "0": "0", + "44": "44", + "22": "22" + } + }, "Daemon": { "_info": { "name": "Daemon._info.name", @@ -2206,6 +2255,16 @@ "help": "GameManager.AutoRestart.help" } }, + "Storage": { + "_info": { + "name": "Storage._info.name", + "help": "Storage._info.help" + }, + "Storage": { + "name": "Storage.Storage.name", + "help": "Storage.Storage.help" + } + }, "Gui": { "Aside": { "Install": "Gui.Aside.Install", @@ -2293,7 +2352,8 @@ "SSHNotInstall": "Gui.Remote.SSHNotInstall" }, "Text": { - "InvalidFeedBack": "Gui.Text.InvalidFeedBack" + "InvalidFeedBack": "Gui.Text.InvalidFeedBack", + "Clear": "Gui.Text.Clear" } } } \ No newline at end of file diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index feaacd663..101438c07 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -198,6 +198,10 @@ "name": "大世界商店", "help": "" }, + "OpsiVoucher": { + "name": "兑换商店", + "help": "" + }, "OpsiDaily": { "name": "大世界每日", "help": "" @@ -218,6 +222,10 @@ "name": "短猫相接", "help": "" }, + "OpsiHazard1Leveling": { + "name": "侵蚀1练级", + "help": "" + }, "OpsiCrossMonth": { "name": "跨月每日", "help": " Alas将在大世界跨月重置之前10分钟进入大世界,等待大世界重置但不退出大世界,然后完成新一天的大世界每日、隐秘海域、深渊海域和短猫相接,以获得额外的金菜。运行大世界每日时,按\"大世界每日\"任务设置运行,其余同理。\n 重要:Alas等待跨月期间,请不要操作游戏。" @@ -377,7 +385,8 @@ "ADB_nc": "ADB_nc", "uiautomator2": "uiautomator2", "aScreenCap": "aScreenCap", - "aScreenCap_nc": "aScreenCap_nc" + "aScreenCap_nc": "aScreenCap_nc", + "DroidCast": "DroidCast" }, "ControlMethod": { "name": "模拟器控制方案", @@ -401,9 +410,13 @@ "name": "重启模拟器", "help": "" }, - "Enable": { - "name": "启用重启模拟器", - "help": "在模拟器被关闭的时候自动启动模拟器\n在服务器日期刷新时重启模拟器以释放内存" + "ErrorRestart": { + "name": "出错时重启模拟器", + "help": "在无法连接到模拟器时自动重启模拟器" + }, + "DailyRestart": { + "name": "服务器刷新时重启模拟器", + "help": "每天主动重启模拟器以解决内存泄漏问题" }, "EmulatorType": { "name": "模拟器类型", @@ -648,6 +661,8 @@ "event_20200423_cn": "复刻苍红的回响", "event_20200326_cn": "微层混合", "event_20200227_cn": "北境序曲", + "war_archives_20210819_cn": "档案 微层混合", + "war_archives_20200903_cn": "档案 峡湾间的星辰", "war_archives_20201029_cn": "档案 激唱的UNIVERSE", "war_archives_20200806_cn": "档案 最重要的宝物", "war_archives_20210624_cn": "档案 浮樱影华", @@ -823,10 +838,11 @@ }, "Mode": { "name": "潜艇出击方案", - "help": "", + "help": "提醒: '狩猎及BOSS战'为'仅狩猎'与'仅BOSS战'的混合,它会在道中进行狩猎打击,并在BOSS战尝试召唤潜艇。", "do_not_use": "不使用", "hunt_only": "仅狩猎", "boss_only": "仅BOSS战", + "hunt_and_boss": "狩猎及BOSS战", "every_combat": "每战出击" }, "AutoSearchMode": { @@ -837,7 +853,7 @@ }, "DistanceToBoss": { "name": "BOSS战前将潜艇移动到BOSS附近", - "help": "仅在\"潜艇出击方案\"为\"仅BOSS战\"时生效\n选择\"距离BOSS X格\"需要保证潜艇狩猎范围能覆盖到BOSS,距离使用曼哈顿距离计算,选择\"使用远洋支援\"需要潜艇队伍里有U522/达芬奇", + "help": "仅在\"潜艇出击方案\"为\"仅BOSS战\"及\"狩猎及BOSS战\",时生效\n选择\"距离BOSS X格\"需要保证潜艇狩猎范围能覆盖到BOSS,距离使用曼哈顿距离计算,选择\"使用远洋支援\"需要潜艇队伍里有U522/达芬奇", "to_boss_position": "至 BOSS 所在位置", "1_grid_to_boss": "距离 BOSS 1 格", "2_grid_to_boss": "距离 BOSS 2 格", @@ -1159,9 +1175,19 @@ "name": "做主要委托(1200油/1000油委托)", "help": "" }, - "CommissionFilter": { + "PresetFilter": { "name": "委托过滤器", - "help": "一般不需要修改" + "help": "如果对委托的产出和收益没有足够的了解,不建议编写自定义过滤器,建议使用预优化委托过滤器\n如果24小时运行刷紧急委托功能,应当使用(7x24刷委托)的过滤器\n不推荐在运行刷紧急委托功能的同时使用石油优先过滤器", + "chip": "钻石>心智>魔方", + "chip_24h": "钻石>心智>魔方(7x24刷委托)", + "cube": "钻石>魔方>石油", + "cube_24h": "钻石>魔方>石油(7x24刷委托)", + "oil": "钻石>石油>魔方", + "custom": "自定义" + }, + "CustomFilter": { + "name": "自定义委托过滤器", + "help": "使用自定义过滤器需将 \"委托过滤器\" 设置为 \"自定义\",并阅读 https://github.com/LmeSzinc/AzurLaneAutoScript/wiki/filter_string_cn" } }, "Tactical": { @@ -2053,6 +2079,16 @@ "help": "每月港口商店可购买商品是固定的,未购买的物品下次仍会出现,并阻塞高价值物品,因此需要购买全部" } }, + "OpsiVoucher": { + "_info": { + "name": "兑换商店(每月白票商店)", + "help": "购买每月白票商店\n使用此功能前必须满足以下条件:\n- 通关大世界主线任务" + }, + "Filter": { + "name": "商店过滤器", + "help": "参考文档 https://github.com/LmeSzinc/AzurLaneAutoScript/wiki/reward_shop_filter_string" + } + }, "OpsiDaily": { "_info": { "name": "大世界每日", @@ -2104,7 +2140,7 @@ }, "ActionPointPreserve": { "name": "保留 X 点行动力", - "help": "行动力低于 X 后停止,自动打开行动力箱子,X 包含箱子中的行动力,建议保留 500 点行动力\n这个值将在大世界重置前3天自动减少至300点,并在最后一天自动减少至0点" + "help": "行动力低于 X 后停止,自动打开行动力箱子,X 包含箱子中的行动力,建议保留1000行动力给侵蚀1练级\n这个值将在大世界重置前3天自动减少至200点,如果启用侵蚀1练级则改为500点,并在最后一天自动减少至0点" }, "HazardLevel": { "name": "侵蚀等级", @@ -2120,6 +2156,19 @@ "help": "仅出击指定的海域,可以用来获取大世界成就星星\n支持海域ID、国服/国际服/日服/台服海域名称,例如 \"51\", \"NA海域东南E\", \"NA Ocean SE Sector E\"\n填入默认值0,或者删除数值即可取消指定" } }, + "OpsiHazard1Leveling": { + "_info": { + "name": "侵蚀1练级", + "help": "警告:侵蚀1无法无限获取行动力,请不要为了运行侵蚀1关闭其他任务\n\n根据LmeSzinc最新的统计,侵蚀1产出的行动力略高于消耗,可以在大量获取经验的同时消耗作战补给凭证换取短猫收益\n推荐携带1艘预装填航母以获取最高的时均经验收益\n由于在商店购买行动力需要大量作战补给凭证,启用本功能后会自动启用短猫相接来补充" + }, + "TargetZone": { + "name": "指定海域", + "help": "仅出击指定的海域,可以用来获取大世界成就星星或规避游戏BUG导致的无法刷新海域", + "0": "不指定", + "44": "44 | 西大陆架D", + "22": "22 | NA海域西南B" + } + }, "Daemon": { "_info": { "name": "半自动点击", @@ -2206,6 +2255,16 @@ "help": "游戏被强制结束后自动登录游戏" } }, + "Storage": { + "_info": { + "name": "任务状态", + "help": "存放任务内部状态,任务异常时可以手动清除" + }, + "Storage": { + "name": "Storage.Storage.name", + "help": "Storage.Storage.help" + } + }, "Gui": { "Aside": { "Install": "安装", @@ -2293,7 +2352,8 @@ "SSHNotInstall": "系统中没有 ssh 工具,请参考教程下载或安装 ssh" }, "Text": { - "InvalidFeedBack": "格式错误。 示例:{0}" + "InvalidFeedBack": "格式错误。 示例:{0}", + "Clear": "清除" } } } \ No newline at end of file diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index e1815c734..839583230 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -198,6 +198,10 @@ "name": "大世界商店", "help": "" }, + "OpsiVoucher": { + "name": "兌換商店", + "help": "" + }, "OpsiDaily": { "name": "大世界每日", "help": "" @@ -218,6 +222,10 @@ "name": "短貓相接", "help": "" }, + "OpsiHazard1Leveling": { + "name": "侵蝕1練級", + "help": "" + }, "OpsiCrossMonth": { "name": "跨月每日", "help": " Alas將在大世界跨月重置之前10分鐘進入大世界,等待大世界重置但不退出大世界,然後完成新一天的大世界每日、隱秘海域、深淵海域和短貓相接,以獲得額外的金菜。運行大世界每日時,按\"大世界每日\"任務設置運行,其餘同理。\n 重要:Alas等待跨月期間,請不要操作遊戲。" @@ -377,7 +385,8 @@ "ADB_nc": "ADB_nc", "uiautomator2": "uiautomator2", "aScreenCap": "aScreenCap", - "aScreenCap_nc": "aScreenCap_nc" + "aScreenCap_nc": "aScreenCap_nc", + "DroidCast": "DroidCast" }, "ControlMethod": { "name": "模擬器控制方案", @@ -401,9 +410,13 @@ "name": "重啓模擬器", "help": "" }, - "Enable": { + "ErrorRestart": { "name": "啟用重啓模擬器", - "help": "在模擬器被關閉的時候自動啟動模擬器\n在服務器日期重繪時重啓模擬器以釋放記憶體" + "help": "在模擬器被關閉的時候自動啟動模擬器" + }, + "DailyRestart": { + "name": "服務器重繪時重啓模擬器", + "help": "每天主動重啓模擬器以解决記憶體洩漏問題" }, "EmulatorType": { "name": "模擬器類型", @@ -648,6 +661,8 @@ "event_20200423_cn": "Crimson Echoes Rerun", "event_20200326_cn": "Microlayer Medley", "event_20200227_cn": "Northern Overture", + "war_archives_20210819_cn": "archives Microlayer Medley", + "war_archives_20200903_cn": "archives Stars of the Shimmering Fjord", "war_archives_20201029_cn": "archives Universe in Unison", "war_archives_20200806_cn": "archives The Enigma and the Shark", "war_archives_20210624_cn": "archives Swirling Cherry Blossoms", @@ -823,10 +838,11 @@ }, "Mode": { "name": "潛艇出擊方案", - "help": "", + "help": "提醒: '狩獵及BOSS戰'為'僅狩獵'與'僅BOSS戰'的混合,它會在道中進行狩獵打擊,並在BOSS戰嘗試召喚潛艇。", "do_not_use": "不使用", "hunt_only": "僅狩獵", "boss_only": "僅BOSS", + "hunt_and_boss": "狩獵及BOSS戰", "every_combat": "每戰出擊" }, "AutoSearchMode": { @@ -837,7 +853,7 @@ }, "DistanceToBoss": { "name": "BOSS戰前將潛艇移動到BOSS附近", - "help": "僅在\"潛艇出擊方案\"為\"僅BOSS戰\"時生效\n選擇\"距離BOSS X格\"需要保證潛艇狩獵範圍能覆蓋到BOSS,距離使用曼哈頓距離計算,選擇\"使用遠洋支援\"需要潛艇隊伍裡有U522或是達芬奇", + "help": "僅在\"潛艇出擊方案\"為\"僅BOSS戰\"及\"狩獵及BOSS戰\"時生效\n選擇\"距離BOSS X格\"需要保證潛艇狩獵範圍能覆蓋到BOSS,距離使用曼哈頓距離計算,選擇\"使用遠洋支援\"需要潛艇隊伍裡有U522或是達芬奇", "to_boss_position": "至 BOSS 所在位置", "1_grid_to_boss": "距離 BOSS 1 格", "2_grid_to_boss": "距離 BOSS 2 格", @@ -1159,9 +1175,19 @@ "name": "做主要委託 (1200油/1000油委託)", "help": "" }, - "CommissionFilter": { - "name": "委託過濾器", - "help": "一般不需要修改" + "PresetFilter": { + "name": "委托過濾器", + "help": "如果對委托的產出和收益沒有足夠的了解,不建議編寫自定義過濾器,建議使用預優化委托過濾器\n如果24小時運行刷緊急委託功能,應當使用24小時版的過濾器\n不推薦在運行刷緊急委託功能的同時使用石油優先過濾器", + "chip": "鑽石>心智>魔方", + "chip_24h": "鑽石>心智>魔方(7x24刷委託)", + "cube": "鑽石>魔方>石油", + "cube_24h": "鑽石>魔方>石油(7x24刷委託)", + "oil": "鑽石>石油>魔方", + "custom": "自定義" + }, + "CustomFilter": { + "name": "自定義委托過濾器", + "help": "使用自定義過濾器需將 \"委托過濾器\" 設置為 \"自定義\",並閱讀 https://github.com/LmeSzinc/AzurLaneAutoScript/wiki/filter_string_cn" } }, "Tactical": { @@ -2053,6 +2079,16 @@ "help": "每月港口商店可購買商品是固定的,未購買的物品下次仍會出現,並阻擋高價值物品,因此需要購買全部" } }, + "OpsiVoucher": { + "_info": { + "name": "兌換商店(每月白票商店)", + "help": "購買每月白票商店\n使用此功能前必須滿足以下條件:\n- 通關大世界主線任務" + }, + "Filter": { + "name": "商店過濾器", + "help": "參考文檔 https://github.com/LmeSzinc/AzurLaneAutoScript/wiki/reward_shop_filter_string" + } + }, "OpsiDaily": { "_info": { "name": "大世界每日", @@ -2104,7 +2140,7 @@ }, "ActionPointPreserve": { "name": "保留 X 點行動力", - "help": "行動力低於 X 後停止,自動打開行動力箱子,X 包含箱子中的行動力,建議保留 500 點行動力\n這個值將在大世界重置前3天自動減少至300點,並在最後一天自動減少至0點" + "help": "行動力低於 X 後停止,自動打開行動力箱子,X 包含箱子中的行動力,建議保留 1000 點行動力给侵蝕1練級\n這個值將在大世界重置前3天自動減少至300點,如果啟用侵蝕1練級則改為500點,並在最後一天自動減少至0點" }, "HazardLevel": { "name": "侵蝕等級", @@ -2120,6 +2156,19 @@ "help": "僅出擊指定的海域,可以用來獲取大世界成就星星\n自動更新的數值,填0可重置進度,重置後自動跳過已開荒的海域\n支持海域ID、國服/國際服/日服/台服海域名稱,例如 \"51\", \"NA海域東南E\", \"NA Ocean SE Sector E\"\n填入默認值0,或者刪除數值即可取消指定" } }, + "OpsiHazard1Leveling": { + "_info": { + "name": "侵蝕1練級", + "help": "警告:侵蝕1無法無限獲取行動力,請不要為了運行侵蝕1關閉其他任務\n\n根據LmeSzinc最新的統計,侵蝕1產出的行動力略高於消耗,可以在大量獲取經驗的同時消耗作戰補給憑證換取短猫收益\n推薦攜帶1艘預裝填航母以獲取最高的時均經驗收益\n由於在商店購買行動力需要大量作戰補給憑證,啟用本功能後會自動啟用短猫相接來補充" + }, + "TargetZone": { + "name": "指定海域", + "help": "僅出擊指定的海域,可以用來獲取大世界成就星星或規避遊戲BUG導致的無法重繪海域", + "0": "不指定", + "44": "44 | 西大陸棚D", + "22": "22 | NA海域西南B" + } + }, "Daemon": { "_info": { "name": "半自動點擊", @@ -2155,7 +2204,7 @@ }, "AdbncScreenshot": { "name": "測試 ADB 截圖", - "help": "Benchmark.AdbncScreenshot.help" + "help": "" }, "Uiautomator2Screenshot": { "name": "測試 uiautomator2 截圖", @@ -2206,6 +2255,16 @@ "help": "遊戲被強制結束後自動登錄遊戲" } }, + "Storage": { + "_info": { + "name": "任務狀態", + "help": "存放任務內部狀態,任務异常時可以手動清除" + }, + "Storage": { + "name": "Storage.Storage.name", + "help": "Storage.Storage.help" + } + }, "Gui": { "Aside": { "Install": "安裝", @@ -2293,7 +2352,8 @@ "SSHNotInstall": "系統中沒有 ssh 工具,請參閱教程下載安裝 ssh" }, "Text": { - "InvalidFeedBack": "格式錯誤。 示例:{0}" + "InvalidFeedBack": "格式錯誤。 示例:{0}", + "Clear": "清除" } } } \ No newline at end of file diff --git a/module/config/redirect_utils/shop_filter.py b/module/config/redirect_utils/shop_filter.py index 208a44ff8..49f6cdf4e 100644 --- a/module/config/redirect_utils/shop_filter.py +++ b/module/config/redirect_utils/shop_filter.py @@ -1,6 +1,6 @@ import re -FILTER_REGEX = re.compile( +FILTER_REGEX_SERIES = re.compile( '(pr|dr)' '([1-4]' @@ -28,7 +28,7 @@ def bp_redirect(value): PRBP = PR; PR1BP = PRS1; PROdinBP = PROdin/PROdinS3 likewise for DR variants """ - matches = re.findall(FILTER_REGEX, value) + matches = re.findall(FILTER_REGEX_SERIES, value) if not matches: return value @@ -45,5 +45,41 @@ def bp_redirect(value): return value +FILTER_REGEX_VOUCHER = re.compile( + '(logger)' + + '(archive)?' + + '(t[1-6])?', + flags=re.IGNORECASE) + + +def voucher_redirect(value): + """ + Redirects voucher shop filter to prevents users + from using banned strings i.e. Logger, LoggerT[1-6], + LoggerArchive, or LoggerArchiveT[1-6] + Banned strings are used for special circumstances + handled by ALAS + """ + matches = re.findall(FILTER_REGEX_VOUCHER, value) + if not matches: + return value + + for match in matches: + flat = ''.join(match) + pattern = rf'\b{flat}\b' + if (match[2] and match[1]) or match[1]: + value = re.sub(pattern, '', value) + value = re.sub('\>\s*\>', '>', value) + value = re.sub('\>\s*$', '', value) + elif match[2]: + value = re.sub(pattern, f'LoggerAbyssal{match[2].upper()} > LoggerObscure{match[2].upper()}', value) + else: + value = re.sub(pattern, f'LoggerAbyssal > LoggerObscure', value) + + return value + if __name__ == '__main__': print(bp_redirect('PlateGeneralT1 > DRAgirBP > CatT3 > PROdinBP > Chip > PR1BP > PRBP > DRDrakeBP > DR2BP')) + print(voucher_redirect('Coin > HECombatPlan > LoggerArchive > TuningCombatT2 > LoggerArchiveT1 > LoggerT6 > Logger')) diff --git a/module/device/connection.py b/module/device/connection.py index ded5c756d..e67908ec9 100644 --- a/module/device/connection.py +++ b/module/device/connection.py @@ -284,6 +284,7 @@ class Connection(ConnectionAttr): f'client can send data to {host_port[2]}:{host_port[3]}') server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(host_port[:2]) + server.settimeout(5) server.listen(5) return server @@ -494,6 +495,7 @@ class Connection(ConnectionAttr): logger.info(msg) del_cached_property(self, 'hermit_session') + del_cached_property(self, 'droidcast_session') del_cached_property(self, 'minitouch_builder') del_cached_property(self, 'reverse_server') @@ -538,7 +540,11 @@ class Connection(ConnectionAttr): logger.info('Install uiautomator2') init = u2.init.Initer(self.adb, loglevel=logging.DEBUG) init.set_atx_agent_addr('127.0.0.1:7912') - init.install() + try: + init.install() + except ConnectionError: + u2.init.GITHUB_BASEURL = 'http://tool.appetizer.io/openatx' + init.install() self.uninstall_minicap() def uninstall_minicap(self): diff --git a/module/device/connection_attr.py b/module/device/connection_attr.py index 4389e0cfb..d5a8fdee6 100644 --- a/module/device/connection_attr.py +++ b/module/device/connection_attr.py @@ -248,7 +248,10 @@ class ConnectionAttr: device = u2.connect(self.serial) else: # Normal uiautomator2 - device = u2.connect(self.serial) + if self.serial.startswith('emulator-') or self.serial.startswith('127.0.0.1:'): + device = u2.connect_usb(self.serial) + else: + device = u2.connect(self.serial) # Stay alive device.set_new_command_timeout(604800) diff --git a/module/device/emulator.py b/module/device/emulator.py index fdd25e5c7..2c026275e 100644 --- a/module/device/emulator.py +++ b/module/device/emulator.py @@ -214,7 +214,7 @@ class EmulatorManager(Connection): try: return super(EmulatorManager, self).adb_connect(serial) except EmulatorNotRunningError: - if self.config.RestartEmulator_Enable \ + if self.config.RestartEmulator_ErrorRestart \ and self.emulator_restart(): return True raise RequestHumanTakeover @@ -302,16 +302,11 @@ class EmulatorManager(Connection): self.sleep(2) return False - logger.warning('Kill emulator failed for 3 times, please check your settings') - raise RequestHumanTakeover - - def emulator_restart(self, kill=True): + def emulator_restart(self): serial, _ = get_serial_pair(self.serial) if serial is None: serial = self.serial - if not self.config.RestartEmulator_Enable: - return False if os.name != 'nt': logger.warning('Restart simulator only works under Windows platform') return False @@ -329,4 +324,5 @@ class EmulatorManager(Connection): if self.emulator_start(serial, emulator, multi_id): return True + logger.warning('Restart emulator failed for 3 times, please check your settings') raise RequestHumanTakeover diff --git a/module/device/method/droidcast.py b/module/device/method/droidcast.py new file mode 100644 index 000000000..989848a3e --- /dev/null +++ b/module/device/method/droidcast.py @@ -0,0 +1,181 @@ +import typing as t +from functools import wraps + +import cv2 +import numpy as np +import requests +from adbutils.errors import AdbError + +from module.base.decorator import cached_property, del_cached_property +from module.device.method.uiautomator_2 import Uiautomator2, ProcessInfo +from module.device.method.utils import (RETRY_DELAY, RETRY_TRIES, handle_adb_error, PackageNotInstalled) +from module.exception import RequestHumanTakeover +from module.logger import logger + + +def retry(func): + @wraps(func) + def retry_wrapper(self, *args, **kwargs): + """ + Args: + self (Adb): + """ + init = None + for _ in range(RETRY_TRIES): + try: + if callable(init): + self.sleep(RETRY_DELAY) + init() + return func(self, *args, **kwargs) + # Can't handle + except RequestHumanTakeover: + break + # When adb server was killed + except ConnectionResetError as e: + logger.error(e) + + def init(): + self.adb_reconnect() + # AdbError + except AdbError as e: + if handle_adb_error(e): + def init(): + self.adb_reconnect() + else: + break + # Package not installed + except PackageNotInstalled as e: + logger.error(e) + + def init(): + self.detect_package() + # DroidCast not running + # requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response')) + except requests.exceptions.ConnectionError as e: + logger.error(e) + + def init(): + self.droidcast_init() + # Unknown, probably a trucked image + except Exception as e: + logger.exception(e) + + def init(): + pass + + logger.critical(f'Retry {func.__name__}() failed') + raise RequestHumanTakeover + + return retry_wrapper + + +class DroidCast(Uiautomator2): + """ + DroidCast, another screenshot method, https://github.com/rayworks/DroidCast + + DroidCast is Added to ALAS for MuMu X support. + """ + + _droidcast_port: int = 0 + + @cached_property + def droidcast_session(self): + session = requests.Session() + session.trust_env = False # Ignore proxy + self._droidcast_port = self.adb_forward('tcp:53516') + return session + + def droidcast_url(self, url='/screenshot?format=png'): + """ + Check APIs from source code: + https://github.com/rayworks/DroidCast/blob/master/app/src/main/java/com/rayworks/droidcast/Main.java + + Available APIs: + - /screenshot + To get JPG screenshots. + - /screenshot?format=png + To get PNG screenshots. + - /screenshot?format=webp + To get WEBP screenshots. + - /src + Websocket to get JPG screenshots. + + Note that /screenshot?format=jpg is unavailable. + """ + return f'http://127.0.0.1:{self._droidcast_port}{url}' + + def droidcast_init(self): + logger.hr('Droidcast init') + self.droidcast_stop() + + logger.info('Pushing DroidCast apk') + self.adb_push(self.config.DROIDCAST_FILEPATH_LOCAL, self.config.DROIDCAST_FILEPATH_REMOTE) + + logger.info('Starting DroidCast apk') + # CLASSPATH=/data/local/tmp/DroidCast.apk app_process / com.rayworks.droidcast.Main > /dev/null + resp = self.u2_shell_background([ + 'CLASSPATH=/data/local/tmp/DroidCast.apk', + 'app_process', + '/', + 'com.rayworks.droidcast.Main', + '>', + '/dev/null' + ]) + logger.info(resp) + + del_cached_property(self, 'droidcast_session') + _ = self.droidcast_session + logger.attr('DroidCast', self.droidcast_url()) + self.droidcast_wait_startup() + + @retry + def screenshot_droidcast(self): + image = self.droidcast_session.get(self.droidcast_url(), timeout=3).content + image = np.frombuffer(image, np.uint8) + image = cv2.imdecode(image, cv2.IMREAD_COLOR) + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + return image + + def droidcast_wait_startup(self): + """ + Wait until DroidCast startup completed. + """ + for _ in range(6): + self.sleep(0.5) + try: + resp = self.droidcast_session.get(self.droidcast_url('/'), timeout=3) + # Route `/` is unavailable, but 404 means startup completed + if resp.status_code == 404: + logger.attr('DroidCast', 'online') + return True + except requests.exceptions.ConnectionError: + logger.attr('DroidCast', 'offline') + + logger.warning('Wait DroidCast startup timeout, assume started') + return False + + def droidcast_uninstall(self): + """ + Stop all DroidCast processes and remove DroidCast APK. + DroidCast has't been installed but a JAVA class call, uninstall is a file delete. + """ + self.droidcast_stop() + logger.info('Removing DroidCast') + self.adb_shell(["rm", self.config.DROIDCAST_FILEPATH_REMOTE]) + + def _droidcast_proc(self) -> t.List[ProcessInfo]: + """ + List all DroidCast processes. + """ + processes = self.proc_list_uiautomato2() + processes = [proc for proc in processes if 'com.rayworks.droidcast.Main' in proc.cmdline] + return processes + + def droidcast_stop(self): + """ + Stop all DroidCast processes. + """ + logger.info('Stopping DroidCast') + for proc in self._droidcast_proc(): + logger.info(f'Kill pid={proc.pid}') + self.adb_shell(['kill', '-s', 9, proc.pid]) diff --git a/module/device/method/uiautomator_2.py b/module/device/method/uiautomator_2.py index b41d18797..8a2f0d9cb 100644 --- a/module/device/method/uiautomator_2.py +++ b/module/device/method/uiautomator_2.py @@ -1,5 +1,8 @@ +import typing as t +from dataclasses import dataclass from functools import wraps from json.decoder import JSONDecodeError +from subprocess import list2cmdline import uiautomator2 as u2 from adbutils.errors import AdbError @@ -89,6 +92,22 @@ def retry(func): return retry_wrapper +@dataclass +class ProcessInfo: + pid: int + ppid: int + thread_count: int + cmdline: str + name: str + + +@dataclass +class ShellBackgroundResponse: + success: bool + pid: int + description: str + + class Uiautomator2(Connection): @retry def screenshot_uiautomator2(self): @@ -211,3 +230,48 @@ class Uiautomator2(Connection): content = self.u2.dump_hierarchy(compressed=True) hierarchy = etree.fromstring(content.encode('utf-8')) return hierarchy + + @retry + def proc_list_uiautomato2(self) -> t.List[ProcessInfo]: + """ + Get info about current processes. + """ + resp = self.u2.http.get("/proc/list", timeout=10) + resp.raise_for_status() + result = [ + ProcessInfo( + pid=proc['pid'], + ppid=proc['ppid'], + thread_count=proc['threadCount'], + cmdline=' '.join(proc['cmdline']), + name=proc['name'], + ) for proc in resp.json() + ] + return result + + @retry + def u2_shell_background(self, cmdline, timeout=10) -> ShellBackgroundResponse: + """ + Run at background. + + Note that this function will always return a success response, + as this is a untested and hidden method in ATX. + """ + if isinstance(cmdline, (list, tuple)): + cmdline = list2cmdline(cmdline) + elif isinstance(cmdline, str): + cmdline = cmdline + else: + raise TypeError("cmdargs type invalid", type(cmdline)) + + data = dict(command=cmdline, timeout=str(timeout)) + ret = self.u2.http.post("/shell/background", data=data, timeout=timeout + 10) + ret.raise_for_status() + + resp = ret.json() + resp = ShellBackgroundResponse( + success=bool(resp.get('success', False)), + pid=resp.get('pid', 0), + description=resp.get('description', '') + ) + return resp diff --git a/module/device/screenshot.py b/module/device/screenshot.py index 3465288a6..d3244e860 100644 --- a/module/device/screenshot.py +++ b/module/device/screenshot.py @@ -12,13 +12,13 @@ from module.base.timer import Timer, timer from module.base.utils import get_color, image_size, limit_in, save_image from module.device.method.adb import Adb from module.device.method.ascreencap import AScreenCap -from module.device.method.uiautomator_2 import Uiautomator2 +from module.device.method.droidcast import DroidCast from module.device.method.wsa import WSA from module.exception import RequestHumanTakeover, ScriptError from module.logger import logger -class Screenshot(Adb, WSA, Uiautomator2, AScreenCap): +class Screenshot(Adb, WSA, DroidCast, AScreenCap): _screen_size_checked = False _screen_black_checked = False _minicap_uninstalled = False @@ -34,6 +34,7 @@ class Screenshot(Adb, WSA, Uiautomator2, AScreenCap): 'uiautomator2': self.screenshot_uiautomator2, 'aScreenCap': self.screenshot_ascreencap, 'aScreenCap_nc': self.screenshot_ascreencap_nc, + 'DroidCast': self.screenshot_droidcast, } @timer @@ -228,8 +229,12 @@ class Screenshot(Adb, WSA, Uiautomator2, AScreenCap): else: logger.warning(f'Received pure black screenshots from emulator, color: {color}') logger.warning(f'Screenshot method `{self.config.Emulator_ScreenshotMethod}` ' - f'may not work on emulator `{self.serial}`') - logger.warning('Or the emulator is not fully started') + f'may not work on emulator `{self.serial}`, or the emulator is not fully started') + if self.serial == '127.0.0.1:7555': + if self.config.Emulator_ScreenshotMethod == 'DroidCast': + self.droidcast_stop() + else: + logger.warning('If you are using MuMu X, please set screenshot method to DroidCast') self._screen_black_checked = False return False else: diff --git a/module/handler/fast_forward.py b/module/handler/fast_forward.py index 0e2c39a22..4b84c32a4 100644 --- a/module/handler/fast_forward.py +++ b/module/handler/fast_forward.py @@ -224,7 +224,7 @@ class FastForwardHandler(AutoSearchHandler): @property def is_call_submarine_at_boss(self): - return self.config.SUBMARINE and self.config.Submarine_Mode == 'boss_only' + return self.config.SUBMARINE and self.config.Submarine_Mode in ['boss_only', 'hunt_and_boss'] def handle_auto_submarine_call_disable(self): """ diff --git a/module/handler/info_handler.py b/module/handler/info_handler.py index da9220e39..dc2120c2f 100644 --- a/module/handler/info_handler.py +++ b/module/handler/info_handler.py @@ -73,28 +73,32 @@ class InfoHandler(ModuleBase): """ _popup_offset = (3, 30) - def handle_popup_confirm(self, name='', interval=2): - if self.appear(POPUP_CANCEL, offset=self._popup_offset) \ - and self.appear(POPUP_CONFIRM, offset=self._popup_offset, interval=interval): + def handle_popup_confirm(self, name='', offset=None, interval=2): + if offset is None: + offset = self._popup_offset + if self.appear(POPUP_CANCEL, offset=offset) \ + and self.appear(POPUP_CONFIRM, offset=offset, interval=interval): POPUP_CONFIRM.name = POPUP_CONFIRM.name + '_' + name self.device.click(POPUP_CONFIRM) POPUP_CONFIRM.name = POPUP_CONFIRM.name[:-len(name) - 1] return True return False - def handle_popup_cancel(self, name='', interval=2): - if self.appear(POPUP_CONFIRM, offset=self._popup_offset) \ - and self.appear(POPUP_CANCEL, offset=self._popup_offset, interval=interval): + def handle_popup_cancel(self, name='', offset=None, interval=2): + if offset is None: + offset = self._popup_offset + if self.appear(POPUP_CONFIRM, offset=offset) \ + and self.appear(POPUP_CANCEL, offset=offset, interval=interval): POPUP_CANCEL.name = POPUP_CANCEL.name + '_' + name self.device.click(POPUP_CANCEL) POPUP_CANCEL.name = POPUP_CANCEL.name[:-len(name) - 1] return True return False - def handle_popup_single(self, name='', offset=None): + def handle_popup_single(self, name='', offset=None, interval=2): if offset is None: offset = self._popup_offset - if self.appear(GET_MISSION, offset=offset, interval=2): + if self.appear(GET_MISSION, offset=offset, interval=interval): prev_name = GET_MISSION.name GET_MISSION.name = POPUP_CONFIRM.name + '_' + name self.device.click(GET_MISSION) diff --git a/module/handler/login.py b/module/handler/login.py index 078bd07fd..71a076f1d 100644 --- a/module/handler/login.py +++ b/module/handler/login.py @@ -153,7 +153,7 @@ class LoginHandler(UI): def app_restart(self): logger.hr('App restart') - if self.config.RestartEmulator_Enable \ + if self.config.RestartEmulator_DailyRestart\ and self.config.Scheduler_NextRun.strftime('%H:%M:%S') \ == get_server_next_update(self.config.Scheduler_ServerUpdate).strftime('%H:%M:%S'): self.device.emulator_restart() diff --git a/module/handler/strategy.py b/module/handler/strategy.py index ba28f137f..59882aef4 100644 --- a/module/handler/strategy.py +++ b/module/handler/strategy.py @@ -95,7 +95,7 @@ class StrategyHandler(InfoHandler): self.strategy_set_execute( formation_index=expected_formation, sub_view=False, - sub_hunt=bool(self.config.Submarine_Fleet) and self.config.Submarine_Mode == 'hunt_only' + sub_hunt=bool(self.config.Submarine_Fleet) and self.config.Submarine_Mode in ['boss_only', 'hunt_and_boss'] ) self.strategy_close() self.__setattr__(f'fleet_{index}_formation_fixed', True) diff --git a/module/os/fleet.py b/module/os/fleet.py index f3d5d6878..32cd00398 100644 --- a/module/os/fleet.py +++ b/module/os/fleet.py @@ -298,7 +298,7 @@ class OSFleet(OSCamera, Combat, Fleet, OSAsh): raise MapWalkError('walk_out_of_step') else: continue - if self.handle_popup_confirm(): + if self.handle_popup_confirm('WALK_UNTIL_STABLE'): # Confirm to submit items, in siren scanning devices confirm_timer.reset() continue diff --git a/module/os/map.py b/module/os/map.py index cac35e23c..5f66a8a15 100644 --- a/module/os/map.py +++ b/module/os/map.py @@ -1,21 +1,30 @@ +import time +from sys import maxsize + import inflection from module.base.timer import Timer +from module.combat.assets import PAUSE from module.config.utils import get_os_reset_remain from module.exception import CampaignEnd, RequestHumanTakeover -from module.exception import GameTooManyClickError +from module.exception import GameTooManyClickError, GameStuckError from module.exception import MapWalkError, ScriptError +from module.exercise.assets import QUIT_CONFIRM, QUIT_RECONFIRM from module.logger import logger from module.map.map import Map from module.os.assets import FLEET_EMP_DEBUFF from module.os.fleet import OSFleet from module.os.globe_camera import GlobeCamera from module.os.globe_operation import RewardUncollectedError -from module.os_handler.assets import AUTO_SEARCH_OS_MAP_OPTION_OFF, AUTO_SEARCH_OS_MAP_OPTION_ON +from module.os_handler.assets import AUTO_SEARCH_OS_MAP_OPTION_OFF, \ + AUTO_SEARCH_OS_MAP_OPTION_ON, AUTO_SEARCH_REWARD +from module.os_handler.strategic import StrategicSearchHandler +from module.ui.assets import GOTO_MAIN +from module.ui.page import page_main from module.ui.ui import page_os -class OSMap(OSFleet, Map, GlobeCamera): +class OSMap(OSFleet, Map, GlobeCamera, StrategicSearchHandler): def os_init(self): """ Call this method before doing any Operation functions. @@ -69,8 +78,8 @@ class OSMap(OSFleet, Map, GlobeCamera): self.map_exit() # Clear current zone - if self.zone.zone_id == 154: - logger.info('In zone 154, skip running first auto search') + if self.zone.zone_id in [22, 44, 154]: + logger.info('In zone 22, 44, 154, skip running first auto search') self.handle_ash_beacon_attack() else: self.run_auto_search(rescan=False) @@ -114,8 +123,8 @@ class OSMap(OSFleet, Map, GlobeCamera): if self.zone == zone: if refresh: logger.info('Goto another zone to refresh current zone') - return self.globe_goto(self.zone_nearest_azur_port(self.zone), - types=('SAFE', 'DANGEROUS'), refresh=False) + self.globe_goto(self.zone_nearest_azur_port(self.zone), + types=('SAFE', 'DANGEROUS'), refresh=False) else: logger.info('Already at target zone') return False @@ -128,7 +137,7 @@ class OSMap(OSFleet, Map, GlobeCamera): # IN_GLOBE if not self.is_in_globe(): logger.warning('Trying to move in globe, but not in os globe map') - raise ScriptError('Trying to move in globe, but not in os globe map') + raise GameStuckError # self.ensure_no_zone_pinned() self.globe_update() self.globe_focus_to(zone) @@ -349,37 +358,38 @@ class OSMap(OSFleet, Map, GlobeCamera): logger.warning('Failed to solve EMP debuff after 5 trial, assume solved') return True - def action_point_limit_override(self): + def get_action_point_limit(self): """ Override user config at the end of every month. To consume all action points without manual configuration. Returns: - bool: If overrode + int: ActionPointPreserve """ remain = get_os_reset_remain() if remain <= 0: if self.config.cross_get('OpsiCrossMonth.Scheduler.Enable', default=False): logger.info('Just less than 1 day to OpSi reset, OpsiCrossMonth is enabled' 'set OpsiMeowfficerFarming.ActionPointPreserve to 300 temporarily') - self.config.override(OpsiMeowfficerFarming_ActionPointPreserve=300) + return 300 else: logger.info('Just less than 1 day to OpSi reset, ' - 'set OpsiMeowfficerFarming.ActionPointPreserve to 0 temporarily') - self.config.override(OpsiMeowfficerFarming_ActionPointPreserve=0) - return True + 'set ActionPointPreserve to 0 temporarily') + return 0 + elif self.config.cross_get( + keys='OpsiHazard1Leveling.Scheduler.Enable', + default=False + ) and remain <= 2: + logger.info('Just less than 3 days to OpSi reset, ' + 'set ActionPointPreserve to 500 temporarily for hazard 1 leveling') + return 500 elif remain <= 2: logger.info('Just less than 3 days to OpSi reset, ' - 'set OpsiMeowfficerFarming.ActionPointPreserve < 300 temporarily') - self.config.override( - OpsiMeowfficerFarming_ActionPointPreserve=min( - self.config.OpsiMeowfficerFarming_ActionPointPreserve, - 300) - ) - return True + 'set ActionPointPreserve to 300 temporarily') + return 300 else: logger.info('Not close to OpSi reset') - return False + return maxsize def handle_after_auto_search(self): logger.hr('After auto search', level=2) @@ -394,8 +404,23 @@ class OSMap(OSFleet, Map, GlobeCamera): return self.config.task.command == 'OpsiExplore' _auto_search_battle_count = 0 + _auto_search_round_timer = 0 - def os_auto_search_daemon(self, drop=None, skip_first_screenshot=True): + def on_auto_search_battle_count_reset(self): + self._auto_search_battle_count = 0 + self._auto_search_round_timer = 0 + + def on_auto_search_battle_count_add(self): + self._auto_search_battle_count += 1 + logger.attr('battle_count', self._auto_search_battle_count) + if self.config.task.command == 'OpsiHazard1Leveling': + if self._auto_search_battle_count % 2 == 1: + if self._auto_search_round_timer: + cost = round(time.time() - self._auto_search_round_timer, 2) + logger.attr('CL1 time cost', f'{cost}s/round') + self._auto_search_round_timer = time.time() + + def os_auto_search_daemon(self, drop=None, strategic=False, skip_first_screenshot=True): """ Raises: CampaignEnd: If auto search ended @@ -407,7 +432,7 @@ class OSMap(OSFleet, Map, GlobeCamera): AUTO_SEARCH_REWARD if get auto search reward. """ logger.hr('OS auto search', level=2) - self._auto_search_battle_count = 0 + self.on_auto_search_battle_count_reset() unlock_checked = False unlock_check_timer = Timer(5, count=10).start() self.ash_popup_canceled = False @@ -442,7 +467,11 @@ class OSMap(OSFleet, Map, GlobeCamera): unlock_checked = True elif self.appear(AUTO_SEARCH_OS_MAP_OPTION_ON, offset=(5, 120)): unlock_checked = True - if self.handle_os_auto_search_map_option(drop=drop, enable=success): + + if self.handle_os_auto_search_map_option( + drop=drop, + enable=success + ): unlock_checked = True continue if self.handle_retirement(): @@ -450,8 +479,9 @@ class OSMap(OSFleet, Map, GlobeCamera): self.ash_popup_canceled = True continue if self.combat_appear(): - self._auto_search_battle_count += 1 - logger.attr('battle_count', self._auto_search_battle_count) + self.on_auto_search_battle_count_add() + if strategic and self.config.task_switched(): + self.interrupt_auto_search() result = self.auto_search_combat(drop=drop) if not result: success = False @@ -461,11 +491,38 @@ class OSMap(OSFleet, Map, GlobeCamera): # Auto search can not handle siren searching device. continue - def os_auto_search_run(self, drop=None): + def interrupt_auto_search(self): + logger.info('Interrupting auto search') + while 1: + self.device.screenshot() + + if self.appear_then_click(AUTO_SEARCH_REWARD, offset=(50, 50), interval=3): + continue + if self.appear_then_click(PAUSE, interval=0.5): + continue + if self.appear_then_click(QUIT_CONFIRM, offset=(20, 20), interval=5): + continue + if self.appear_then_click(QUIT_RECONFIRM, offset=True, interval=5): + continue + + if self.appear(AUTO_SEARCH_OS_MAP_OPTION_OFF, offset=(5, 120), interval=3) \ + and AUTO_SEARCH_OS_MAP_OPTION_OFF.match_appear_on(self.device.image): + self.device.click(GOTO_MAIN) + continue + if self.ui_additional(): + continue + # End + if self.ui_page_appear(page_main): + logger.info('Auto search interrupted') + self.config.task_stop() + + def os_auto_search_run(self, drop=None, strategic=False): for _ in range(5): backup = self.config.temporary(Campaign_UseAutoSearch=True) try: - self.os_auto_search_daemon(drop=drop) + if strategic: + self.strategic_search_start(skip_first_screenshot=True) + self.os_auto_search_daemon(drop=drop, strategic=strategic) except CampaignEnd: logger.info('OS auto search finished') finally: @@ -475,6 +532,7 @@ class OSMap(OSFleet, Map, GlobeCamera): # Break if zone cleared if self.config.OpsiAshBeacon_AshAttack: if self.handle_ash_beacon_attack() or self.ash_popup_canceled: + strategic = False continue else: break @@ -486,7 +544,7 @@ class OSMap(OSFleet, Map, GlobeCamera): else: break - def clear_question(self, drop): + def clear_question(self, drop=None): """ Clear nearly (and 3 grids from above) question marks on radar. Try 3 times at max to avoid loop tries on 2 adjacent fleet mechanism. @@ -587,6 +645,19 @@ class OSMap(OSFleet, Map, GlobeCamera): _solved_map_event = set() _solved_fleet_mechanism = 0 + def run_strategic_search(self): + self.handle_ash_beacon_attack() + + logger.hr('Run strategy search', level=2) + self.os_auto_search_run(strategic=True) + + self.hp_reset() + self.hp_get() + self._solved_map_event = set() + self._solved_fleet_mechanism = False + self.clear_question() + self.map_rescan() + def map_rescan_current(self, drop=None): """ @@ -631,7 +702,8 @@ class OSMap(OSFleet, Map, GlobeCamera): grid = grids[0] logger.info(f'Found scanning device on {grid}') self.device.click(grid) - result = self.wait_until_walk_stable(drop=drop, walk_out_of_step=False, confirm_timer=Timer(1.5, count=4)) + result = self.wait_until_walk_stable(drop=drop, walk_out_of_step=False, + confirm_timer=Timer(1.5, count=4)) self.os_auto_search_run(drop=drop) if 'event' in result: self._solved_map_event.add('is_scanning_device') diff --git a/module/os/operation_siren.py b/module/os/operation_siren.py index c919eb7e2..7bb85bdf4 100644 --- a/module/os/operation_siren.py +++ b/module/os/operation_siren.py @@ -2,15 +2,19 @@ from datetime import datetime, timedelta import numpy as np +from module.base.timer import Timer from module.config.utils import (get_os_next_reset, get_os_reset_remain, DEFAULT_TIME) from module.exception import RequestHumanTakeover, GameStuckError, ScriptError from module.logger import logger from module.map.map_grids import SelectedGrids +from module.shop.shop_voucher import VoucherShop from module.os.fleet import BossFleet from module.os.globe_operation import OSExploreError +from module.os_handler.assets import EXCHANGE_CHECK, EXCHANGE_ENTER from module.os.map import OSMap +from module.os_handler.shop import OCR_SHOP_YELLOW_COINS class OperationSiren(OSMap): @@ -67,11 +71,15 @@ class OperationSiren(OSMap): self.run_auto_search() self.handle_after_auto_search() - def os_finish_daily_mission(self): + def os_finish_daily_mission(self, question=True, rescan=None): """ Finish all daily mission in Operation Siren. Suggest to run os_port_daily to accept missions first. + Args: + question (bool): refer to run_auto_search + rescan (None, bool): refer to run_auto_search + Returns: bool: True if all finished. """ @@ -91,7 +99,7 @@ class OperationSiren(OSMap): self.os_order_execute( recon_scan=False, submarine_call=self.config.OpsiFleet_Submarine and result != 'pinned_at_archive_zone') - self.run_auto_search() + self.run_auto_search(question, rescan) self.handle_after_auto_search() self.config.check_task_switch() @@ -245,14 +253,38 @@ class OperationSiren(OSMap): self.os_port_daily(mission=False, supply=self.config.OpsiShop_BuySupply) self.config.task_delay(server_update=True) + def _os_voucher_enter(self): + self.os_map_goto_globe(unpin=False) + self.ui_click(click_button=EXCHANGE_ENTER, check_button=EXCHANGE_CHECK, + offset=(200, 20), retry_wait=3, skip_first_screenshot=True) + + def _os_voucher_exit(self): + self.ui_back(check_button=EXCHANGE_ENTER, appear_button=EXCHANGE_CHECK, + offset=(200, 20), retry_wait=3, skip_first_screenshot=True) + self.os_globe_goto_map() + + def os_voucher(self): + logger.hr('OS voucher', level=1) + self._os_voucher_enter() + VoucherShop(self.config, self.device).run() + self._os_voucher_exit() + + next_reset = get_os_next_reset() + logger.info('OS voucher finished, delay to next reset') + logger.attr('OpsiNextReset', next_reset) + self.config.task_delay(target=next_reset) + def os_meowfficer_farming(self): """ Recommend 3 or 5 for higher meowfficer searching point per action points ratio. """ logger.hr(f'OS meowfficer farming, hazard_level={self.config.OpsiMeowfficerFarming_HazardLevel}', level=1) - self.action_point_limit_override() + preserve = min(self.get_action_point_limit(), self.config.OpsiMeowfficerFarming_ActionPointPreserve) + if preserve == 0: + self.config.override(OpsiFleet_Submarine=False) + while 1: - self.config.OS_ACTION_POINT_PRESERVE = self.config.OpsiMeowfficerFarming_ActionPointPreserve + self.config.OS_ACTION_POINT_PRESERVE = preserve if self.config.OpsiAshBeacon_AshAttack \ and not self._ash_fully_collected \ and self.config.OpsiAshBeacon_EnsureFullyCollected: @@ -294,6 +326,55 @@ class OperationSiren(OSMap): self.handle_after_auto_search() self.config.check_task_switch() + def os_hazard1_leveling(self): + logger.hr('OS hazard 1 leveling', level=1) + while 1: + # Limited action point preserve of hazard 1 to 200 + self.config.OS_ACTION_POINT_PRESERVE = 200 + if self.config.OpsiAshBeacon_AshAttack \ + and not self._ash_fully_collected \ + and self.config.OpsiAshBeacon_EnsureFullyCollected: + logger.info('Ash beacon not fully collected, ignore action point limit temporarily') + self.config.OS_ACTION_POINT_PRESERVE = 0 + logger.attr('OS_ACTION_POINT_PRESERVE', self.config.OS_ACTION_POINT_PRESERVE) + + timeout = Timer(2).start() + skip_first_screenshot = True + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + yellow_coins = OCR_SHOP_YELLOW_COINS.ocr(self.device.image) + if yellow_coins < 100 and not timeout.reached(): + logger.info('Yellow coins less than 100, assuming it is an ocr error') + continue + elif yellow_coins < 100000: + logger.info('Reach the limit of yellow coins, preserve=100000') + self.config.task_delay(server_update=True) + self.config.task_stop() + else: + break + + self.get_current_zone() + + # Preset action point to 100 + self.set_action_point(cost=100) + if self.config.OpsiHazard1Leveling_TargetZone != 0: + zone = self.config.OpsiHazard1Leveling_TargetZone + else: + zone = 44 + + logger.hr(f'OS hazard 1 leveling, zone_id={zone}', level=1) + if self.zone.zone_id != zone or not self.is_zone_name_hidden: + self.globe_goto(self.name_to_zone(zone), types='SAFE', refresh=True) + self.fleet_set(self.config.OpsiFleet_Fleet) + self.run_strategic_search() + + self.handle_after_auto_search() + self.config.check_task_switch() + def _os_explore_task_delay(self): """ Delay other OpSi tasks during os_explore @@ -431,6 +512,19 @@ class OperationSiren(OSMap): else: break + def delay_abyssal(self, result=True): + """ + Args: + result(bool): If still have obscure coordinates. + """ + if get_os_reset_remain() == 0: + logger.info('Just less than 1 day to OpSi reset, delay 2.5 hours') + self.config.task_delay(minute=150, server_update=True) + self.config.task_stop() + elif self.config.cross_get('OpsiHazard1Leveling.Scheduler.Enable', default=False) or not result: + self.config.task_delay(server_update=True) + self.config.task_stop() + def clear_abyssal(self): """ Get one abyssal logger in storage, @@ -445,13 +539,7 @@ class OperationSiren(OSMap): logger.hr('OS clear abyssal', level=1) result = self.storage_get_next_item('ABYSSAL', use_logger=self.config.OpsiGeneral_UseLogger) if not result: - # No obscure coordinates, delay next run to tomorrow. - if get_os_reset_remain() > 0: - self.config.task_delay(server_update=True) - else: - logger.info('Just less than 1 day to OpSi reset, delay 2.5 hours') - self.config.task_delay(minute=150, server_update=True) - self.config.task_stop() + self.delay_abyssal(result=False) self.config.override( OpsiGeneral_DoRandomMapEvent=False, @@ -464,12 +552,40 @@ class OperationSiren(OSMap): raise RequestHumanTakeover self.fleet_repair(revert=False) + self.delay_abyssal() def os_abyssal(self): while 1: self.clear_abyssal() self.config.check_task_switch() + def os_archive(self): + """ + Unused func, not currently a monthly trend + Retain in case AL devs add as official feature + + Complete active archive zone in daily mission + Purchase next available logger archive then repeat + until exhausted + """ + shop = VoucherShop(self.config, self.device) + while 1: + # In case logger bought manually, + # finish pre-existing archive zone + self.os_finish_daily_mission(question=False, rescan=False) + + logger.hr('OS voucher', level=1) + self._os_voucher_enter() + bought = shop.run_once() + self._os_voucher_exit() + if not bought: + break + + next_reset = get_os_next_reset() + logger.info('All archive zones finished, delay to next reset') + logger.attr('OpsiNextReset', next_reset) + self.config.task_delay(target=next_reset) + def clear_stronghold(self): """ Find a siren stronghold on globe map, diff --git a/module/os_ash/meta.py b/module/os_ash/meta.py index 2f86bf489..4bac53a61 100644 --- a/module/os_ash/meta.py +++ b/module/os_ash/meta.py @@ -58,7 +58,7 @@ class Meta(UI, MapEventHandler): logger.info('Wrong click into battle preparation page') self.device.click(BACK_ARROW) return True - if self.handle_popup_cancel(): + if self.handle_popup_cancel('META'): return True if self.appear_then_click(META_ENTRANCE, offset=(20, 300), interval=2): return True diff --git a/module/os_handler/action_point.py b/module/os_handler/action_point.py index 13c5533f9..9d0cc7de4 100644 --- a/module/os_handler/action_point.py +++ b/module/os_handler/action_point.py @@ -1,5 +1,6 @@ import module.config.server as server from module.base.button import ButtonGrid +from module.base.timer import Timer from module.base.utils import * from module.logger import logger from module.ocr.ocr import Digit, DigitCounter @@ -9,6 +10,8 @@ from module.ui.assets import OS_CHECK from module.ui.ui import UI OCR_ACTION_POINT_REMAIN = Digit(ACTION_POINT_REMAIN, letter=(255, 219, 66), name='OCR_ACTION_POINT_REMAIN') +OCR_ACTION_POINT_REMAIN_OS = Digit(ACTION_POINT_REMAIN_OS, letter=(239, 239, 239), + threshold=160, name='OCR_SHOP_YELLOW_COINS_OS') if server.server != 'jp': # Letters in ACTION_POINT_BUY_REMAIN are not the numeric fonts usually used in azur lane. OCR_ACTION_POINT_BUY_REMAIN = DigitCounter( @@ -51,6 +54,12 @@ ACTION_POINTS_BUY = { 4: 1000, 5: 1000, } +ACTION_POINT_BOX = { + 0: 0, + 1: 20, + 2: 50, + 3: 100, +} class ActionPointLimit(Exception): @@ -78,7 +87,7 @@ class ActionPointHandler(UI): self.device.sleep(0.3) continue - if self.handle_popup_confirm(): + if self.handle_popup_confirm('ACTION_POINT_USE'): continue self.action_point_update() @@ -95,7 +104,7 @@ class ActionPointHandler(UI): current = OCR_ACTION_POINT_REMAIN.ocr(self.device.image) total = current if self.config.OS_ACTION_POINT_BOX_USE: - total += np.sum(np.array(box) * (0, 20, 50, 100)) + total += np.sum(np.array(box) * tuple(ACTION_POINT_BOX.values())) oil = box[0] logger.info(f'Action points: {current}({total}), oil: {oil}') @@ -103,6 +112,23 @@ class ActionPointHandler(UI): self._action_point_box = box self._action_point_total = total + def action_point_safe_get(self, skip_first_screenshot=True): + timeout = Timer(1, count=2).start() + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if timeout.reached(): + logger.warning('Get action points timeout') + break + + self.action_point_update() + + if sum(self._action_point_box[1:]) > 0 and self._action_point_box[0] > 0: + break + @staticmethod def action_point_get_cost(zone, pinned): """ @@ -208,11 +234,12 @@ class ActionPointHandler(UI): """ self.ui_click(ACTION_POINT_CANCEL, check_button=OS_CHECK, skip_first_screenshot=skip_first_screenshot) - def handle_action_point(self, zone, pinned): + def handle_action_point(self, zone, pinned, cost=None): """ Args: zone (Zone): Zone to enter. pinned (str): Zone type. Available types: DANGEROUS, SAFE, OBSCURE, ABYSSAL, STRONGHOLD. + cost (int): Custom action point cost value. Returns: bool: If handled. @@ -227,11 +254,15 @@ class ActionPointHandler(UI): return False # AP boxes have an animation to show - self.device.sleep(0.3) - self.device.screenshot() - self.action_point_update() - cost = self.action_point_get_cost(zone, pinned) + self.action_point_safe_get() + if cost is None: + cost = self.action_point_get_cost(zone, pinned) buy_checked = False + if self._action_point_total <= self.config.OS_ACTION_POINT_PRESERVE: + logger.info(f'Reach the limit of action points, preserve={self.config.OS_ACTION_POINT_PRESERVE}') + self.action_point_quit() + raise ActionPointLimit + for _ in range(12): # Having enough action points if self._action_point_current >= cost: @@ -246,8 +277,16 @@ class ActionPointHandler(UI): else: buy_checked = True + # Sort action point boxes + box = [] + for index in [1, 2, 3]: + if self._action_point_box[index] > 0: + if self._action_point_current + ACTION_POINT_BOX[index] >= 200: + box.append(index) + else: + box.insert(0, index) + # Use action point boxes - box = [index for index in [3, 2, 1] if self._action_point_box[index] > 0] if len(box): if self._action_point_total > self.config.OS_ACTION_POINT_PRESERVE: self.action_point_set_button(box[0]) @@ -264,3 +303,22 @@ class ActionPointHandler(UI): logger.warning('Failed to get action points after 12 trial') return False + + def set_action_point(self, zone=None, pinned=None, cost=None): + """ + Args: + zone (Zone): Zone to enter. + pinned (str): Zone type. Available types: DANGEROUS, SAFE, OBSCURE, ABYSSAL, STRONGHOLD. + cost (int): Custom action point cost value. + + Returns: + bool: If handled. + """ + self.ui_click(ACTION_POINT_REMAIN_OS, ACTION_POINT_USE, OS_CHECK) + if not self.handle_action_point(zone, pinned, cost): + return False + + while 1: + if self.appear(IN_MAP, offset=(200, 5)): + return True + self.device.screenshot() diff --git a/module/os_handler/assets.py b/module/os_handler/assets.py index 9aa8691a2..d3550babc 100644 --- a/module/os_handler/assets.py +++ b/module/os_handler/assets.py @@ -7,6 +7,7 @@ from module.base.template import Template ACTION_POINT_BUY_REMAIN = Button(area={'cn': (803, 458, 833, 482), 'en': (907, 458, 941, 483), 'jp': (891, 464, 927, 484), 'tw': (803, 458, 833, 482)}, color={'cn': (108, 138, 125), 'en': (106, 145, 116), 'jp': (135, 142, 153), 'tw': (108, 138, 125)}, button={'cn': (803, 458, 833, 482), 'en': (907, 458, 941, 483), 'jp': (891, 464, 927, 484), 'tw': (803, 458, 833, 482)}, file={'cn': './assets/cn/os_handler/ACTION_POINT_BUY_REMAIN.png', 'en': './assets/en/os_handler/ACTION_POINT_BUY_REMAIN.png', 'jp': './assets/jp/os_handler/ACTION_POINT_BUY_REMAIN.png', 'tw': './assets/tw/os_handler/ACTION_POINT_BUY_REMAIN.png'}) ACTION_POINT_CANCEL = Button(area={'cn': (370, 528, 542, 585), 'en': (375, 531, 537, 584), 'jp': (369, 527, 544, 587), 'tw': (371, 528, 541, 586)}, color={'cn': (167, 168, 171), 'en': (170, 172, 175), 'jp': (161, 163, 165), 'tw': (168, 169, 172)}, button={'cn': (370, 528, 542, 585), 'en': (375, 531, 537, 584), 'jp': (369, 527, 544, 587), 'tw': (371, 528, 541, 586)}, file={'cn': './assets/cn/os_handler/ACTION_POINT_CANCEL.png', 'en': './assets/en/os_handler/ACTION_POINT_CANCEL.png', 'jp': './assets/jp/os_handler/ACTION_POINT_CANCEL.png', 'tw': './assets/tw/os_handler/ACTION_POINT_CANCEL.png'}) ACTION_POINT_REMAIN = Button(area={'cn': (892, 220, 952, 241), 'en': (892, 220, 952, 241), 'jp': (908, 220, 979, 240), 'tw': (892, 220, 952, 241)}, color={'cn': (124, 127, 113), 'en': (124, 127, 113), 'jp': (119, 121, 113), 'tw': (124, 127, 113)}, button={'cn': (892, 220, 952, 241), 'en': (892, 220, 952, 241), 'jp': (908, 220, 979, 240), 'tw': (892, 220, 952, 241)}, file={'cn': './assets/cn/os_handler/ACTION_POINT_REMAIN.png', 'en': './assets/en/os_handler/ACTION_POINT_REMAIN.png', 'jp': './assets/jp/os_handler/ACTION_POINT_REMAIN.png', 'tw': './assets/tw/os_handler/ACTION_POINT_REMAIN.png'}) +ACTION_POINT_REMAIN_OS = Button(area={'cn': (878, 28, 928, 46), 'en': (878, 28, 928, 46), 'jp': (878, 28, 928, 46), 'tw': (878, 28, 928, 46)}, color={'cn': (78, 89, 100), 'en': (78, 89, 100), 'jp': (78, 89, 100), 'tw': (78, 89, 100)}, button={'cn': (878, 28, 928, 46), 'en': (878, 28, 928, 46), 'jp': (878, 28, 928, 46), 'tw': (878, 28, 928, 46)}, file={'cn': './assets/cn/os_handler/ACTION_POINT_REMAIN_OS.png', 'en': './assets/cn/os_handler/ACTION_POINT_REMAIN_OS.png', 'jp': './assets/cn/os_handler/ACTION_POINT_REMAIN_OS.png', 'tw': './assets/cn/os_handler/ACTION_POINT_REMAIN_OS.png'}) ACTION_POINT_USE = Button(area={'cn': (738, 528, 910, 585), 'en': (742, 531, 909, 584), 'jp': (737, 528, 911, 586), 'tw': (739, 528, 909, 585)}, color={'cn': (93, 142, 203), 'en': (107, 152, 208), 'jp': (92, 141, 203), 'tw': (95, 144, 205)}, button={'cn': (738, 528, 910, 585), 'en': (742, 531, 909, 584), 'jp': (737, 528, 911, 586), 'tw': (739, 528, 909, 585)}, file={'cn': './assets/cn/os_handler/ACTION_POINT_USE.png', 'en': './assets/en/os_handler/ACTION_POINT_USE.png', 'jp': './assets/jp/os_handler/ACTION_POINT_USE.png', 'tw': './assets/tw/os_handler/ACTION_POINT_USE.png'}) ASH_POPUP_CHECK = Button(area={'cn': (665, 318, 759, 340), 'en': (372, 324, 601, 342), 'jp': (438, 311, 534, 346), 'tw': (665, 298, 759, 320)}, color={'cn': (154, 163, 172), 'en': (174, 179, 184), 'jp': (146, 154, 161), 'tw': (157, 166, 176)}, button={'cn': (665, 318, 759, 340), 'en': (372, 324, 601, 342), 'jp': (438, 311, 534, 346), 'tw': (665, 298, 759, 320)}, file={'cn': './assets/cn/os_handler/ASH_POPUP_CHECK.png', 'en': './assets/en/os_handler/ASH_POPUP_CHECK.png', 'jp': './assets/jp/os_handler/ASH_POPUP_CHECK.png', 'tw': './assets/tw/os_handler/ASH_POPUP_CHECK.png'}) AUTO_SEARCH_OS_MAP_OPTION_OFF = Button(area={'cn': (1205, 549, 1275, 566), 'en': (1201, 524, 1274, 534), 'jp': (1204, 572, 1276, 593), 'tw': (1206, 573, 1275, 591)}, color={'cn': (196, 169, 169), 'en': (167, 140, 142), 'jp': (180, 154, 157), 'tw': (167, 143, 147)}, button={'cn': (1205, 549, 1275, 566), 'en': (1201, 524, 1274, 534), 'jp': (1204, 572, 1276, 593), 'tw': (1206, 573, 1275, 591)}, file={'cn': './assets/cn/os_handler/AUTO_SEARCH_OS_MAP_OPTION_OFF.png', 'en': './assets/en/os_handler/AUTO_SEARCH_OS_MAP_OPTION_OFF.png', 'jp': './assets/jp/os_handler/AUTO_SEARCH_OS_MAP_OPTION_OFF.png', 'tw': './assets/tw/os_handler/AUTO_SEARCH_OS_MAP_OPTION_OFF.png'}) @@ -14,6 +15,7 @@ AUTO_SEARCH_OS_MAP_OPTION_ON = Button(area={'cn': (1205, 549, 1275, 566), 'en': AUTO_SEARCH_REWARD = Button(area={'cn': (575, 598, 721, 646), 'en': (574, 597, 722, 648), 'jp': (577, 597, 722, 645), 'tw': (576, 598, 720, 647)}, color={'cn': (169, 168, 170), 'en': (168, 171, 174), 'jp': (165, 170, 175), 'tw': (171, 174, 179)}, button={'cn': (575, 598, 721, 646), 'en': (574, 597, 722, 648), 'jp': (577, 597, 722, 645), 'tw': (576, 598, 720, 647)}, file={'cn': './assets/cn/os_handler/AUTO_SEARCH_REWARD.png', 'en': './assets/en/os_handler/AUTO_SEARCH_REWARD.png', 'jp': './assets/jp/os_handler/AUTO_SEARCH_REWARD.png', 'tw': './assets/tw/os_handler/AUTO_SEARCH_REWARD.png'}) CLICK_SAFE_AREA = Button(area={'cn': (1104, 169, 1214, 284), 'en': (1104, 169, 1214, 284), 'jp': (1104, 169, 1214, 284), 'tw': (1104, 169, 1214, 284)}, color={'cn': (96, 114, 142), 'en': (96, 114, 142), 'jp': (96, 114, 142), 'tw': (96, 114, 142)}, button={'cn': (1104, 169, 1214, 284), 'en': (1104, 169, 1214, 284), 'jp': (1104, 169, 1214, 284), 'tw': (1104, 169, 1214, 284)}, file={'cn': './assets/cn/os_handler/CLICK_SAFE_AREA.png', 'en': './assets/en/os_handler/CLICK_SAFE_AREA.png', 'jp': './assets/jp/os_handler/CLICK_SAFE_AREA.png', 'tw': './assets/tw/os_handler/CLICK_SAFE_AREA.png'}) EXCHANGE_CHECK = Button(area={'cn': (144, 17, 249, 41), 'en': (143, 16, 240, 37), 'jp': (141, 16, 250, 42), 'tw': (144, 17, 249, 41)}, color={'cn': (153, 170, 209), 'en': (117, 133, 171), 'jp': (86, 98, 127), 'tw': (153, 170, 209)}, button={'cn': (144, 17, 249, 41), 'en': (143, 16, 240, 37), 'jp': (141, 16, 250, 42), 'tw': (144, 17, 249, 41)}, file={'cn': './assets/cn/os_handler/EXCHANGE_CHECK.png', 'en': './assets/en/os_handler/EXCHANGE_CHECK.png', 'jp': './assets/jp/os_handler/EXCHANGE_CHECK.png', 'tw': './assets/cn/os_handler/EXCHANGE_CHECK.png'}) +EXCHANGE_ENTER = Button(area={'cn': (195, 629, 344, 693), 'en': (202, 640, 332, 690), 'jp': (195, 629, 345, 693), 'tw': (195, 629, 344, 693)}, color={'cn': (58, 54, 52), 'en': (64, 61, 59), 'jp': (53, 50, 48), 'tw': (58, 54, 52)}, button={'cn': (195, 629, 344, 693), 'en': (202, 640, 332, 690), 'jp': (195, 629, 345, 693), 'tw': (195, 629, 344, 693)}, file={'cn': './assets/cn/os_handler/EXCHANGE_ENTER.png', 'en': './assets/en/os_handler/EXCHANGE_ENTER.png', 'jp': './assets/jp/os_handler/EXCHANGE_ENTER.png', 'tw': './assets/cn/os_handler/EXCHANGE_ENTER.png'}) GET_ADAPTABILITY = Button(area={'cn': (538, 191, 742, 227), 'en': (527, 194, 659, 215), 'jp': (538, 191, 743, 227), 'tw': (539, 191, 741, 227)}, color={'cn': (166, 195, 246), 'en': (198, 207, 230), 'jp': (158, 190, 245), 'tw': (164, 195, 246)}, button={'cn': (538, 191, 742, 227), 'en': (527, 194, 659, 215), 'jp': (538, 191, 743, 227), 'tw': (539, 191, 741, 227)}, file={'cn': './assets/cn/os_handler/GET_ADAPTABILITY.png', 'en': './assets/en/os_handler/GET_ADAPTABILITY.png', 'jp': './assets/jp/os_handler/GET_ADAPTABILITY.png', 'tw': './assets/tw/os_handler/GET_ADAPTABILITY.png'}) GET_MEOWFFICER_ITEMS_1 = Button(area={'cn': (558, 211, 730, 242), 'en': (550, 211, 723, 247), 'jp': (538, 217, 732, 253), 'tw': (558, 211, 730, 242)}, color={'cn': (186, 200, 231), 'en': (165, 191, 241), 'jp': (143, 180, 249), 'tw': (186, 200, 231)}, button={'cn': (558, 211, 730, 242), 'en': (550, 211, 723, 247), 'jp': (538, 217, 732, 253), 'tw': (558, 211, 730, 242)}, file={'cn': './assets/cn/os_handler/GET_MEOWFFICER_ITEMS_1.png', 'en': './assets/en/os_handler/GET_MEOWFFICER_ITEMS_1.png', 'jp': './assets/jp/os_handler/GET_MEOWFFICER_ITEMS_1.png', 'tw': './assets/tw/os_handler/GET_MEOWFFICER_ITEMS_1.png'}) GET_MEOWFFICER_ITEMS_2 = Button(area={'cn': (558, 140, 730, 171), 'en': (550, 140, 723, 176), 'jp': (538, 146, 731, 182), 'tw': (558, 140, 730, 171)}, color={'cn': (186, 200, 231), 'en': (165, 191, 242), 'jp': (143, 179, 249), 'tw': (186, 200, 231)}, button={'cn': (558, 140, 730, 171), 'en': (550, 140, 723, 176), 'jp': (538, 146, 731, 182), 'tw': (558, 140, 730, 171)}, file={'cn': './assets/cn/os_handler/GET_MEOWFFICER_ITEMS_2.png', 'en': './assets/en/os_handler/GET_MEOWFFICER_ITEMS_2.png', 'jp': './assets/jp/os_handler/GET_MEOWFFICER_ITEMS_2.png', 'tw': './assets/tw/os_handler/GET_MEOWFFICER_ITEMS_2.png'}) @@ -57,6 +59,20 @@ STORAGE_COORDINATE_CHECKOUT = Button(area={'cn': (554, 493, 726, 550), 'en': (56 STORAGE_ENTER = Button(area={'cn': (770, 636, 880, 699), 'en': (761, 640, 876, 695), 'jp': (757, 636, 880, 699), 'tw': (618, 638, 725, 698)}, color={'cn': (240, 199, 121), 'en': (238, 191, 115), 'jp': (237, 187, 100), 'tw': (240, 197, 125)}, button={'cn': (770, 636, 880, 699), 'en': (761, 640, 876, 695), 'jp': (757, 636, 880, 699), 'tw': (618, 638, 725, 698)}, file={'cn': './assets/cn/os_handler/STORAGE_ENTER.png', 'en': './assets/en/os_handler/STORAGE_ENTER.png', 'jp': './assets/jp/os_handler/STORAGE_ENTER.png', 'tw': './assets/tw/os_handler/STORAGE_ENTER.png'}) STORAGE_USE = Button(area={'cn': (709, 506, 769, 536), 'en': (729, 509, 786, 533), 'jp': (764, 506, 856, 536), 'tw': (709, 506, 769, 536)}, color={'cn': (161, 186, 220), 'en': (164, 189, 222), 'jp': (142, 178, 224), 'tw': (161, 186, 220)}, button={'cn': (687, 493, 860, 550), 'en': (693, 495, 858, 549), 'jp': (686, 492, 861, 552), 'tw': (687, 493, 860, 550)}, file={'cn': './assets/cn/os_handler/STORAGE_USE.png', 'en': './assets/en/os_handler/STORAGE_USE.png', 'jp': './assets/jp/os_handler/STORAGE_USE.png', 'tw': './assets/tw/os_handler/STORAGE_USE.png'}) STORATE_SCROLL = Button(area={'cn': (1256, 102, 1264, 589), 'en': (1256, 102, 1264, 589), 'jp': (1256, 102, 1264, 589), 'tw': (1256, 102, 1264, 589)}, color={'cn': (119, 109, 60), 'en': (119, 109, 60), 'jp': (119, 109, 60), 'tw': (119, 109, 60)}, button={'cn': (1256, 102, 1264, 589), 'en': (1256, 102, 1264, 589), 'jp': (1256, 102, 1264, 589), 'tw': (1256, 102, 1264, 589)}, file={'cn': './assets/cn/os_handler/STORATE_SCROLL.png', 'en': './assets/en/os_handler/STORATE_SCROLL.png', 'jp': './assets/jp/os_handler/STORATE_SCROLL.png', 'tw': './assets/tw/os_handler/STORATE_SCROLL.png'}) +STRATEGIC_SEARCH_DEVICE_CHECK = Button(area={'cn': (474, 404, 556, 422), 'en': (474, 404, 556, 422), 'jp': (474, 404, 556, 422), 'tw': (474, 404, 556, 422)}, color={'cn': (149, 156, 168), 'en': (149, 156, 168), 'jp': (149, 156, 168), 'tw': (149, 156, 168)}, button={'cn': (474, 404, 556, 422), 'en': (474, 404, 556, 422), 'jp': (474, 404, 556, 422), 'tw': (474, 404, 556, 422)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CHECK.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CHECK.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CHECK.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CHECK.png'}) +STRATEGIC_SEARCH_DEVICE_CONTINUE = Button(area={'cn': (519, 443, 535, 458), 'en': (519, 443, 535, 458), 'jp': (519, 443, 535, 458), 'tw': (519, 443, 535, 458)}, color={'cn': (89, 128, 66), 'en': (89, 128, 66), 'jp': (89, 128, 66), 'tw': (89, 128, 66)}, button={'cn': (519, 443, 535, 458), 'en': (519, 443, 535, 458), 'jp': (519, 443, 535, 458), 'tw': (519, 443, 535, 458)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CONTINUE.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CONTINUE.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CONTINUE.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_CONTINUE.png'}) +STRATEGIC_SEARCH_DEVICE_STOP = Button(area={'cn': (446, 443, 462, 458), 'en': (446, 443, 462, 458), 'jp': (446, 443, 462, 458), 'tw': (446, 443, 462, 458)}, color={'cn': (89, 128, 67), 'en': (89, 128, 67), 'jp': (89, 128, 67), 'tw': (89, 128, 67)}, button={'cn': (446, 443, 462, 458), 'en': (446, 443, 462, 458), 'jp': (446, 443, 462, 458), 'tw': (446, 443, 462, 458)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_STOP.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_STOP.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_STOP.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_DEVICE_STOP.png'}) +STRATEGIC_SEARCH_MAP_OPTION_OFF = Button(area={'cn': (1178, 576, 1278, 617), 'en': (1178, 576, 1278, 617), 'jp': (1178, 576, 1278, 617), 'tw': (1178, 576, 1278, 617)}, color={'cn': (96, 102, 122), 'en': (96, 102, 122), 'jp': (96, 102, 122), 'tw': (96, 102, 122)}, button={'cn': (1178, 576, 1278, 617), 'en': (1178, 576, 1278, 617), 'jp': (1178, 576, 1278, 617), 'tw': (1178, 576, 1278, 617)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_MAP_OPTION_OFF.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_MAP_OPTION_OFF.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_MAP_OPTION_OFF.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_MAP_OPTION_OFF.png'}) +STRATEGIC_SEARCH_MERCHANT_CONTINUE = Button(area={'cn': (519, 443, 535, 458), 'en': (519, 443, 535, 458), 'jp': (519, 443, 535, 458), 'tw': (519, 443, 535, 458)}, color={'cn': (89, 128, 66), 'en': (89, 128, 66), 'jp': (89, 128, 66), 'tw': (89, 128, 66)}, button={'cn': (519, 443, 535, 458), 'en': (519, 443, 535, 458), 'jp': (519, 443, 535, 458), 'tw': (519, 443, 535, 458)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_CONTINUE.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_CONTINUE.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_CONTINUE.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_CONTINUE.png'}) +STRATEGIC_SEARCH_MERCHANT_STOP = Button(area={'cn': (446, 443, 462, 458), 'en': (446, 443, 462, 458), 'jp': (446, 443, 462, 458), 'tw': (446, 443, 462, 458)}, color={'cn': (89, 128, 67), 'en': (89, 128, 67), 'jp': (89, 128, 67), 'tw': (89, 128, 67)}, button={'cn': (446, 443, 462, 458), 'en': (446, 443, 462, 458), 'jp': (446, 443, 462, 458), 'tw': (446, 443, 462, 458)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_STOP.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_STOP.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_STOP.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_MERCHANT_STOP.png'}) +STRATEGIC_SEARCH_POPUP_CHECK = Button(area={'cn': (370, 166, 566, 198), 'en': (370, 166, 566, 198), 'jp': (370, 166, 566, 198), 'tw': (370, 166, 566, 198)}, color={'cn': (150, 170, 209), 'en': (150, 170, 209), 'jp': (150, 170, 209), 'tw': (150, 170, 209)}, button={'cn': (370, 166, 566, 198), 'en': (370, 166, 566, 198), 'jp': (370, 166, 566, 198), 'tw': (370, 166, 566, 198)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_POPUP_CHECK.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_POPUP_CHECK.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_POPUP_CHECK.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_POPUP_CHECK.png'}) +STRATEGIC_SEARCH_SCROLL_AREA = Button(area={'cn': (902, 277, 910, 470), 'en': (902, 277, 910, 470), 'jp': (902, 277, 910, 470), 'tw': (902, 277, 910, 470)}, color={'cn': (151, 145, 102), 'en': (151, 145, 102), 'jp': (151, 145, 102), 'tw': (151, 145, 102)}, button={'cn': (902, 277, 910, 470), 'en': (902, 277, 910, 470), 'jp': (902, 277, 910, 470), 'tw': (902, 277, 910, 470)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_SCROLL_AREA.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_SCROLL_AREA.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_SCROLL_AREA.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_SCROLL_AREA.png'}) +STRATEGIC_SEARCH_SECURED_OPTION_OFF = Button(area={'cn': (457, 229, 550, 257), 'en': (457, 229, 550, 257), 'jp': (457, 229, 550, 257), 'tw': (457, 229, 550, 257)}, color={'cn': (111, 141, 195), 'en': (111, 141, 195), 'jp': (111, 141, 195), 'tw': (111, 141, 195)}, button={'cn': (640, 217, 913, 270), 'en': (640, 217, 913, 270), 'jp': (640, 217, 913, 270), 'tw': (640, 217, 913, 270)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_OFF.png'}) +STRATEGIC_SEARCH_SECURED_OPTION_ON = Button(area={'cn': (731, 229, 823, 258), 'en': (731, 229, 823, 258), 'jp': (731, 229, 823, 258), 'tw': (731, 229, 823, 258)}, color={'cn': (105, 136, 192), 'en': (105, 136, 192), 'jp': (105, 136, 192), 'tw': (105, 136, 192)}, button={'cn': (731, 229, 823, 258), 'en': (731, 229, 823, 258), 'jp': (731, 229, 823, 258), 'tw': (731, 229, 823, 258)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_ON.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_ON.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_ON.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_SECURED_OPTION_ON.png'}) +STRATEGIC_SEARCH_SUBMIT_OFF = Button(area={'cn': (446, 427, 462, 442), 'en': (446, 427, 462, 442), 'jp': (446, 427, 462, 442), 'tw': (446, 427, 462, 442)}, color={'cn': (89, 128, 67), 'en': (89, 128, 67), 'jp': (89, 128, 67), 'tw': (89, 128, 67)}, button={'cn': (446, 427, 462, 442), 'en': (446, 427, 462, 442), 'jp': (446, 427, 462, 442), 'tw': (446, 427, 462, 442)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_OFF.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_OFF.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_OFF.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_OFF.png'}) +STRATEGIC_SEARCH_SUBMIT_ON = Button(area={'cn': (519, 427, 535, 442), 'en': (519, 427, 535, 442), 'jp': (519, 427, 535, 442), 'tw': (519, 427, 535, 442)}, color={'cn': (89, 128, 67), 'en': (89, 128, 67), 'jp': (89, 128, 67), 'tw': (89, 128, 67)}, button={'cn': (519, 427, 535, 442), 'en': (519, 427, 535, 442), 'jp': (519, 427, 535, 442), 'tw': (519, 427, 535, 442)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_ON.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_ON.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_ON.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_SUBMIT_ON.png'}) +STRATEGIC_SEARCH_ZONE_MODE_RANDOM = Button(area={'cn': (750, 335, 766, 350), 'en': (750, 335, 766, 350), 'jp': (750, 335, 766, 350), 'tw': (750, 335, 766, 350)}, color={'cn': (89, 128, 67), 'en': (89, 128, 67), 'jp': (89, 128, 67), 'tw': (89, 128, 67)}, button={'cn': (750, 335, 766, 350), 'en': (750, 335, 766, 350), 'jp': (750, 335, 766, 350), 'tw': (750, 335, 766, 350)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_RANDOM.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_RANDOM.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_RANDOM.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_RANDOM.png'}) +STRATEGIC_SEARCH_ZONE_MODE_REPEAT = Button(area={'cn': (551, 335, 567, 350), 'en': (551, 335, 567, 350), 'jp': (551, 335, 567, 350), 'tw': (551, 335, 567, 350)}, color={'cn': (89, 128, 67), 'en': (89, 128, 67), 'jp': (89, 128, 67), 'tw': (89, 128, 67)}, button={'cn': (551, 335, 567, 350), 'en': (551, 335, 567, 350), 'jp': (551, 335, 567, 350), 'tw': (551, 335, 567, 350)}, file={'cn': './assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_REPEAT.png', 'en': './assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_REPEAT.png', 'jp': './assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_REPEAT.png', 'tw': './assets/cn/os_handler/STRATEGIC_SEARCH_ZONE_MODE_REPEAT.png'}) TEMPLATE_STORAGE_ABYSSAL = Template(file={'cn': './assets/cn/os_handler/TEMPLATE_STORAGE_ABYSSAL.png', 'en': './assets/en/os_handler/TEMPLATE_STORAGE_ABYSSAL.png', 'jp': './assets/jp/os_handler/TEMPLATE_STORAGE_ABYSSAL.png', 'tw': './assets/tw/os_handler/TEMPLATE_STORAGE_ABYSSAL.png'}) TEMPLATE_STORAGE_COMBAT = Template(file={'cn': './assets/cn/os_handler/TEMPLATE_STORAGE_COMBAT.png', 'en': './assets/en/os_handler/TEMPLATE_STORAGE_COMBAT.png', 'jp': './assets/jp/os_handler/TEMPLATE_STORAGE_COMBAT.png', 'tw': './assets/tw/os_handler/TEMPLATE_STORAGE_COMBAT.png'}) TEMPLATE_STORAGE_LOGGER = Template(file={'cn': './assets/cn/os_handler/TEMPLATE_STORAGE_LOGGER.gif', 'en': './assets/en/os_handler/TEMPLATE_STORAGE_LOGGER.gif', 'jp': './assets/jp/os_handler/TEMPLATE_STORAGE_LOGGER.gif', 'tw': './assets/tw/os_handler/TEMPLATE_STORAGE_LOGGER.gif'}) diff --git a/module/os_handler/map_event.py b/module/os_handler/map_event.py index f15b572d2..85d75e052 100644 --- a/module/os_handler/map_event.py +++ b/module/os_handler/map_event.py @@ -208,7 +208,8 @@ class MapEventHandler(EnemySearchingHandler): confirm_timer.reset() continue if self.appear_then_click(GLOBE_GOTO_MAP, offset=(20, 20), interval=2): - # Sometimes entered globe map after clicking AUTO_SEARCH_REWARD, but don't know why + # Sometimes entered globe map after clicking AUTO_SEARCH_REWARD + # because of duplicated clicks and clicks to places outside the map confirm_timer.reset() continue @@ -221,11 +222,12 @@ class MapEventHandler(EnemySearchingHandler): return cleared - def handle_os_auto_search_map_option(self, drop=None, enable=True): + def handle_os_auto_search_map_option(self, drop=None, enable=True, quit=True): """ Args: - drop (DropImage) + drop (DropImage): enable (bool): + quit (bool): Returns: bool: If clicked. @@ -238,13 +240,14 @@ class MapEventHandler(EnemySearchingHandler): raise CampaignEnd if self.appear(AUTO_SEARCH_REWARD, offset=(50, 50)): self.device.screenshot_interval_set() - if self.os_auto_search_quit(drop=drop): + if quit and self.os_auto_search_quit(drop=drop): # No more items on current map raise CampaignEnd else: # Auto search stopped but map hasn't been cleared return True - if enable: + + elif enable: if self.appear(AUTO_SEARCH_OS_MAP_OPTION_OFF, offset=(5, 120), interval=3) \ and AUTO_SEARCH_OS_MAP_OPTION_OFF.match_appear_on(self.device.image): self.device.click(AUTO_SEARCH_OS_MAP_OPTION_OFF) diff --git a/module/os_handler/strategic.py b/module/os_handler/strategic.py new file mode 100644 index 000000000..4b20a1e7f --- /dev/null +++ b/module/os_handler/strategic.py @@ -0,0 +1,139 @@ +from module.base.utils import get_color +from module.exception import GameStuckError +from module.logger import logger +from module.os_handler.assets import * +from module.os_handler.enemy_searching import EnemySearchingHandler +from module.ui.scroll import Scroll + +STRATEGIC_SEARCH_SCROLL = Scroll(STRATEGIC_SEARCH_SCROLL_AREA, color=(247, 211, 66), name='STRATEGIC_SEARCH_SCROLL') + + +class StrategicSearchHandler(EnemySearchingHandler): + def strategy_search_enter(self, skip_first_screenshot=False): + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if STRATEGIC_SEARCH_MAP_OPTION_OFF.match_appear_on(self.device.image) \ + and self.appear_then_click(STRATEGIC_SEARCH_MAP_OPTION_OFF, interval=2): + continue + if self.appear(STRATEGIC_SEARCH_POPUP_CHECK): + return True + + def strategic_search_set_option(self, skip_first_screenshot=False): + STRATEGIC_SEARCH_SCROLL.drag_threshold = 0.1 + + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + if not self.appear(STRATEGIC_SEARCH_POPUP_CHECK): + return False + + if self.appear(STRATEGIC_SEARCH_SECURED_OPTION_OFF) \ + and get_color(self.device.image, STRATEGIC_SEARCH_SECURED_OPTION_OFF.area)[2] > 185: + logger.attr('search_mode', 'explorable') + self.device.click(STRATEGIC_SEARCH_SECURED_OPTION_OFF) + continue + if self.appear(STRATEGIC_SEARCH_SECURED_OPTION_ON) \ + and get_color(self.device.image, STRATEGIC_SEARCH_SECURED_OPTION_ON.area)[2] > 185: + logger.attr('search_mode', 'secured') + skip_first_screenshot = True + break + + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + if not self.appear(STRATEGIC_SEARCH_POPUP_CHECK): + return False + + if self.appear(STRATEGIC_SEARCH_ZONE_MODE_RANDOM): + logger.attr('zone_mode', 'random') + self.device.click(STRATEGIC_SEARCH_ZONE_MODE_REPEAT) + if self.appear(STRATEGIC_SEARCH_MERCHANT_CONTINUE): + logger.attr('encounter_merchant', 'continue') + self.device.click(STRATEGIC_SEARCH_MERCHANT_STOP) + continue + if self.appear(STRATEGIC_SEARCH_ZONE_MODE_REPEAT) \ + and self.appear(STRATEGIC_SEARCH_MERCHANT_STOP): + logger.attr('zone_mode', 'repeat') + logger.attr('encounter_merchant', 'stop') + skip_first_screenshot = True + break + + STRATEGIC_SEARCH_SCROLL.set(0.4, main=self) + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + if not self.appear(STRATEGIC_SEARCH_POPUP_CHECK): + return False + + check = self.appear(STRATEGIC_SEARCH_DEVICE_CHECK, offset=(20, 200), threshold=0.7) + STRATEGIC_SEARCH_DEVICE_STOP.load_offset(STRATEGIC_SEARCH_DEVICE_CHECK) + STRATEGIC_SEARCH_DEVICE_CONTINUE.load_offset(STRATEGIC_SEARCH_DEVICE_CHECK) + if check and \ + self.image_color_count(STRATEGIC_SEARCH_DEVICE_STOP.button, color=(156, 255, 82), count=30): + logger.attr('encounter_device', 'stop') + skip_first_screenshot = True + break + if check and \ + self.image_color_count(STRATEGIC_SEARCH_DEVICE_CONTINUE.button, color=(156, 255, 82), count=30): + logger.attr('encounter_device', 'continue') + self.device.click(STRATEGIC_SEARCH_DEVICE_STOP) + + STRATEGIC_SEARCH_SCROLL.set_bottom(main=self) + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + if not self.appear(STRATEGIC_SEARCH_POPUP_CHECK): + return False + + if self.appear(STRATEGIC_SEARCH_SUBMIT_OFF): + logger.attr('auto_submit', 'off') + self.device.click(STRATEGIC_SEARCH_SUBMIT_ON) + continue + if self.appear(STRATEGIC_SEARCH_SUBMIT_ON): + logger.attr('auto_submit', 'on') + break + + return True + + def strategic_search_confirm(self, skip_first_screenshot=False): + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.handle_popup_confirm(offset=(30, 30), name='STRATEGIC_SEARCH'): + continue + + if self.is_in_map(): + return True + + def strategic_search_start(self, skip_first_screenshot=False): + """ + Pages: + in: IN_MAP + out: IN_MAP, with strategic search running + """ + logger.hr('Strategic search start') + while 1: + flag = True + flag &= self.strategy_search_enter(skip_first_screenshot=skip_first_screenshot) + skip_first_screenshot = False + flag &= self.strategic_search_set_option(skip_first_screenshot=True) + flag &= self.strategic_search_confirm(skip_first_screenshot=True) + if flag: + return True + if not self.is_in_map(): + raise GameStuckError diff --git a/module/research/selector.py b/module/research/selector.py index 6bc1d49e7..d1fab539f 100644 --- a/module/research/selector.py +++ b/module/research/selector.py @@ -121,18 +121,14 @@ class ResearchSelector(ResearchUI): if preset == 'custom': string = self.config.Research_CustomFilter if enforce: - value = GeneratedConfig.Research_PresetFilter - string = string.split() - string.append('>') - string.extend(DICT_FILTER_PRESET[value].split()) - string = " ".join(string) + string = string + ' > ' + DICT_FILTER_PRESET[GeneratedConfig.Research_PresetFilter] else: - if self.config.Research_UseCube == 'always_use' or enforce: - if f'{preset}_cube' in DICT_FILTER_PRESET: - preset = f'{preset}_cube' + if (self.config.Research_UseCube == 'always_use' or enforce) \ + and f'{preset}_cube' in DICT_FILTER_PRESET: + preset = f'{preset}_cube' if preset not in DICT_FILTER_PRESET: logger.warning(f'Preset not found: {preset}, use default preset') - preset = 'series_5_blueprint_152' + preset = GeneratedConfig.Research_PresetFilter string = DICT_FILTER_PRESET[preset] logger.attr('Research preset', preset) diff --git a/module/retire/assets.py b/module/retire/assets.py index 7973cbfaf..5c0de18c7 100644 --- a/module/retire/assets.py +++ b/module/retire/assets.py @@ -43,14 +43,14 @@ SORT_ASC = Button(area={'cn': (1014, 22, 1019, 26), 'en': (1014, 22, 1019, 26), SORT_DESC = Button(area={'cn': (1014, 29, 1019, 33), 'en': (1014, 29, 1019, 33), 'jp': (1013, 29, 1019, 32), 'tw': (1014, 29, 1019, 33)}, color={'cn': (189, 207, 231), 'en': (189, 207, 231), 'jp': (189, 207, 231), 'tw': (189, 207, 231)}, button={'cn': (1014, 29, 1019, 33), 'en': (1014, 29, 1019, 33), 'jp': (1013, 29, 1019, 32), 'tw': (1014, 29, 1019, 33)}, file={'cn': './assets/cn/retire/SORT_DESC.png', 'en': './assets/en/retire/SORT_DESC.png', 'jp': './assets/jp/retire/SORT_DESC.png', 'tw': './assets/tw/retire/SORT_DESC.png'}) SR_SSR_CONFIRM = Button(area={'cn': (757, 480, 829, 511), 'en': (706, 463, 881, 523), 'jp': (723, 470, 870, 509), 'tw': (757, 473, 829, 504)}, color={'cn': (150, 181, 221), 'en': (106, 152, 208), 'jp': (101, 146, 203), 'tw': (148, 180, 221)}, button={'cn': (757, 480, 829, 511), 'en': (706, 463, 881, 523), 'jp': (723, 470, 870, 509), 'tw': (757, 473, 829, 504)}, file={'cn': './assets/cn/retire/SR_SSR_CONFIRM.png', 'en': './assets/en/retire/SR_SSR_CONFIRM.png', 'jp': './assets/jp/retire/SR_SSR_CONFIRM.png', 'tw': './assets/tw/retire/SR_SSR_CONFIRM.png'}) TEMPLATE_BOGUE = Template(file={'cn': './assets/cn/retire/TEMPLATE_BOGUE.png', 'en': './assets/en/retire/TEMPLATE_BOGUE.png', 'jp': './assets/jp/retire/TEMPLATE_BOGUE.png', 'tw': './assets/tw/retire/TEMPLATE_BOGUE.png'}) -TEMPLATE_FLEET_1 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_1.png', 'en': './assets/en/retire/TEMPLATE_FLEET_1.png', 'jp': './assets/cn/retire/TEMPLATE_FLEET_1.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_1.png'}) -TEMPLATE_FLEET_2 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_2.png', 'en': './assets/en/retire/TEMPLATE_FLEET_2.png', 'jp': './assets/cn/retire/TEMPLATE_FLEET_2.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_2.png'}) -TEMPLATE_FLEET_3 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_3.png', 'en': './assets/en/retire/TEMPLATE_FLEET_3.png', 'jp': './assets/cn/retire/TEMPLATE_FLEET_3.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_3.png'}) -TEMPLATE_FLEET_4 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_4.png', 'en': './assets/en/retire/TEMPLATE_FLEET_4.png', 'jp': './assets/cn/retire/TEMPLATE_FLEET_4.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_4.png'}) -TEMPLATE_FLEET_5 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_5.png', 'en': './assets/en/retire/TEMPLATE_FLEET_5.png', 'jp': './assets/cn/retire/TEMPLATE_FLEET_5.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_5.png'}) -TEMPLATE_FLEET_6 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_6.png', 'en': './assets/en/retire/TEMPLATE_FLEET_6.png', 'jp': './assets/cn/retire/TEMPLATE_FLEET_6.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_6.png'}) +TEMPLATE_FLEET_1 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_1.png', 'en': './assets/en/retire/TEMPLATE_FLEET_1.png', 'jp': './assets/jp/retire/TEMPLATE_FLEET_1.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_1.png'}) +TEMPLATE_FLEET_2 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_2.png', 'en': './assets/en/retire/TEMPLATE_FLEET_2.png', 'jp': './assets/jp/retire/TEMPLATE_FLEET_2.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_2.png'}) +TEMPLATE_FLEET_3 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_3.png', 'en': './assets/en/retire/TEMPLATE_FLEET_3.png', 'jp': './assets/jp/retire/TEMPLATE_FLEET_3.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_3.png'}) +TEMPLATE_FLEET_4 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_4.png', 'en': './assets/en/retire/TEMPLATE_FLEET_4.png', 'jp': './assets/jp/retire/TEMPLATE_FLEET_4.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_4.png'}) +TEMPLATE_FLEET_5 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_5.png', 'en': './assets/en/retire/TEMPLATE_FLEET_5.png', 'jp': './assets/jp/retire/TEMPLATE_FLEET_5.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_5.png'}) +TEMPLATE_FLEET_6 = Template(file={'cn': './assets/cn/retire/TEMPLATE_FLEET_6.png', 'en': './assets/en/retire/TEMPLATE_FLEET_6.png', 'jp': './assets/jp/retire/TEMPLATE_FLEET_6.png', 'tw': './assets/cn/retire/TEMPLATE_FLEET_6.png'}) TEMPLATE_HERMES = Template(file={'cn': './assets/cn/retire/TEMPLATE_HERMES.png', 'en': './assets/en/retire/TEMPLATE_HERMES.png', 'jp': './assets/jp/retire/TEMPLATE_HERMES.png', 'tw': './assets/tw/retire/TEMPLATE_HERMES.png'}) -TEMPLATE_IN_BATTLE = Template(file={'cn': './assets/cn/retire/TEMPLATE_IN_BATTLE.png', 'en': './assets/en/retire/TEMPLATE_IN_BATTLE.png', 'jp': './assets/cn/retire/TEMPLATE_IN_BATTLE.png', 'tw': './assets/cn/retire/TEMPLATE_IN_BATTLE.png'}) -TEMPLATE_IN_COMMISSION = Template(file={'cn': './assets/cn/retire/TEMPLATE_IN_COMMISSION.png', 'en': './assets/en/retire/TEMPLATE_IN_COMMISSION.png', 'jp': './assets/cn/retire/TEMPLATE_IN_COMMISSION.png', 'tw': './assets/cn/retire/TEMPLATE_IN_COMMISSION.png'}) +TEMPLATE_IN_BATTLE = Template(file={'cn': './assets/cn/retire/TEMPLATE_IN_BATTLE.png', 'en': './assets/en/retire/TEMPLATE_IN_BATTLE.png', 'jp': './assets/jp/retire/TEMPLATE_IN_BATTLE.png', 'tw': './assets/cn/retire/TEMPLATE_IN_BATTLE.png'}) +TEMPLATE_IN_COMMISSION = Template(file={'cn': './assets/cn/retire/TEMPLATE_IN_COMMISSION.png', 'en': './assets/en/retire/TEMPLATE_IN_COMMISSION.png', 'jp': './assets/jp/retire/TEMPLATE_IN_COMMISSION.png', 'tw': './assets/cn/retire/TEMPLATE_IN_COMMISSION.png'}) TEMPLATE_LANGLEY = Template(file={'cn': './assets/cn/retire/TEMPLATE_LANGLEY.png', 'en': './assets/en/retire/TEMPLATE_LANGLEY.png', 'jp': './assets/jp/retire/TEMPLATE_LANGLEY.png', 'tw': './assets/tw/retire/TEMPLATE_LANGLEY.png'}) TEMPLATE_RANGER = Template(file={'cn': './assets/cn/retire/TEMPLATE_RANGER.png', 'en': './assets/en/retire/TEMPLATE_RANGER.png', 'jp': './assets/jp/retire/TEMPLATE_RANGER.png', 'tw': './assets/tw/retire/TEMPLATE_RANGER.png'}) diff --git a/module/retire/retirement.py b/module/retire/retirement.py index 01699ee6b..ef0b2a774 100644 --- a/module/retire/retirement.py +++ b/module/retire/retirement.py @@ -76,7 +76,6 @@ class Retirement(Enhancement, QuickRetireSettingHandler): """ logger.info('Retirement confirm') executed = False - backup, self._popup_offset = self._popup_offset, (20, 50) for button in [SHIP_CONFIRM, SHIP_CONFIRM_2, EQUIP_CONFIRM, EQUIP_CONFIRM_2, GET_ITEMS_1, SR_SSR_CONFIRM]: self.interval_clear(button) timeout = Timer(10, count=10).start() @@ -127,14 +126,12 @@ class Retirement(Enhancement, QuickRetireSettingHandler): or self.config.Retirement_OldRetireSR \ or self.config.Retirement_OldRetireSSR \ or self.config.Retirement_RetireMode == 'one_click_retire': - if self.handle_popup_confirm('RETIRE_SR_SSR'): + if self.handle_popup_confirm(name='RETIRE_SR_SSR', offset=(20, 50)): continue if self.config.SERVER in ['cn', 'jp', 'tw'] and \ - self.appear_then_click(SR_SSR_CONFIRM, offset=self._popup_offset, interval=2): + self.appear_then_click(SR_SSR_CONFIRM, offset=(20, 50), interval=2): continue - self._popup_offset = backup - def retirement_appear(self): return self.appear(RETIRE_APPEAR_1, offset=30) \ and self.appear(RETIRE_APPEAR_2, offset=30) \ @@ -284,7 +281,7 @@ class Retirement(Enhancement, QuickRetireSettingHandler): return 0 def server_support_flagship_retire() -> bool: - return self.config.SERVER in ['cn', 'en'] + return self.config.SERVER in ['cn', 'en', 'jp'] if not server_support_flagship_retire(): logger.info(f'Server {self.config.SERVER} does not yet support flagships retirement, skip') diff --git a/module/shop/assets.py b/module/shop/assets.py index f9057dfa9..cd3e49323 100644 --- a/module/shop/assets.py +++ b/module/shop/assets.py @@ -12,7 +12,7 @@ SELECT_MINUS = Button(area={'cn': (562, 201, 588, 227), 'en': (562, 201, 588, 22 SELECT_PLUS = Button(area={'cn': (674, 201, 700, 227), 'en': (674, 201, 700, 227), 'jp': (674, 201, 700, 227), 'tw': (674, 201, 700, 227)}, color={'cn': (97, 70, 70), 'en': (97, 70, 70), 'jp': (97, 70, 70), 'tw': (97, 70, 70)}, button={'cn': (674, 201, 700, 227), 'en': (674, 201, 700, 227), 'jp': (674, 201, 700, 227), 'tw': (674, 201, 700, 227)}, file={'cn': './assets/cn/shop/SELECT_PLUS.png', 'en': './assets/en/shop/SELECT_PLUS.png', 'jp': './assets/jp/shop/SELECT_PLUS.png', 'tw': './assets/tw/shop/SELECT_PLUS.png'}) SHOP_AMOUNT = Button(area={'cn': (600, 310, 680, 340), 'en': (600, 310, 680, 340), 'jp': (600, 310, 680, 340), 'tw': (600, 310, 680, 340)}, color={'cn': (48, 52, 62), 'en': (48, 52, 62), 'jp': (48, 52, 62), 'tw': (48, 52, 62)}, button={'cn': (600, 310, 680, 340), 'en': (600, 310, 680, 340), 'jp': (600, 310, 680, 340), 'tw': (600, 310, 680, 340)}, file={'cn': './assets/cn/shop/SHOP_AMOUNT.png', 'en': './assets/en/shop/SHOP_AMOUNT.png', 'jp': './assets/jp/shop/SHOP_AMOUNT.png', 'tw': './assets/tw/shop/SHOP_AMOUNT.png'}) SHOP_BUY_CONFIRM = Button(area={'cn': (703, 483, 876, 540), 'en': (708, 487, 872, 534), 'jp': (720, 494, 862, 531), 'tw': (706, 485, 875, 537)}, color={'cn': (94, 142, 203), 'en': (115, 157, 210), 'jp': (100, 147, 205), 'tw': (95, 143, 203)}, button={'cn': (703, 483, 876, 540), 'en': (708, 487, 872, 534), 'jp': (720, 494, 862, 531), 'tw': (706, 485, 875, 537)}, file={'cn': './assets/cn/shop/SHOP_BUY_CONFIRM.png', 'en': './assets/en/shop/SHOP_BUY_CONFIRM.png', 'jp': './assets/jp/shop/SHOP_BUY_CONFIRM.png', 'tw': './assets/tw/shop/SHOP_BUY_CONFIRM.png'}) -SHOP_BUY_CONFIRM_AMOUNT = Button(area={'cn': (678, 590, 851, 647), 'en': (689, 594, 839, 644), 'jp': (694, 596, 832, 640), 'tw': (677, 589, 852, 649)}, color={'cn': (81, 130, 190), 'en': (103, 146, 197), 'jp': (84, 132, 191), 'tw': (82, 131, 191)}, button={'cn': (678, 590, 851, 647), 'en': (689, 594, 839, 644), 'jp': (694, 596, 832, 640), 'tw': (677, 589, 852, 649)}, file={'cn': './assets/cn/shop/SHOP_BUY_CONFIRM_AMOUNT.png', 'en': './assets/en/shop/SHOP_BUY_CONFIRM_AMOUNT.png', 'jp': './assets/jp/shop/SHOP_BUY_CONFIRM_AMOUNT.png', 'tw': './assets/tw/shop/SHOP_BUY_CONFIRM_AMOUNT.png'}) +SHOP_BUY_CONFIRM_AMOUNT = Button(area={'cn': (729, 602, 800, 633), 'en': (689, 594, 839, 644), 'jp': (694, 596, 832, 640), 'tw': (729, 602, 800, 633)}, color={'cn': (137, 169, 210), 'en': (103, 146, 197), 'jp': (84, 132, 191), 'tw': (138, 171, 211)}, button={'cn': (729, 602, 800, 633), 'en': (689, 594, 839, 644), 'jp': (694, 596, 832, 640), 'tw': (729, 602, 800, 633)}, file={'cn': './assets/cn/shop/SHOP_BUY_CONFIRM_AMOUNT.png', 'en': './assets/en/shop/SHOP_BUY_CONFIRM_AMOUNT.png', 'jp': './assets/jp/shop/SHOP_BUY_CONFIRM_AMOUNT.png', 'tw': './assets/tw/shop/SHOP_BUY_CONFIRM_AMOUNT.png'}) SHOP_BUY_CONFIRM_MISTAKE = Button(area={'cn': (590, 300, 612, 318), 'en': (590, 300, 612, 318), 'jp': (590, 300, 612, 318), 'tw': (590, 300, 612, 318)}, color={'cn': (154, 79, 103), 'en': (154, 79, 103), 'jp': (154, 79, 103), 'tw': (154, 79, 103)}, button={'cn': (590, 300, 612, 318), 'en': (590, 300, 612, 318), 'jp': (590, 300, 612, 318), 'tw': (590, 300, 612, 318)}, file={'cn': './assets/cn/shop/SHOP_BUY_CONFIRM_MISTAKE.png', 'en': './assets/en/shop/SHOP_BUY_CONFIRM_MISTAKE.png', 'jp': './assets/jp/shop/SHOP_BUY_CONFIRM_MISTAKE.png', 'tw': './assets/tw/shop/SHOP_BUY_CONFIRM_MISTAKE.png'}) SHOP_BUY_CONFIRM_SELECT = Button(area={'cn': (604, 642, 676, 671), 'en': (567, 630, 712, 675), 'jp': (588, 638, 689, 669), 'tw': (558, 627, 725, 680)}, color={'cn': (236, 191, 130), 'en': (229, 171, 90), 'jp': (230, 170, 89), 'tw': (225, 155, 60)}, button={'cn': (604, 642, 676, 671), 'en': (567, 630, 712, 675), 'jp': (588, 638, 689, 669), 'tw': (558, 627, 725, 680)}, file={'cn': './assets/cn/shop/SHOP_BUY_CONFIRM_SELECT.png', 'en': './assets/en/shop/SHOP_BUY_CONFIRM_SELECT.png', 'jp': './assets/jp/shop/SHOP_BUY_CONFIRM_SELECT.png', 'tw': './assets/tw/shop/SHOP_BUY_CONFIRM_SELECT.png'}) SHOP_CLICK_SAFE_AREA = Button(area={'cn': (108, 108, 143, 233), 'en': (108, 108, 143, 233), 'jp': (108, 108, 143, 233), 'tw': (108, 108, 143, 233)}, color={'cn': (50, 57, 76), 'en': (50, 57, 76), 'jp': (50, 57, 76), 'tw': (50, 57, 76)}, button={'cn': (108, 108, 143, 233), 'en': (108, 108, 143, 233), 'jp': (108, 108, 143, 233), 'tw': (108, 108, 143, 233)}, file={'cn': './assets/cn/shop/SHOP_CLICK_SAFE_AREA.png', 'en': './assets/en/shop/SHOP_CLICK_SAFE_AREA.png', 'jp': './assets/jp/shop/SHOP_CLICK_SAFE_AREA.png', 'tw': './assets/tw/shop/SHOP_CLICK_SAFE_AREA.png'}) @@ -29,3 +29,5 @@ SHOP_SELECT_PR1 = Button(area={'cn': (178, 210, 293, 325), 'en': (178, 210, 293, SHOP_SELECT_PR2 = Button(area={'cn': (334, 208, 453, 326), 'en': (334, 208, 453, 326), 'jp': (334, 208, 453, 326), 'tw': (334, 208, 453, 326)}, color={'cn': (166, 166, 158), 'en': (166, 166, 158), 'jp': (166, 166, 158), 'tw': (166, 166, 158)}, button={'cn': (334, 208, 453, 326), 'en': (334, 208, 453, 326), 'jp': (334, 208, 453, 326), 'tw': (334, 208, 453, 326)}, file={'cn': './assets/cn/shop/SHOP_SELECT_PR2.png', 'en': './assets/en/shop/SHOP_SELECT_PR2.png', 'jp': './assets/jp/shop/SHOP_SELECT_PR2.png', 'tw': './assets/tw/shop/SHOP_SELECT_PR2.png'}) SHOP_SELECT_PR3 = Button(area={'cn': (334, 208, 453, 327), 'en': (334, 208, 453, 327), 'jp': (334, 208, 453, 327), 'tw': (334, 208, 453, 327)}, color={'cn': (161, 162, 153), 'en': (161, 162, 153), 'jp': (161, 162, 153), 'tw': (161, 162, 153)}, button={'cn': (334, 208, 453, 327), 'en': (334, 208, 453, 327), 'jp': (334, 208, 453, 327), 'tw': (334, 208, 453, 327)}, file={'cn': './assets/cn/shop/SHOP_SELECT_PR3.png', 'en': './assets/en/shop/SHOP_SELECT_PR3.png', 'jp': './assets/jp/shop/SHOP_SELECT_PR3.png', 'tw': './assets/tw/shop/SHOP_SELECT_PR3.png'}) SHOP_SELECT_STOCK = Button(area={'cn': (1070, 152, 1126, 178), 'en': (1070, 152, 1126, 178), 'jp': (1070, 152, 1126, 178), 'tw': (1070, 152, 1126, 178)}, color={'cn': (76, 88, 90), 'en': (76, 88, 90), 'jp': (76, 88, 90), 'tw': (76, 88, 90)}, button={'cn': (1070, 152, 1126, 178), 'en': (1070, 152, 1126, 178), 'jp': (1070, 152, 1126, 178), 'tw': (1070, 152, 1126, 178)}, file={'cn': './assets/cn/shop/SHOP_SELECT_STOCK.png', 'en': './assets/en/shop/SHOP_SELECT_STOCK.png', 'jp': './assets/jp/shop/SHOP_SELECT_STOCK.png', 'tw': './assets/tw/shop/SHOP_SELECT_STOCK.png'}) +SHOP_VOUCHER = Button(area={'cn': (1115, 123, 1261, 158), 'en': (1115, 123, 1261, 158), 'jp': (1115, 123, 1261, 158), 'tw': (1115, 123, 1261, 158)}, color={'cn': (90, 96, 110), 'en': (90, 96, 110), 'jp': (90, 96, 110), 'tw': (90, 96, 110)}, button={'cn': (1115, 123, 1261, 158), 'en': (1115, 123, 1261, 158), 'jp': (1115, 123, 1261, 158), 'tw': (1115, 123, 1261, 158)}, file={'cn': './assets/cn/shop/SHOP_VOUCHER.png', 'en': './assets/en/shop/SHOP_VOUCHER.png', 'jp': './assets/jp/shop/SHOP_VOUCHER.png', 'tw': './assets/tw/shop/SHOP_VOUCHER.png'}) +VOUCHER_SHOP_SCROLL_AREA = Button(area={'cn': (1257, 184, 1263, 647), 'en': (1257, 184, 1263, 647), 'jp': (1257, 184, 1263, 647), 'tw': (1257, 184, 1263, 647)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255), 'jp': (255, 255, 255), 'tw': (255, 255, 255)}, button={'cn': (1257, 184, 1263, 647), 'en': (1257, 184, 1263, 647), 'jp': (1257, 184, 1263, 647), 'tw': (1257, 184, 1263, 647)}, file={'cn': './assets/cn/shop/VOUCHER_SHOP_SCROLL_AREA.png', 'en': './assets/en/shop/VOUCHER_SHOP_SCROLL_AREA.png', 'jp': './assets/jp/shop/VOUCHER_SHOP_SCROLL_AREA.png', 'tw': './assets/tw/shop/VOUCHER_SHOP_SCROLL_AREA.png'}) diff --git a/module/shop/base.py b/module/shop/base.py index 6e7db6319..f81e103ab 100644 --- a/module/shop/base.py +++ b/module/shop/base.py @@ -14,7 +14,11 @@ from module.statistics.item import ItemGrid from module.tactical.tactical_class import Book FILTER_REGEX = re.compile( - '^(cube|drill|chip|array|pr|dr|box|bulin|book|food|plate|retrofit|cat)' + '^(array|book|box|bulin|cat' + '|chip|coin|cube|drill|food' + '|plate|retrofit|pr|dr' + '|logger|tuning' + '|hecombatplan)' '(neptune|monarch|ibuki|izumo|roon|saintlouis' '|seattle|georgia|kitakaze|azuma|friedrich' @@ -23,7 +27,9 @@ FILTER_REGEX = re.compile( '|plymouth|rupprecht|harbin|chkalov|brest' '|red|blue|yellow' '|general|gun|torpedo|antiair|plane|wild' - '|dd|cl|bb|cv)?' + '|dd|cl|bb|cv' + '|abyssal|archive|obscure' + '|combat|offense|survival)?' '(s[1-5]|t[1-6])?$', flags=re.IGNORECASE) @@ -52,10 +58,10 @@ class ShopItemGrid(ItemGrid): if group is not None else None for group in result.groups()] else: - if not name.isnumeric(): - logger.warning(f'Unable to parse shop item {name}; ' - 'check template asset and filter regexp') - raise ScriptError + # if not name.isnumeric(): + # logger.warning(f'Unable to parse shop item {name}; ' + # 'check template asset and filter regexp') + # raise ScriptError continue # Sometimes book's color and/or tier will be misidentified diff --git a/module/shop/shop_medal.py b/module/shop/shop_medal.py index 454af033b..5a42c2422 100644 --- a/module/shop/shop_medal.py +++ b/module/shop/shop_medal.py @@ -205,6 +205,7 @@ class MedalShop2(ShopClerk): self.wait_until_medal_appear() # Execute buy operations + MEDAL_SHOP_SCROLL.set_top(main=self) while 1: self.shop_buy() if MEDAL_SHOP_SCROLL.at_bottom(main=self): diff --git a/module/shop/shop_voucher.py b/module/shop/shop_voucher.py new file mode 100644 index 000000000..5184f585f --- /dev/null +++ b/module/shop/shop_voucher.py @@ -0,0 +1,245 @@ +from module.base.button import ButtonGrid +from module.base.decorator import cached_property, del_cached_property +from module.base.timer import Timer +from module.config.redirect_utils.shop_filter import voucher_redirect +from module.handler.assets import POPUP_CANCEL, POPUP_CONFIRM +from module.logger import logger +from module.map_detection.utils import Points +from module.ocr.ocr import Digit +from module.shop.assets import * +from module.shop.base import ShopItemGrid +from module.shop.clerk import ShopClerk +from module.shop.shop_medal import ShopPriceOcr +from module.ui.scroll import Scroll + +OCR_SHOP_VOUCHER = Digit(SHOP_VOUCHER, letter=(255, 255, 255), name='OCR_SHOP_VOUCHER') +VOUCHER_SHOP_SCROLL = Scroll(VOUCHER_SHOP_SCROLL_AREA, color=(255, 255, 255)) +PRICE_OCR = ShopPriceOcr([], letter=(255, 223, 57), threshold=32, name='Price_ocr') +TEMPLATE_VOUCHER_ICON = Template('./assets/shop/cost/Voucher.png') + + +class VoucherShop(ShopClerk): + @cached_property + def shop_filter(self): + """ + Returns: + str: + """ + return voucher_redirect(self.config.OpsiVoucher_Filter.strip()) + + def _get_vouchers(self): + """ + Returns: + np.array: [[x1, y1], [x2, y2]], location of the voucher icon upper-left corner. + """ + left_column = self.image_crop((476, 306, 1256, 646)) + vouchers = TEMPLATE_VOUCHER_ICON.match_multi(left_column, similarity=0.75, threshold=5) + vouchers = Points([(0., v.area[1]) for v in vouchers]).group(threshold=5) + logger.attr('Vouchers_icon', len(vouchers)) + return vouchers + + def wait_until_voucher_appear(self, skip_first_screenshot=True): + """ + After entering voucher shop page, + items are not loaded that fast, + wait until any voucher icon appears + """ + timeout = Timer(1, count=3).start() + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + vouchers = self._get_vouchers() + + if timeout.reached(): + break + if len(vouchers): + break + + @cached_property + def shop_grid(self): + """ + Returns: + ButtonGrid: + """ + vouchers = self._get_vouchers() + count = len(vouchers) + if count == 0: + logger.warning('Unable to find voucher icon, assume item list is at top') + origin_y = 200 + delta_y = 191 + row = 2 + elif count == 1: + y_list = vouchers[:, 1] + # +256, to location on screen + # -88, from the top of voucher icon to the top of shop item + origin_y = y_list[0] + 256 - 88 + delta_y = 191 + row = 1 + elif count == 2: + y_list = vouchers[:, 1] + y1, y2 = y_list[0], y_list[1] + origin_y = min(y1, y2) + 256 - 88 + delta_y = abs(y1 - y2) + row = 2 + else: + logger.warning(f'Unexpected voucher icon match result: {[v.area for v in vouchers]}') + origin_y = 200 + delta_y = 191 + row = 2 + + # Make up a ButtonGrid + # Original grid is: + # shop_grid = ButtonGrid( + # origin=(463, 200), delta=(156, 191), button_shape=(99, 99), grid_shape=(5, 2), name='SHOP_GRID') + if self.config.SERVER in ['cn', 'jp']: + shop_grid = ButtonGrid( + origin=(305, origin_y), delta=(189.5, delta_y), button_shape=(99, 99), grid_shape=(5, row), + name='SHOP_GRID') + else: + shop_grid = ButtonGrid( + origin=(463, origin_y), delta=(156, delta_y), button_shape=(99, 99), grid_shape=(5, row), + name='SHOP_GRID') + return shop_grid + + @cached_property + def shop_voucher_items(self): + """ + Returns: + ShopItemGrid: + """ + shop_grid = self.shop_grid + shop_voucher_items = ShopItemGrid( + shop_grid, + templates={}, amount_area=(60, 74, 96, 95), + price_area=(52, 132, 132, 162)) + shop_voucher_items.load_template_folder('./assets/shop/voucher') + shop_voucher_items.load_cost_template_folder('./assets/shop/cost') + shop_voucher_items.similarity = 0.85 + shop_voucher_items.cost_similarity = 0.5 + shop_voucher_items.price_ocr = PRICE_OCR + return shop_voucher_items + + def shop_items(self): + """ + Shared alias name for all shops, + so to use @Config must define + a unique alias as cover + + Returns: + ShopItemGrid: + """ + return self.shop_voucher_items + + def shop_currency(self): + """ + Ocr shop voucher currency + Then return voucher count + + Returns: + int: voucher amount + """ + self._currency = OCR_SHOP_VOUCHER.ocr(self.device.image) + logger.info(f'Voucher: {self._currency}') + return self._currency + + def shop_interval_clear(self): + """ + Clear interval on select assets for + shop_buy_handle + """ + super().shop_interval_clear() + self.interval_clear([ + SHOP_BUY_CONFIRM_SELECT, + SHOP_BUY_CONFIRM_AMOUNT, + POPUP_CONFIRM, + POPUP_CANCEL, + ]) + + def shop_buy_handle(self, item): + """ + Handle shop_voucher buy interface if detected + + Args: + item: Item to handle + + Returns: + bool: whether interface was detected and handled + """ + if self.appear(SHOP_BUY_CONFIRM_SELECT, offset=(20, 20), interval=3): + self.shop_buy_select_execute(item) + self.interval_reset(SHOP_BUY_CONFIRM_SELECT) + return True + if self.appear(SHOP_BUY_CONFIRM_AMOUNT, offset=(20, 20), interval=3): + self.shop_buy_amount_execute(item) + self.interval_reset(SHOP_BUY_CONFIRM_AMOUNT) + return True + if self.handle_popup_confirm(name='SHOP_BUY_VOUCHER', offset=(20, 50)): + return True + if self.config.SERVER in ['cn', 'jp', 'tw']: + # A button named `Exchange` when buying item in amount of 1. + if self.appear_then_click(SHOP_BUY_CONFIRM_AMOUNT, offset=(-20, -160, 20, -120), interval=3): + return True + + return False + + def run(self): + """ + Run Voucher Shop + """ + # Base case; exit run if filter empty + if not self.shop_filter: + return + + # When called, expected to be in + # correct Voucher Shop interface + logger.hr('Voucher Shop', level=1) + self.wait_until_voucher_appear() + + # Execute buy operations + VOUCHER_SHOP_SCROLL.set_top(main=self) + while 1: + self.shop_buy() + if VOUCHER_SHOP_SCROLL.at_bottom(main=self): + logger.info('Voucher Shop reach bottom, stop') + break + else: + VOUCHER_SHOP_SCROLL.next_page(main=self, page=0.66) + del_cached_property(self, 'shop_grid') + del_cached_property(self, 'shop_voucher_items') + continue + + def run_once(self): + """ + Run Voucher Shop to purchase + a single logger archive type + item + + Returns: + bool + """ + # Replace filter + self.shop_filter = 'LoggerArchive' + + # When called, expected to be in + # correct Voucher Shop interface + logger.hr('Voucher Shop Once', level=1) + self.wait_until_voucher_appear() + + # Execute buy operations + items = self.shop_get_items() + self.shop_currency() + if self._currency <= 0: + logger.warning(f'Current funds: {self._currency}, stopped') + return False + + item = self.shop_get_item_to_buy(items) + if item is None: + logger.info('No logger archives available for purchase') + return False + self.shop_buy_execute(item) + + logger.info('Purchased single logger archive') + return True diff --git a/module/statistics/azurstats.py b/module/statistics/azurstats.py index cd3499207..b6bb2eed7 100644 --- a/module/statistics/azurstats.py +++ b/module/statistics/azurstats.py @@ -37,6 +37,7 @@ class DropImage: """ if self: self.images.append(image) + logger.info(f'Drop record added, genre={self.genre}, amount={self.count}') def handle_add(self, main, before=None): """ diff --git a/module/storage/storage.py b/module/storage/storage.py index 0e087d6de..d76f0fc69 100644 --- a/module/storage/storage.py +++ b/module/storage/storage.py @@ -242,7 +242,7 @@ class StorageHandler(StorageUI): continue if self.appear_then_click(DISASSEMBLE_POPUP_CONFIRM, offset=(-15, -5, 5, 70), interval=5): continue - if self.handle_popup_confirm(): + if self.handle_popup_confirm('DISASSEMBLE'): continue if self.appear(GET_ITEMS_1, offset=(5, 5), interval=3): self.device.click(DISASSEMBLE_CONFIRM) diff --git a/module/ui/scroll.py b/module/ui/scroll.py index 63165768b..d80dd9e12 100644 --- a/module/ui/scroll.py +++ b/module/ui/scroll.py @@ -157,7 +157,7 @@ class Scroll: Args: page (int, float): Relative position to drag. 1.0 means next page, -1.0 means previous page. main (ModuleBase): - random_range (tuple[int]): + random_range (tuple[float]): skip_first_screenshot: """ if not skip_first_screenshot: diff --git a/module/ui/ui.py b/module/ui/ui.py index d86396e6f..06d7c69c1 100644 --- a/module/ui/ui.py +++ b/module/ui/ui.py @@ -517,6 +517,7 @@ class UI(InfoHandler): # Meowfficer popup if self.appear_then_click(MEOWFFICER_INFO, offset=(30, 30), interval=3): + self.interval_reset(GET_SHIP) return True # Campaign preparation diff --git a/module/war_archives/assets.py b/module/war_archives/assets.py index cb937981c..8160da932 100644 --- a/module/war_archives/assets.py +++ b/module/war_archives/assets.py @@ -15,7 +15,9 @@ TEMPLATE_FALLEN_WINGS = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_ TEMPLATE_GLORIOUS_BATTLE = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_GLORIOUS_BATTLE.png', 'en': './assets/en/war_archives/TEMPLATE_GLORIOUS_BATTLE.png', 'jp': './assets/jp/war_archives/TEMPLATE_GLORIOUS_BATTLE.png', 'tw': './assets/tw/war_archives/TEMPLATE_GLORIOUS_BATTLE.png'}) TEMPLATE_INK_STAINED_STEEL_SAKURA = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_INK_STAINED_STEEL_SAKURA.png', 'en': './assets/en/war_archives/TEMPLATE_INK_STAINED_STEEL_SAKURA.png', 'jp': './assets/jp/war_archives/TEMPLATE_INK_STAINED_STEEL_SAKURA.png', 'tw': './assets/cn/war_archives/TEMPLATE_INK_STAINED_STEEL_SAKURA.png'}) TEMPLATE_IRIS_OF_LIGHT_AND_DARK = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_IRIS_OF_LIGHT_AND_DARK.png', 'en': './assets/en/war_archives/TEMPLATE_IRIS_OF_LIGHT_AND_DARK.png', 'jp': './assets/jp/war_archives/TEMPLATE_IRIS_OF_LIGHT_AND_DARK.png', 'tw': './assets/cn/war_archives/TEMPLATE_IRIS_OF_LIGHT_AND_DARK.png'}) +TEMPLATE_MICROLAYER_MEDLEY = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_MICROLAYER_MEDLEY.png', 'en': './assets/cn/war_archives/TEMPLATE_MICROLAYER_MEDLEY.png', 'jp': './assets/cn/war_archives/TEMPLATE_MICROLAYER_MEDLEY.png', 'tw': './assets/cn/war_archives/TEMPLATE_MICROLAYER_MEDLEY.png'}) TEMPLATE_SCHERZO_OF_IRON_AND_BLOOD = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_SCHERZO_OF_IRON_AND_BLOOD.png', 'en': './assets/en/war_archives/TEMPLATE_SCHERZO_OF_IRON_AND_BLOOD.png', 'jp': './assets/cn/war_archives/TEMPLATE_SCHERZO_OF_IRON_AND_BLOOD.png', 'tw': './assets/cn/war_archives/TEMPLATE_SCHERZO_OF_IRON_AND_BLOOD.png'}) +TEMPLATE_STARS_OF_THE_SHIMMERING_FJORD = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_STARS_OF_THE_SHIMMERING_FJORD.png', 'en': './assets/cn/war_archives/TEMPLATE_STARS_OF_THE_SHIMMERING_FJORD.png', 'jp': './assets/cn/war_archives/TEMPLATE_STARS_OF_THE_SHIMMERING_FJORD.png', 'tw': './assets/cn/war_archives/TEMPLATE_STARS_OF_THE_SHIMMERING_FJORD.png'}) TEMPLATE_STRIVE_WISH_AND_STRATEGIZE = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_STRIVE_WISH_AND_STRATEGIZE.png', 'en': './assets/en/war_archives/TEMPLATE_STRIVE_WISH_AND_STRATEGIZE.png', 'jp': './assets/jp/war_archives/TEMPLATE_STRIVE_WISH_AND_STRATEGIZE.png', 'tw': './assets/tw/war_archives/TEMPLATE_STRIVE_WISH_AND_STRATEGIZE.png'}) TEMPLATE_SWIRLING_CHERRY_BLOSSOMS = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_SWIRLING_CHERRY_BLOSSOMS.png', 'en': './assets/en/war_archives/TEMPLATE_SWIRLING_CHERRY_BLOSSOMS.png', 'jp': './assets/cn/war_archives/TEMPLATE_SWIRLING_CHERRY_BLOSSOMS.png', 'tw': './assets/cn/war_archives/TEMPLATE_SWIRLING_CHERRY_BLOSSOMS.png'}) TEMPLATE_THE_ENIGMA_AND_THE_SHARK = Template(file={'cn': './assets/cn/war_archives/TEMPLATE_THE_ENIGMA_AND_THE_SHARK.png', 'en': './assets/cn/war_archives/TEMPLATE_THE_ENIGMA_AND_THE_SHARK.png', 'jp': './assets/cn/war_archives/TEMPLATE_THE_ENIGMA_AND_THE_SHARK.png', 'tw': './assets/cn/war_archives/TEMPLATE_THE_ENIGMA_AND_THE_SHARK.png'}) diff --git a/module/war_archives/dictionary.py b/module/war_archives/dictionary.py index bb9dd48df..14b3736cb 100644 --- a/module/war_archives/dictionary.py +++ b/module/war_archives/dictionary.py @@ -18,4 +18,6 @@ dic_archives_template = { 'war_archives_20210624_cn': TEMPLATE_SWIRLING_CHERRY_BLOSSOMS, 'war_archives_20200806_cn': TEMPLATE_THE_ENIGMA_AND_THE_SHARK, 'war_archives_20201029_cn': TEMPLATE_UNIVERSE_IN_UNISON, + 'war_archives_20200903_cn': TEMPLATE_STARS_OF_THE_SHIMMERING_FJORD, + 'war_archives_20210819_cn': TEMPLATE_MICROLAYER_MEDLEY, } diff --git a/module/webui/app.py b/module/webui/app.py index 3c8b5863d..547ed3521 100644 --- a/module/webui/app.py +++ b/module/webui/app.py @@ -9,8 +9,8 @@ from typing import Dict, List, Optional import module.webui.lang as lang from module.config.config import AzurLaneConfig, Function from module.config.utils import ( - alas_template, alas_instance, + alas_template, deep_get, deep_iter, deep_set, @@ -39,9 +39,9 @@ from module.webui.utils import ( TaskHandler, add_css, filepath_css, + get_alas_config_listen_path, get_localstorage, get_window_visibility_state, - get_alas_config_listen_path, login, parse_pin_value, raise_exception, @@ -51,13 +51,14 @@ from module.webui.widgets import ( BinarySwitchButton, RichLog, T_Output_Kwargs, - put_output, put_icon_buttons, put_loading_text, put_none, + put_output, ) from pywebio import config as webconfig from pywebio.output import ( + Output, clear, close_popup, popup, @@ -79,7 +80,7 @@ from pywebio.output import ( use_scope, ) from pywebio.pin import pin, pin_on_change -from pywebio.session import go_app, info, register_thread, run_js, set_env +from pywebio.session import go_app, info, local, register_thread, run_js, set_env task_handler = TaskHandler() @@ -224,14 +225,14 @@ class AlasGUI(Frame): config = self.alas_config.read_file(self.alas_name) for group, arg_dict in deep_iter(self.ALAS_ARGS[task], depth=1): - self.set_group(group, arg_dict, config, task) - self.set_navigator(group) + if self.set_group(group, arg_dict, config, task): + self.set_navigator(group) @use_scope("groups") def set_group(self, group, arg_dict, config, task): group_name = group[0] - output_list = [] + output_list: List[Output] = [] for arg, arg_dict in deep_iter(arg_dict, depth=1): output_kwargs: T_Output_Kwargs = arg_dict.copy() @@ -274,12 +275,14 @@ class AlasGUI(Frame): # Invalid feedback output_kwargs["invalid_feedback"] = t("Gui.Text.InvalidFeedBack", value) - # output will inherit current scope when created - with use_scope(f"group_{group_name}"): - output_list.append(put_output(output_kwargs)) + o = put_output(output_kwargs) + if o is not None: + # output will inherit current scope when created, override here + o.spec["scope"] = f"#pywebio-scope-group_{group_name}" + output_list.append(o) if not output_list: - return + return 0 with use_scope(f"group_{group_name}"): put_text(t(f"{group_name}._info.name")) @@ -289,6 +292,8 @@ class AlasGUI(Frame): put_html('
') for output in output_list: output.show() + + return len(output_list) @use_scope("navigator") def set_navigator(self, group): @@ -425,11 +430,11 @@ class AlasGUI(Frame): break def _save_config( - self, - modified: Dict[str, str], - config_name: str, - read=State.config_updater.read_file, - write=State.config_updater.write_file + self, + modified: Dict[str, str], + config_name: str, + read=State.config_updater.read_file, + write=State.config_updater.write_file, ) -> None: try: valid = [] @@ -616,6 +621,8 @@ class AlasGUI(Frame): config = self.alas_config.read_file(self.alas_name) for group, arg_dict in deep_iter(self.ALAS_ARGS[task], depth=1): + if group[0] == "Storage": + continue self.set_group(group, arg_dict, config, task) self.task_handler.add(switch_scheduler.g(), 1, True) @@ -951,7 +958,7 @@ class AlasGUI(Frame): name = pin["AddAlas_name"] origin = pin["AddAlas_copyfrom"] - if set(name) & set(".\\/:*?\"<>|"): + if set(name) & set(".\\/:*?\"'<>|"): clear(s) put(name, origin) put_error(t("Gui.AddAlas.InvalidChar"), scope=s) @@ -1239,7 +1246,9 @@ def app(): time.sleep(1.5) run_js("location.reload();") return - AlasGUI().run() + gui = AlasGUI() + local.gui = gui + gui.run() app = asgi_app( applications=[index], diff --git a/module/webui/utils.py b/module/webui/utils.py index fb5d1944f..7cd3a35ee 100644 --- a/module/webui/utils.py +++ b/module/webui/utils.py @@ -20,6 +20,7 @@ from pywebio.session import register_thread, run_js from rich.console import Console, ConsoleOptions from rich.terminal_theme import TerminalTheme + RE_DATETIME = ( r"\d{4}\-(0\d|1[0-2])\-([0-2]\d|[3][0-1]) " r"([0-1]\d|[2][0-3]):([0-5]\d):([0-5]\d)" @@ -396,6 +397,7 @@ str2type = { "float": float, "int": int, "bool": bool, + "ignore": lambda x: x, } diff --git a/module/webui/widgets.py b/module/webui/widgets.py index 1305de0aa..90dddbf02 100644 --- a/module/webui/widgets.py +++ b/module/webui/widgets.py @@ -1,6 +1,8 @@ +import json import random import string -from typing import Any, Callable, Dict, Generator, List, Union +import time +from typing import TYPE_CHECKING, Any, Callable, Dict, Generator, List, Optional, Union from module.logger import WEB_THEME, Highlighter, HTMLConsole from module.webui.lang import t @@ -16,9 +18,13 @@ from module.webui.utils import ( from pywebio.exceptions import SessionException from pywebio.io_ctrl import Output from pywebio.output import * -from pywebio.session import eval_js, run_js +from pywebio.pin import pin +from pywebio.session import eval_js, local, run_js from rich.console import ConsoleRenderable +if TYPE_CHECKING: + from module.webui.app import AlasGUI + class ScrollableCode: """ @@ -350,7 +356,9 @@ def put_arg_select(kwargs: T_Output_Kwargs) -> Output: def put_arg_textarea(kwargs: T_Output_Kwargs) -> Output: name: str = kwargs["name"] mode: str = kwargs.pop("mode", None) - kwargs.setdefault("code", {"lineWrapping": True, "lineNumbers": False, "mode": mode}) + kwargs.setdefault( + "code", {"lineWrapping": True, "lineNumbers": False, "mode": mode} + ) return put_scope( f"arg_contianer-textarea-{name}", @@ -378,27 +386,59 @@ def put_arg_checkbox(kwargs: T_Output_Kwargs) -> Output: def put_arg_datetime(kwargs: T_Output_Kwargs) -> Output: - name: str = kwargs.get("name") + name: str = kwargs["name"] return put_scope( f"arg_container-datetime-{name}", [ get_title_help(kwargs), put_input(**kwargs).style("--input--"), - ] + ], + ) + + +def put_arg_storage(kwargs: T_Output_Kwargs) -> Optional[Output]: + name: str = kwargs["name"] + if kwargs["value"] == {}: + return None + + kwargs["value"] = json.dumps( + kwargs["value"], indent=2, ensure_ascii=False, sort_keys=False, default=str + ) + kwargs.setdefault( + "code", {"lineWrapping": True, "lineNumbers": False, "mode": "json"} + ) + + def clear_callback(): + alasgui: "AlasGUI" = local.gui + alasgui.modified_config_queue.put( + {"name": ".".join(name.split("_")), "value": {}} + ) + # https://github.com/pywebio/PyWebIO/issues/459 + # pin[name] = "{}" + + return put_scope( + f"arg_container-storage-{name}", + [ + put_textarea(**kwargs), + put_html( + f'' + ).onclick(clear_callback), + ], ) _widget_type_to_func: Dict[str, Callable] = { "input": put_arg_input, "lock": put_arg_input, - "datetime": put_arg_input, # TODO + "datetime": put_arg_input, # TODO "select": put_arg_select, "textarea": put_arg_textarea, "checkbox": put_arg_checkbox, + "storage": put_arg_storage, } -def put_output(output_kwargs: T_Output_Kwargs) -> Output: +def put_output(output_kwargs: T_Output_Kwargs) -> Optional[Output]: return _widget_type_to_func[output_kwargs["widget_type"]](output_kwargs) diff --git a/submodule/AlasMaaBridge/module/config/i18n/zh-CN.json b/submodule/AlasMaaBridge/module/config/i18n/zh-CN.json index d85acac31..026cbcd5d 100644 --- a/submodule/AlasMaaBridge/module/config/i18n/zh-CN.json +++ b/submodule/AlasMaaBridge/module/config/i18n/zh-CN.json @@ -152,7 +152,7 @@ }, "MaaPath": { "name": "MAA安装路径", - "help": "警告:由于MAA本身不支持多开,如果想要使用多开功能需要手动复制MAA,否则会导致严重错误" + "help": "Alas的MAA插件支持多开,多个MAA配置可以共用安装路径" } }, "MaaRecord": {