From 49c9e0afd9768c703ef092703eceeed7e7be0dab Mon Sep 17 00:00:00 2001 From: LmeSzinc <37934724+LmeSzinc@users.noreply.github.com> Date: Thu, 24 Feb 2022 20:43:38 +0800 Subject: [PATCH] Add: Abyssal Refrain chapter AC - Add: clear_bouncing_enemy() - Opt: Retry faster if fleet was caught by a moving enemy --- campaign/event_20220224_cn/a1.py | 89 +++++++++++++++++ campaign/event_20220224_cn/a2.py | 103 +++++++++++++++++++ campaign/event_20220224_cn/a3.py | 100 +++++++++++++++++++ campaign/event_20220224_cn/c1.py | 89 +++++++++++++++++ campaign/event_20220224_cn/c2.py | 104 ++++++++++++++++++++ campaign/event_20220224_cn/c3.py | 102 +++++++++++++++++++ campaign/event_20220224_cn/campaign_base.py | 8 ++ module/campaign/campaign_base.py | 2 + module/config/config_manual.py | 1 + module/handler/fast_forward.py | 1 + module/map/fleet.py | 11 ++- module/map/map.py | 45 +++++++++ module/map/map_base.py | 33 ++++++- module/map_detection/grid_info.py | 6 ++ 14 files changed, 689 insertions(+), 5 deletions(-) create mode 100644 campaign/event_20220224_cn/a1.py create mode 100644 campaign/event_20220224_cn/a2.py create mode 100644 campaign/event_20220224_cn/a3.py create mode 100644 campaign/event_20220224_cn/c1.py create mode 100644 campaign/event_20220224_cn/c2.py create mode 100644 campaign/event_20220224_cn/c3.py create mode 100644 campaign/event_20220224_cn/campaign_base.py diff --git a/campaign/event_20220224_cn/a1.py b/campaign/event_20220224_cn/a1.py new file mode 100644 index 000000000..e638846a8 --- /dev/null +++ b/campaign/event_20220224_cn/a1.py @@ -0,0 +1,89 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + +MAP = CampaignMap('A1') +MAP.shape = 'J6' +MAP.camera_data = ['D2', 'D4', 'G2', 'G4'] +MAP.camera_data_spawn_point = ['D2', 'D4'] +MAP.map_data = """ + ++ Me Me ++ ++ ++ ME -- ME ++ + -- -- -- ME -- -- -- -- -- ME + SP -- -- __ -- -- -- -- -- MB + SP -- -- ME -- -- -- -- -- MB + ++ -- -- -- ++ -- ME -- ME -- + ++ Me Me ++ ++ ME -- ++ ++ ++ +""" +MAP.map_data_loop = """ + ++ Me Me ++ ++ ++ ME -- ME ++ + -- -- -- ME -- -- -- -- -- ME + SP -- -- __ -- MS -- MS -- MB + SP -- -- ME -- -- -- -- -- MB + ++ -- -- ME ++ -- ME -- ME -- + ++ Me Me ++ ++ ME -- ++ ++ ++ +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1, 'boss': 1}, + {'battle': 4, 'enemy': 1}, +] +MAP.spawn_data_loop = [ + {'battle': 0, 'enemy': 2, 'siren': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1, 'boss': 1}, + {'battle': 4, 'enemy': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, 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() + +MAP.bouncing_enemy_data = [(C2, C3, C4), ] +MAP.fortress_data = [E3, (E2, F2, F3, E4, F4)] + + +class Config: + # ===== Start of generated config ===== + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_HAS_FORTRESS = True + MAP_HAS_BOUNCING_ENEMY = True + MAP_SWIPE_MULTIPLY = 1.698 + MAP_SWIPE_MULTIPLY_MINITOUCH = 1.642 + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_bouncing_enemy(): + return True + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_3(self): + return self.clear_boss() diff --git a/campaign/event_20220224_cn/a2.py b/campaign/event_20220224_cn/a2.py new file mode 100644 index 000000000..be9837c03 --- /dev/null +++ b/campaign/event_20220224_cn/a2.py @@ -0,0 +1,103 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .a1 import Config as ConfigBase + +MAP = CampaignMap('A2') +MAP.shape = 'H10' +MAP.camera_data = ['D2', 'E5', 'E7'] +MAP.camera_data_spawn_point = ['D2'] +MAP.map_data = """ + ++ -- SP SP -- -- ++ ++ + -- Me -- -- -- Me ++ ++ + Me -- -- -- -- -- Me -- + ++ ++ -- __ -- Me -- -- + -- ++ -- -- -- ++ ++ ++ + -- ME -- -- -- -- ME -- + ++ ME -- -- -- -- -- MB + -- ME -- ME -- -- ME -- + -- ++ ME -- ME ME ++ ++ + -- -- -- -- -- -- -- ++ +""" +MAP.map_data_loop = """ + ++ -- SP SP -- -- ++ ++ + -- Me -- -- -- Me ++ ++ + Me -- -- MS -- -- Me -- + ++ ++ -- __ -- Me -- -- + -- ++ -- MS -- ++ ++ ++ + -- ME -- -- ME -- ME -- + ++ ME -- -- -- -- -- MB + -- ME -- ME -- -- ME -- + -- ++ ME -- ME ME ++ ++ + -- -- -- -- -- -- -- ++ +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +MAP.spawn_data_loop = [ + {'battle': 0, 'enemy': 2, 'siren': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, \ +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, \ +A8, B8, C8, D8, E8, F8, G8, H8, \ +A9, B9, C9, D9, E9, F9, G9, H9, \ +A10, B10, C10, D10, E10, F10, G10, H10, \ + = MAP.flatten() + +MAP.bouncing_enemy_data = [(B3, C3, D3, E3, F3), ] +MAP.fortress_data = [D5, (C5, E5, C6, D6, E6)] + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = 1.859 + MAP_SWIPE_MULTIPLY_MINITOUCH = 1.797 + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_bouncing_enemy(): + return True + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/event_20220224_cn/a3.py b/campaign/event_20220224_cn/a3.py new file mode 100644 index 000000000..371486575 --- /dev/null +++ b/campaign/event_20220224_cn/a3.py @@ -0,0 +1,100 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .a1 import Config as ConfigBase + +MAP = CampaignMap('A3') +MAP.shape = 'I9' +MAP.camera_data = ['E3', 'E5', 'E7'] +MAP.camera_data_spawn_point = ['E7'] +MAP.map_data = """ + ++ ++ ++ -- MB -- ++ ++ ++ + ++ -- -- -- -- -- -- -- ++ + -- ME -- ME ++ ME -- ME -- + -- -- -- -- -- -- -- -- -- + -- ME -- -- -- -- -- ME -- + ++ ++ -- -- -- -- -- ++ ++ + ++ ++ Me -- Me -- Me ++ ++ + ++ Me -- -- __ -- -- Me ++ + ++ -- -- SP -- SP -- -- ++ +""" +MAP.map_data_loop = """ + ++ ++ ++ -- MB -- ++ ++ ++ + ++ -- -- -- -- -- -- -- ++ + -- ME -- ME ++ ME -- ME -- + -- -- -- -- -- -- -- -- -- + -- ME -- ME -- ME -- ME -- + ++ ++ -- MS -- MS -- ++ ++ + ++ ++ Me -- Me -- Me ++ ++ + ++ Me -- -- __ -- -- Me ++ + ++ -- -- SP -- SP -- -- ++ +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 1}, + {'battle': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +MAP.spawn_data_loop = [ + {'battle': 0, 'enemy': 2, 'siren': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 1}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, \ +A9, B9, C9, D9, E9, F9, G9, H9, I9, \ + = MAP.flatten() + +MAP.bouncing_enemy_data = [(C4, D4, E4, F4, G4), ] +MAP.fortress_data = [(D6, F6), (C5, D5, E5, F5, G5, C6, E6, G6)] + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = 1.518 + MAP_SWIPE_MULTIPLY_MINITOUCH = 1.468 + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_bouncing_enemy(): + return True + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/event_20220224_cn/c1.py b/campaign/event_20220224_cn/c1.py new file mode 100644 index 000000000..8699f2949 --- /dev/null +++ b/campaign/event_20220224_cn/c1.py @@ -0,0 +1,89 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + +MAP = CampaignMap('C1') +MAP.shape = 'J6' +MAP.camera_data = ['D2', 'D4', 'G2', 'G4'] +MAP.camera_data_spawn_point = ['D2', 'D4'] +MAP.map_data = """ + ++ Me Me ++ ++ ++ ME -- ME ++ + -- -- -- ME -- -- -- -- -- ME + SP -- -- __ -- -- -- -- -- MB + SP -- -- ME -- -- -- -- -- MB + ++ -- -- -- ++ -- ME -- ME -- + ++ Me Me ++ ++ ME -- ++ ++ ++ +""" +MAP.map_data_loop = """ + ++ Me Me ++ ++ ++ ME -- ME ++ + -- -- -- ME -- -- -- -- -- ME + SP -- -- __ -- MS -- MS -- MB + SP -- -- ME -- -- -- -- -- MB + ++ -- -- ME ++ -- ME -- ME -- + ++ Me Me ++ ++ ME -- ++ ++ ++ +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +MAP.spawn_data_loop = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, 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() + +MAP.bouncing_enemy_data = [(C2, C3, C4), (H1, H2, H3, H4, H5)] +MAP.fortress_data = [E3, (E2, F2, F3, E4, F4)] + + +class Config: + # ===== Start of generated config ===== + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_HAS_FORTRESS = True + MAP_HAS_BOUNCING_ENEMY = True + MAP_SWIPE_MULTIPLY = 1.698 + MAP_SWIPE_MULTIPLY_MINITOUCH = 1.642 + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_bouncing_enemy(): + return True + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/event_20220224_cn/c2.py b/campaign/event_20220224_cn/c2.py new file mode 100644 index 000000000..e89cb9234 --- /dev/null +++ b/campaign/event_20220224_cn/c2.py @@ -0,0 +1,104 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .c1 import Config as ConfigBase + +MAP = CampaignMap('C2') +MAP.shape = 'H10' +MAP.camera_data = ['D2', 'E5', 'E7'] +MAP.camera_data_spawn_point = ['D2'] +MAP.map_data = """ + ++ -- SP SP -- -- ++ ++ + -- Me -- -- -- Me ++ ++ + Me -- -- -- -- -- Me -- + ++ ++ -- __ -- Me -- -- + -- ++ -- -- -- ++ ++ ++ + -- ME -- -- -- -- ME -- + ++ ME -- -- -- -- -- MB + -- ME -- ME -- -- ME -- + -- ++ ME -- ME ME ++ ++ + -- -- -- -- -- -- -- ++ +""" +MAP.map_data_loop = """ + ++ -- SP SP -- -- ++ ++ + -- Me -- -- -- Me ++ ++ + Me -- -- MS -- -- Me -- + ++ ++ -- __ -- Me -- -- + -- ++ -- MS -- ++ ++ ++ + -- ME -- -- ME -- ME -- + ++ ME -- -- -- -- -- MB + -- ME -- ME -- -- ME -- + -- ++ ME -- ME ME ++ ++ + -- -- -- -- -- -- -- ++ +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 1}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +MAP.spawn_data_loop = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, \ +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, \ +A8, B8, C8, D8, E8, F8, G8, H8, \ +A9, B9, C9, D9, E9, F9, G9, H9, \ +A10, B10, C10, D10, E10, F10, G10, H10, \ + = MAP.flatten() + +MAP.bouncing_enemy_data = [(B3, C3, D3, E3, F3), (F6, F7, F8)] +MAP.fortress_data = [D5, (C5, E5, C6, D6, E6)] + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = 1.859 + MAP_SWIPE_MULTIPLY_MINITOUCH = 1.797 + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_bouncing_enemy(): + return True + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_4(self): + return self.clear_boss() diff --git a/campaign/event_20220224_cn/c3.py b/campaign/event_20220224_cn/c3.py new file mode 100644 index 000000000..d6e34cf55 --- /dev/null +++ b/campaign/event_20220224_cn/c3.py @@ -0,0 +1,102 @@ +from .campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .c1 import Config as ConfigBase + +MAP = CampaignMap('C3') +MAP.shape = 'I9' +MAP.camera_data = ['E3', 'E5', 'E7'] +MAP.camera_data_spawn_point = ['E7'] +MAP.map_data = """ + ++ ++ ++ -- MB -- ++ ++ ++ + ++ -- -- -- -- -- -- -- ++ + -- ME -- ME ++ ME -- ME -- + -- -- -- -- -- -- -- -- -- + -- ME -- -- -- -- -- ME -- + ++ ++ -- -- -- -- -- ++ ++ + ++ ++ Me -- Me -- Me ++ ++ + ++ Me -- -- __ -- -- Me ++ + ++ -- -- SP -- SP -- -- ++ +""" +MAP.map_data_loop = """ + ++ ++ ++ -- MB -- ++ ++ ++ + ++ -- -- -- -- -- -- -- ++ + -- ME -- ME ++ ME -- ME -- + -- -- -- -- -- -- -- -- -- + -- ME -- ME -- ME -- ME -- + ++ ++ -- MS -- MS -- ++ ++ + ++ ++ Me -- Me -- Me ++ ++ + ++ Me -- -- __ -- -- Me ++ + ++ -- -- SP -- SP -- -- ++ +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 1}, + {'battle': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1}, + {'battle': 5, 'boss': 1}, +] +MAP.spawn_data_loop = [ + {'battle': 0, 'enemy': 2, 'siren': 2}, + {'battle': 1, 'enemy': 1}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1}, + {'battle': 5, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, \ +A9, B9, C9, D9, E9, F9, G9, H9, I9, \ + = MAP.flatten() + +MAP.bouncing_enemy_data = [(C4, D4, E4, F4, G4), (C2, D2, E2, F2, G2)] +MAP.fortress_data = [(D6, F6), (C5, D5, E5, F5, G5, C6, E6, G6)] + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_HAS_MAP_STORY = True + MAP_HAS_FLEET_STEP = True + MAP_HAS_AMBUSH = False + MAP_HAS_MYSTERY = False + # ===== End of generated config ===== + + MAP_SWIPE_MULTIPLY = 1.518 + MAP_SWIPE_MULTIPLY_MINITOUCH = 1.468 + + +class Campaign(CampaignBase): + MAP = MAP + ENEMY_FILTER = '1L > 1M > 1E > 1C > 2L > 2M > 2E > 2C > 3L > 3M > 3E > 3C' + + def battle_0(self): + if self.clear_bouncing_enemy(): + return True + if self.clear_siren(): + return True + if self.clear_filter_enemy(self.ENEMY_FILTER, preserve=0): + return True + + return self.battle_default() + + def battle_5(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/event_20220224_cn/campaign_base.py b/campaign/event_20220224_cn/campaign_base.py new file mode 100644 index 000000000..f96d2f7bf --- /dev/null +++ b/campaign/event_20220224_cn/campaign_base.py @@ -0,0 +1,8 @@ +from module.campaign.campaign_base import CampaignBase as CampaignBase_ + + +class CampaignBase(CampaignBase_): + def handle_clear_mode_config_cover(self): + if super().handle_clear_mode_config_cover(): + self.config.MAP_SIREN_TEMPLATE = ['SS'] + self.config.MAP_HAS_SIREN = True diff --git a/module/campaign/campaign_base.py b/module/campaign/campaign_base.py index 545ab562c..9f3ba0670 100644 --- a/module/campaign/campaign_base.py +++ b/module/campaign/campaign_base.py @@ -66,6 +66,8 @@ class CampaignBase(CampaignUI, Map, AutoSearchCombat): return True return self.battle_default() else: + if self.clear_bouncing_enemy(): + return True if self.clear_siren(): return True self.clear_mechanism() diff --git a/module/config/config_manual.py b/module/config/config_manual.py index c6a574b60..f9fc2bdcb 100644 --- a/module/config/config_manual.py +++ b/module/config/config_manual.py @@ -89,6 +89,7 @@ class ManualConfig: MAP_HAS_MAZE = False # event_20210422_cn adds maze and maze walls move every 3 rounds. MAP_HAS_FORTRESS = False # event_2021917_cn, clear fortress to remove roadblock to boss. MAP_HAS_MISSILE_ATTACK = False # event_202111229_cn, missile attack covers the feature area of sirens. + MAP_HAS_BOUNCING_ENEMY = False # event_20220224_cn, enemy is bouncing in a fixed route. MAP_FOCUS_ENEMY_AFTER_BATTLE = False # Operation siren MAP_ENEMY_TEMPLATE = ['Light', 'Main', 'Carrier', 'Treasure'] MAP_SIREN_TEMPLATE = ['DD', 'CL', 'CA', 'BB', 'CV'] diff --git a/module/handler/fast_forward.py b/module/handler/fast_forward.py index 9fd3deeaa..06faa2fb2 100644 --- a/module/handler/fast_forward.py +++ b/module/handler/fast_forward.py @@ -112,6 +112,7 @@ class FastForwardHandler(AutoSearchHandler): self.config.MAP_HAS_LAND_BASED = False self.config.MAP_HAS_MAZE = False self.config.MAP_HAS_FORTRESS = False + self.config.MAP_HAS_BOUNCING_ENEMY = False self.map_is_clear_mode = True self.map_is_auto_search = self.config.Campaign_UseAutoSearch self.map_is_2x_book = self.config.Campaign_Use2xBook diff --git a/module/map/fleet.py b/module/map/fleet.py index 453730603..84c8ac7e2 100644 --- a/module/map/fleet.py +++ b/module/map/fleet.py @@ -204,6 +204,11 @@ class Fleet(Camera, AmbushHandler): if (self.round + 1) % 3 == 0: second += 1.0 + if self.config.MAP_HAS_BOUNCING_ENEMY: + for route in self.map.bouncing_enemy_data: + if route.select(may_bouncing_enemy=True): + second += self.config.MAP_SIREN_MOVE_WAIT + return second @property @@ -325,6 +330,8 @@ class Fleet(Camera, AmbushHandler): arrive_timer = Timer(0.5 + extra, count=2) arrive_unexpected_timer = Timer(1.5 + extra, count=6) walk_timeout.reset() + if not (grid.predict_fleet() and grid.predict_current_fleet()): + ambushed_retry.start() # Ambush if self.handle_ambush(): @@ -811,7 +818,8 @@ class Fleet(Camera, AmbushHandler): self.map.load_mechanism( land_based=self.config.MAP_HAS_LAND_BASED, maze=self.config.MAP_HAS_MAZE, - fortress=self.config.MAP_HAS_FORTRESS + fortress=self.config.MAP_HAS_FORTRESS, + bouncing_enemy=self.config.MAP_HAS_BOUNCING_ENEMY, ) def map_control_init(self): @@ -844,6 +852,7 @@ class Fleet(Camera, AmbushHandler): if self.config.POOR_MAP_DATA and self.map.is_map_data_poor: self.config.POOR_MAP_DATA = False self.map.fortress_data = [(), ()] + self.map.bouncing_enemy_data = [] return True diff --git a/module/map/map.py b/module/map/map.py index 1deadc202..47b6a0a4e 100644 --- a/module/map/map.py +++ b/module/map/map.py @@ -1,3 +1,4 @@ +import itertools import re from module.base.filter import Filter @@ -673,3 +674,47 @@ class Map(Fleet): return True return False + + def clear_bouncing_enemy(self): + """ + Clear enemies which are bouncing in a fixed route. + This method will be disabled once it cleared an enemy, since there's only one bouncing enemy on the map. + + Args: + route (tuple[GridInfo]): + + Returns: + bool: If cleared an enemy. + """ + if not self.config.MAP_HAS_BOUNCING_ENEMY: + return False + + route = None + for a_route in self.map.bouncing_enemy_data: + if a_route.select(may_bouncing_enemy=True, is_accessible=True): + route = a_route + break + if route is None: + return False + + logger.hr('Clear bouncing enemy') + logger.info(f'Clear bouncing enemy: {route}') + self.show_fleet() + prev = self.battle_count + for n, grid in enumerate(itertools.cycle(route)): + if self.config.Emotion_CalculateEmotion and self.config.Campaign_UseFleetLock: + self.emotion.wait(fleet_index=self.fleet_current_index) + self.goto(grid, expected='combat_nothing') + + if self.battle_count > prev: + logger.info('Cleared an bouncing enemy') + route.select(may_bouncing_enemy=True).set(may_bouncing_enemy=False) + self.full_scan() + self.find_path_initial() + self.map.show_cost() + return True + if n >= 12: + logger.warning('Failed to clear bouncing enemy after 12 trial') + return False + + return False diff --git a/module/map/map_base.py b/module/map/map_base.py index 937b76b7c..e7666a417 100644 --- a/module/map/map_base.py +++ b/module/map/map_base.py @@ -21,6 +21,7 @@ class CampaignMap: self._maze_data = [] self.maze_round = 9 self._fortress_data = [(), ()] + self._bouncing_enemy_data = [] self._spawn_data = [] self._spawn_data_stack = [] self._spawn_data_loop = [] @@ -198,8 +199,8 @@ class CampaignMap: @fortress_data.setter def fortress_data(self, data): enemy, block = data - enemy = self.to_selected((enemy,) if isinstance(enemy, str) else enemy) - block = self.to_selected((block,) if isinstance(block, str) else block) + enemy = self.to_selected((enemy,) if not isinstance(enemy, (tuple, list)) else enemy) + block = self.to_selected((block,) if not isinstance(block, (tuple, list)) else block) self._fortress_data = [enemy, block] def _load_fortress_data(self, data): @@ -213,14 +214,34 @@ class CampaignMap: enemy.set(is_fortress=True) block.set(is_mechanism_block=True) - def load_mechanism(self, land_based=False, maze=False, fortress=False): - logger.info(f'Load mechanism, land_base={land_based}, maze={maze}, fortress={fortress}') + @property + def bouncing_enemy_data(self): + return self._bouncing_enemy_data + + @bouncing_enemy_data.setter + def bouncing_enemy_data(self, data): + self._bouncing_enemy_data = [self.to_selected(route) for route in data] + + def _load_bouncing_enemy_data(self, data): + """ + Args: + data (list[SelectedGrids]): Grids that enemy is bouncing in. + [enemy_route, enemy_route, ...], Such as [(C2, C3, C4), ] + """ + for route in data: + route.set(may_bouncing_enemy=True) + + def load_mechanism(self, land_based=False, maze=False, fortress=False, bouncing_enemy=False): + logger.info(f'Load mechanism, land_base={land_based}, maze={maze}, fortress={fortress}, ' + f'bouncing_enemy={bouncing_enemy}') if land_based: self._load_land_base_data(self.land_based_data) if maze: self._load_maze_data(self.maze_data) if fortress: self._load_fortress_data(self._fortress_data) + if bouncing_enemy: + self._load_bouncing_enemy_data(self._bouncing_enemy_data) def grid_connection_initial(self, wall=False, portal=False): """ @@ -691,6 +712,10 @@ class CampaignMap: if grid.__getattribute__('is_' + attr): missing[attr] -= 1 missing['enemy'] += len(self.fortress_data[0]) - self.select(is_fortress=True).count + for route in self.bouncing_enemy_data: + if not route.select(may_bouncing_enemy=True): + # bouncing enemy cleared, re-add one enemy + missing['enemy'] += 1 for upper in self.map_covered: if (upper.may_enemy or mode == 'movable') and not upper.is_enemy: diff --git a/module/map_detection/grid_info.py b/module/map_detection/grid_info.py index 5360d07cf..94aad1f7d 100644 --- a/module/map_detection/grid_info.py +++ b/module/map_detection/grid_info.py @@ -65,6 +65,7 @@ class GridInfo: is_fortress = False # Machine fortress is_flare = False is_missile_attack = False + may_bouncing_enemy = False cost = 9999 cost_1 = 9999 cost_2 = 9999 @@ -122,6 +123,7 @@ class GridInfo: 'AM': 'is_ammo', 'FR': 'is_fortress', 'MI': 'is_missile_attack', + 'BE': 'may_bouncing_enemy', '==': 'is_cleared', } for key, value in dic.items(): @@ -244,6 +246,9 @@ class GridInfo: if info.enemy_genre and not (info.enemy_genre == 'Enemy' and self.enemy_genre): self.enemy_genre = info.enemy_genre return True + elif self.is_fortress: + # Fortress can be a normal enemy + return True else: return False if info.is_mystery: @@ -303,6 +308,7 @@ class GridInfo: self.is_mechanism_block = False self.mechanism_trigger = None self.mechanism_block = None + self.may_bouncing_enemy = False def covered_grid(self): """Relative coordinate of the covered grid.