diff --git a/campaign/campaign_main/campaign_14_1.py b/campaign/campaign_main/campaign_14_1.py new file mode 100644 index 000000000..e561c92bd --- /dev/null +++ b/campaign/campaign_main/campaign_14_1.py @@ -0,0 +1,70 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger + +MAP = CampaignMap('14-1') +MAP.shape = 'H7' +MAP.camera_data = ['D2', 'D5', 'E2', 'E5'] +MAP.camera_data_spawn_point = ['E5'] +MAP.map_data = """ + MB ++ ++ ME Me -- ME ME + MB ME ++ ME __ ME -- -- + -- ME Me -- -- -- ME ++ + -- __ -- Me ME ME Me ++ + -- ++ MM -- ME Me -- -- + -- ME ME -- -- -- Me ME + MB ME -- ++ ++ SP SP ME +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 2, 'mystery': 1}, + {'battle': 1, 'enemy': 2}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1}, + {'battle': 5}, + {'battle': 6, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, \ +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 = ['0'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = False + MAP_HAS_FLEET_STEP = False + MAP_HAS_AMBUSH = True + MAP_HAS_MYSTERY = True + # ===== 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_6(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/campaign_main/campaign_14_2.py b/campaign/campaign_main/campaign_14_2.py new file mode 100644 index 000000000..e4186ad4f --- /dev/null +++ b/campaign/campaign_main/campaign_14_2.py @@ -0,0 +1,74 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .14-1 import Config as ConfigBase + +MAP = CampaignMap('14-2') +MAP.shape = 'I8' +MAP.camera_data = ['D2', 'D6', 'F2', 'F6'] +MAP.camera_data_spawn_point = ['F2'] +MAP.map_data = """ + MB MB ++ -- ME -- ME ++ ++ + -- ME ++ ME -- Me -- SP SP + -- -- Me Me Me -- -- ME ME + ME __ -- Me -- ME -- -- ME + MM ME -- -- Me -- -- ME ++ + ++ ++ ++ -- ++ -- ME ME ME + Me MB ME __ -- -- Me MM ME + ME -- -- -- ME ME ++ ++ ME +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 3, 'mystery': 1}, + {'battle': 1, 'enemy': 2}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1}, + {'battle': 5}, + {'battle': 6, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['0'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = False + MAP_HAS_FLEET_STEP = False + MAP_HAS_AMBUSH = True + MAP_HAS_MYSTERY = True + # ===== 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_6(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/campaign_main/campaign_14_3.py b/campaign/campaign_main/campaign_14_3.py new file mode 100644 index 000000000..fb0b22064 --- /dev/null +++ b/campaign/campaign_main/campaign_14_3.py @@ -0,0 +1,74 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .14-1 import Config as ConfigBase + +MAP = CampaignMap('14-3') +MAP.shape = 'J8' +MAP.camera_data = ['D2', 'D6', 'G2', 'G6'] +MAP.camera_data_spawn_point = ['G2'] +MAP.map_data = """ + ME ++ ++ ++ -- ME ++ ++ ME ME + -- Me ME -- ME -- ++ ++ ME -- + MB -- ME ME -- -- SP SP ME Me + -- __ -- ++ -- ME Me -- ME -- + MB -- Me MM -- ME -- Me ++ ++ + ME -- Me ME -- Me ME -- ME ++ + ++ -- ME __ ME Me ++ Me ME -- + MB -- MB MB -- ++ ++ -- ME -- +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'enemy': 3, 'mystery': 1}, + {'battle': 1, 'enemy': 3}, + {'battle': 2, 'enemy': 2}, + {'battle': 3, 'enemy': 1}, + {'battle': 4, 'enemy': 1}, + {'battle': 5}, + {'battle': 6, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, J6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, J7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, J8, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['0'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = False + MAP_HAS_FLEET_STEP = False + MAP_HAS_AMBUSH = True + MAP_HAS_MYSTERY = True + # ===== 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_6(self): + return self.fleet_boss.clear_boss() diff --git a/campaign/campaign_main/campaign_14_4.py b/campaign/campaign_main/campaign_14_4.py new file mode 100644 index 000000000..24503e240 --- /dev/null +++ b/campaign/campaign_main/campaign_14_4.py @@ -0,0 +1,78 @@ +from module.campaign.campaign_base import CampaignBase +from module.map.map_base import CampaignMap +from module.map.map_grids import SelectedGrids, RoadGrids +from module.logger import logger +from .14-1 import Config as ConfigBase + +MAP = CampaignMap('14-4') +MAP.shape = 'K9' +MAP.camera_data = ['D2', 'D6', 'D7', 'H2', 'H6', 'H7'] +MAP.camera_data_spawn_point = ['H2'] +MAP.map_data = """ + ME -- ++ ++ -- ME ME ME ++ ++ ++ + -- ME ME ME ME ME ME -- SP SP -- + MB -- __ -- -- -- -- -- ME -- -- + MB ME -- Me Me -- Me ++ ++ -- ME + MM -- Me ME -- Me -- MA ++ -- ME + ++ ME ME -- ++ -- ME -- ME -- -- + ++ -- ME Me Me ME -- -- -- -- ++ + -- -- -- -- -- __ -- ME ME -- ME + -- -- ++ MB MB ++ ++ MM ME ME ME +""" +MAP.weight_data = """ + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 + 50 50 50 50 50 50 50 50 50 50 50 +""" +MAP.spawn_data = [ + {'battle': 0, 'mystery': 2}, + {'battle': 1}, + {'battle': 2}, + {'battle': 3}, + {'battle': 4}, + {'battle': 5}, + {'battle': 6}, + {'battle': 7, 'boss': 1}, +] +A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, \ +A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, \ +A3, B3, C3, D3, E3, F3, G3, H3, I3, J3, K3, \ +A4, B4, C4, D4, E4, F4, G4, H4, I4, J4, K4, \ +A5, B5, C5, D5, E5, F5, G5, H5, I5, J5, K5, \ +A6, B6, C6, D6, E6, F6, G6, H6, I6, J6, K6, \ +A7, B7, C7, D7, E7, F7, G7, H7, I7, J7, K7, \ +A8, B8, C8, D8, E8, F8, G8, H8, I8, J8, K8, \ +A9, B9, C9, D9, E9, F9, G9, H9, I9, J9, K9, \ + = MAP.flatten() + + +class Config(ConfigBase): + # ===== Start of generated config ===== + MAP_SIREN_TEMPLATE = ['0'] + MOVABLE_ENEMY_TURN = (2,) + MAP_HAS_SIREN = True + MAP_HAS_MOVABLE_ENEMY = True + MAP_HAS_MAP_STORY = False + MAP_HAS_FLEET_STEP = False + MAP_HAS_AMBUSH = True + MAP_HAS_MYSTERY = True + # ===== 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_7(self): + return self.fleet_boss.clear_boss() diff --git a/dev_tools/map_extractor.py b/dev_tools/map_extractor.py index 75e9a273e..809939472 100644 --- a/dev_tools/map_extractor.py +++ b/dev_tools/map_extractor.py @@ -4,6 +4,8 @@ import re from dev_tools.slpp import slpp from module.base.utils import location2node from module.map.utils import * +from dev_tools.utils import LuaLoader +from module.logger import logger """ This an auto-tool to extract map files used in Alas. @@ -523,10 +525,9 @@ class ChapterTemplate: """ This an auto-tool to extract map files used in Alas. -Git clone https://github.com/Dimbreath/AzurLaneData, to get the decrypted scripts. +Git clone https://github.com/AzurLaneTools/AzurLaneLuaScripts, to get the decrypted scripts. Arguments: - FILE: Folder contains `chapter_template.lua` and `expedition_data_template.lua`, - Such as '//sharecfg' + FILE: Path to your AzurLaneLuaScripts directory FOLDER: Folder to save, './campaign/test' KEYWORD: A keyword in map name, such as '短兵相接' (7-2, zh-CN), 'Counterattack!' (3-4, en-US) Or map id, such as 702 (7-2), 1140017 (Iris of Light and Dark D2) @@ -543,11 +544,12 @@ SELECT = False OVERWRITE = True IS_WAR_ARCHIVES = False -DATA = load_lua_by_function(FILE, 'chapter_template.lua') -DATA_LOOP = load_lua(FILE, 'chapter_template_loop.lua', prefix=41) -MAP_EVENT_LIST = load_lua(FILE, 'map_event_list.lua', prefix=34) -MAP_EVENT_TEMPLATE = load_lua(FILE, 'map_event_template.lua', prefix=38) -EXPECTATION_DATA = load_lua_by_function(FILE, 'expedition_data_template.lua') +LOADER = LuaLoader(FILE, server='CN') +DATA = LOADER.load('./sharecfg/chapter_template.lua') +DATA_LOOP = LOADER.load('./sharecfg/chapter_template_loop.lua') +MAP_EVENT_LIST = LOADER.load('./sharecfg/map_event_list.lua') +MAP_EVENT_TEMPLATE = LOADER.load('./sharecfg/map_event_template.lua') +EXPECTATION_DATA = LOADER.load('./sharecfgdata/expedition_data_template.lua') ct = ChapterTemplate() ct.extract(ct.get_chapter_by_name(KEYWORD, select=SELECT), folder=FOLDER) diff --git a/dev_tools/utils.py b/dev_tools/utils.py new file mode 100644 index 000000000..a60308952 --- /dev/null +++ b/dev_tools/utils.py @@ -0,0 +1,107 @@ +import os +import re + +from tqdm import tqdm +from dev_tools.slpp import slpp + + +class LuaLoader: + """ + Load decrypted scripts + """ + + def __init__(self, folder, server='zh-CN'): + self.folder = folder + self.server = server + + def filepath(self, path): + return os.path.join(self.folder, self.server, path) + + def _load_file(self, file): + """ + Args: + file (str): + + Returns: + dict: + """ + with open(self.filepath(file), 'r', encoding='utf-8') as f: + text = f.read() + + result = {} + matched = re.findall('function \(\)(.*?)end[()]', text, re.S) + if matched: + # Most files are in this format + """ + pg = pg or {} + slot0 = pg + slot0.chapter_template = {} + + (function () + ... + end)() + """ + for func in matched: + add = slpp.decode('{' + func + '}') + result.update(add) + elif text.startswith('pg'): + # Old format + """ + pg = pg or {} + pg.item_data_statistics = { + ... + } + """ + # or + """ + pg = pg or {} + + rawset(pg, "item_data_statistics", rawget(pg, "item_data_statistics") or { + ... + } + """ + text = '{' + text.split('{', 2)[2] + result = slpp.decode(text) + else: + # Another format, just bare data + """ + _G.pg.expedition_data_template[...] = { + ... + } + _G.pg.expedition_data_template[...] = { + ... + } + ... + """ + text = '{' + text + '}' + result = slpp.decode(text) + + return result + + def load(self, path): + """ + Load a lua file to python dictionary, handling the differences + + Args: + path (str): Relavice path from {folder}/{server}. + Can be a file or a directory + + Returns: + dict: + """ + print(f'Loading {path}') + if os.path.isdir(self.filepath(path)): + result = {} + for file in tqdm(os.listdir(self.filepath(path))): + result.update(self._load_file(f'./{path}/{file}')) + else: + result = self._load_file(path) + + print(f'{len(result.keys())} items loaded') + return result + + +if __name__ == '__main__': + # Use example + lua = LuaLoader(r'xxx/AzurLaneData', server='en-US') + res = lua.load('./sharecfg/item_data_statistics.lua') diff --git a/module/campaign/campaign_ocr.py b/module/campaign/campaign_ocr.py index acb705ce7..d9278a1e5 100644 --- a/module/campaign/campaign_ocr.py +++ b/module/campaign/campaign_ocr.py @@ -142,7 +142,7 @@ class CampaignOcr(ModuleBase): x_skip = 10 interval = 5 x_color = np.convolve(np.mean(image, axis=0), np.ones(interval), 'valid') / interval - x_list = np.where(x_color[x_skip:] > 235)[0] + x_list = np.where(x_color[x_skip:] > 240)[0] if x_list is None or len(x_list) == 0: logger.warning('No interval between digit and text.') area = (0, 0, image.shape[1], image.shape[0])