示例#1
0
 def _string_to_Map(self, string):
     try:
         return get_map(string)
     except (KeyError, ValueError):
         raise InvalidFieldValueError(
             f"Could not interpret input as a Map specifier for field {self.field_nickname}. Try a string like "
             f"'m10_02_00_00'.")
示例#2
0
 def on_map_choice(self, event=None):
     """Check if current text has changed (and warn), then switch to other text."""
     if not self._ignored_unsaved():
         game_map = get_map(self.selected_map_id)
         self.map_choice.var.set(
             f"{game_map.emevd_file_stem} [{game_map.verbose_name}]")
         return
     self.selected_map_id = self.map_choice.var.get().split(' [')[0]
     if self.global_map_choice_func and event is not None:
         self.global_map_choice_func(self.selected_map_id)
     self.text_editor.delete(1.0, 'end')
     self.text_editor.insert(1.0, self.evs_text[self.selected_map_id])
     self.text_editor.mark_set("insert", "1.0")
     self.text_editor.color_syntax()
示例#3
0
 def refresh(self):
     game_maps = [get_map(m) for m in self.evs_file_paths]
     map_options = [
         f"{game_map.emevd_file_stem} [{game_map.verbose_name}]"
         for game_map in game_maps
     ]
     self.map_choice["values"] = map_options
     if map_options:
         self.map_choice.var.set(map_options[0])
         self.selected_map_id = self.map_choice.get().split(' [')[0]
         self.text_editor.delete(1.0, "end")
         self.text_editor.insert(1.0, self.evs_text[self.selected_map_id])
         self.text_editor.mark_set("insert", "1.0")
         self.text_editor.color_syntax()
示例#4
0
 def _export_talk(self, export_directory):
     """Exports all TalkESDBND files to game from individual map folders in '[project]/talk'."""
     if self.game_name == "Dark Souls Prepare to Die Edition":
         game_version = "ptde"
     elif self.game_name == "Dark Souls Remastered":
         game_version = "dsr"
     else:
         raise ValueError("Cannot export talk for non-DS1 game.")
     for map_directory in (self.project_root / "talk").glob("*"):
         if map_directory.name not in [g.name for g in ALL_MAPS]:
             continue  # unexpected folder
         bnd_file_name = get_map(
             map_directory.name).esd_file_stem + ".talkesdbnd"
         talk = TalkESDBND(map_directory, game_version=game_version)
         talk.write(export_directory / bnd_file_name)
示例#5
0
 def _import_talk(self, import_directory):
     if self.game_name == "Dark Souls Prepare to Die Edition":
         game_version = "ptde"
     elif self.game_name == "Dark Souls Remastered":
         game_version = "dsr"
     else:
         raise ValueError("Cannot export talk for non-DS1 game.")
     for talkesdbnd in import_directory.glob(
             f"*.talkesdbnd{'.dcx' if game_version == 'dsr' else ''}"):
         map_id = talkesdbnd.name.split(".talkesdbnd")[0]
         if map_id in ("m12_00_00_01", "m14_02_00_00"):
             continue  # skipped
         try:
             map_name = get_map(map_id).name
         except KeyError:
             _LOGGER.warning(
                 f"Ignoring unexpected `.talkesdbnd` file in Dark Souls files: {talkesdbnd.name}"
             )
             continue
         TalkESDBND(talkesdbnd, game_version=game_version).write_all_esp(
             self.project_root / f"talk/{map_name}")
示例#6
0
 def set_global_map_choice(self, map_id, ignore_tabs=()):
     game_map = get_map(map_id)
     if "maps" not in ignore_tabs:
         if game_map.msb_file_stem is not None:
             self.maps_tab.map_choice.var.set(f"{game_map.msb_file_stem} [{game_map.verbose_name}]")
             self.maps_tab.on_map_choice()
     if "entities" not in ignore_tabs:
         if game_map.msb_file_stem is not None:
             self.entities_tab.map_choice.var.set(f"{game_map.msb_file_stem} [{game_map.verbose_name}]")
             self.entities_tab.on_map_choice()
     if "events" not in ignore_tabs:
         if game_map.emevd_file_stem is not None:
             self.events_tab.map_choice.var.set(f"{game_map.emevd_file_stem} [{game_map.verbose_name})")
             self.events_tab.on_map_choice()
     if "ai" not in ignore_tabs:
         if game_map.ai_file_stem is not None:
             self.ai_tab.map_choice.var.set(f"{game_map.ai_file_stem} [{game_map.verbose_name}]")
             self.ai_tab.on_map_choice()
     if "talk" not in ignore_tabs:
         if game_map.esd_file_stem is not None:
             self.talk_tab.map_choice.var.set(f"{game_map.esd_file_stem} [{game_map.verbose_name}]")
             self.talk_tab.on_map_choice()
示例#7
0
 def refresh(self):
     """Reloads all ESP files from all maps."""
     self.esp_file_paths = {}
     self.esp_text = {}
     for map_directory in self.esp_directory.glob("*"):
         try:
             game_map = get_map(map_directory.name)
         except ValueError:
             _LOGGER.warning(
                 f"Unexpected folder found in project '/talk' directory and ignored: "
                 f"{map_directory.name}")
             continue
         for esp_path in map_directory.glob("*.esp.py"):
             talk_match = _TALK_ESP_MATCH.match(esp_path.name)
             if not talk_match:
                 _LOGGER.warning(
                     f"Invalid ESP file found and ignored: {str(esp_path)}")
                 continue
             talk_id = int(talk_match.group(1))
             self.esp_file_paths.setdefault(game_map.esd_file_stem,
                                            {})[talk_id] = esp_path
             with esp_path.open(mode="r", encoding="utf-8") as f:
                 self.esp_text.setdefault(game_map.esd_file_stem,
                                          {})[talk_id] = f.read()
示例#8
0
 def get_selected_msb(self):
     map_name = get_map(self.map_choice_id).name
     return self.Maps[map_name]
 def __getitem__(self, map_source):
     game_map = get_map(map_source)
     return self._data[game_map.name]
示例#10
0
    def _import_entities_module(self):
        """Reads '{map_id}_entities.py' file and loads names from it into map data.

        Also tries to read descriptions from inline comments with regex. (Messing too much with the formatting in the
        module file may interfere with this.)
        """
        game_map = get_map(self.map_choice_id)
        msb = self.get_selected_msb()
        module_path = self.evs_directory / f"{game_map.emevd_file_stem}_entities.py"
        if not module_path.is_file():
            return self.error_dialog(
                "No Entity Module",
                "Entity module not yet created in project 'events' folder.")
        evs_path = self.evs_directory / f"{game_map.emevd_file_stem}.evs.py"
        if not evs_path.is_file():
            return self.error_dialog(
                "No EVS Script",
                "EVS script not yet imported into project 'events' folder.")
        sys.path.append(str(module_path.parent))

        try:
            entity_module = import_module(module_path.stem)
        except Exception as e:
            return self.error_dialog(
                "Import Error",
                f"Could not import {module_path.name}. Error:\n\n{str(e)}")
        entries_by_entity_enum = {}
        found_map_entry_class_names = []
        not_found = []
        skipped = set()  # will also skip description
        for attr_name, attr in [
                m[0:2]
                for m in inspect.getmembers(entity_module, inspect.isclass)
                if m[1].__module__ == entity_module.__name__
        ]:
            for entry_game_type in ENTITY_GAME_TYPES:
                if entry_game_type in attr.__bases__:
                    break
            else:
                continue  # ignore this class
            found_map_entry_class_names.append(attr_name)
            for entity_enum in attr:
                entry = msb.get_entry_with_entity_id(entity_enum.value,
                                                     allow_multiple=True)
                if entry is None:
                    not_found.append(entity_enum.value)
                    continue
                if isinstance(entry, list):
                    choice = self.CustomDialog(
                        title="Multiple Entries with Same ID",
                        message=
                        f"Entity ID {entity_enum.value} in Python module '{module_path.stem}' appears "
                        f"multiple times in MSB. This will rename only the first one found (type "
                        f"{entry[0].ENTRY_SUBTYPE.name}). Is this OK?",
                        button_kwargs=("YES", "NO", "NO"),
                        button_names=("Yes, change it", "No, skip it",
                                      "No, abort import"),
                        default_output=2,
                        cancel_output=2,
                    )
                    if choice == 2:
                        return
                    elif choice == 1:
                        skipped.add(entity_enum.value)
                        continue
                    else:
                        entry = entry[0]
                entry_type_name = entry.ENTRY_SUBTYPE.get_pluralized_type_name(
                )
                entry_subtype_name = entry.ENTRY_SUBTYPE.name
                if entry_game_type.get_msb_entry_type_subtype() != (
                        entry_type_name, entry_subtype_name):
                    choice = self.CustomDialog(
                        title="Entry Type Mismatch",
                        message=
                        f"Entity ID {entity_enum.value} in Python module '{module_path.stem}' has type "
                        f"'{attr_name}', but has different type '{entry_type_name}.{entry_subtype_name} in MSB. "
                        f"Change name anyway?",
                        button_kwargs=("YES", "NO", "NO"),
                        button_names=("Yes, change it", "No, skip it",
                                      "No, abort import"),
                        default_output=2,
                        cancel_output=2,
                    )
                    if choice == 2:
                        return
                    elif choice == 1:
                        skipped.add(entity_enum.value)
                        continue
                entries_by_entity_enum[entity_enum] = entry
        if not entries_by_entity_enum:
            return self.CustomDialog(
                "No IDs to Update",
                "No IDs in the Python entities module are present in the MSB.")
        if not_found:
            not_found_string = word_wrap(", ".join(not_found), 50)
            if (self.CustomDialog(
                    title="Allow Missing IDs?",
                    message=
                    f"These entity IDs in the Python module could not be found in the MSB:"
                    f"\n\n{not_found_string}"
                    f"\n\nContinue updating {len(entries_by_entity_enum)} other IDs?",
                    button_kwargs=("YES", "NO"),
                    button_names=("Yes, continue", "No, abort import"),
                    default_output=0,
                    cancel_output=1,
            ) == 1):
                return

        # Find descriptions with regular expressions.
        skip_description = skipped.union(not_found)
        current_attr_name = ""
        descriptions_by_attr_and_id = {}
        with module_path.open("r", encoding="utf-8") as module:
            for line in module:
                class_match = _RE_ENTITIES_ENUM_CLASS.match(line)
                if class_match:
                    attr_name = class_match.group(1)
                    if attr_name in found_map_entry_class_names:
                        current_attr_name = attr_name
                    continue
                if current_attr_name:
                    member_match = _RE_ENTITIES_ENUM_MEMBER.match(line)
                    if member_match:
                        entity_id, description = member_match.group(2, 3)
                        if entity_id not in skip_description:
                            descriptions_by_attr_and_id[
                                current_attr_name,
                                int(entity_id)] = description.strip()

        for entity_enum, entry in entries_by_entity_enum.items():
            entry.name = entity_enum.name
        for (attr_name,
             entity_id), description in descriptions_by_attr_and_id.items():
            # attr_name not actually used, as entity ID uniqueness should have already been resolved
            entry = msb.get_entry_with_entity_id(entity_id,
                                                 allow_multiple=True)
            if not entry:
                continue  # shouldn't happen (as missing entity ID should be skipped) but just in case
            if isinstance(entry, list):
                entry = entry[0]  # could happen
            entry.description = description

        self.refresh_entries()

        self.CustomDialog(
            "Import Successful",
            f"Entity names and descriptions imported successfully.")
示例#11
0
 def _get_map(self):
     return get_map(self.map_choice_id)
示例#12
0
 def get_map(self) -> Map:
     return get_map(self.map_choice_id)