Example #1
0
    def on_btn_import_code_clicked(self, *args):
        md = SkyTempleMessageDialog(
            MainController.window(),
            Gtk.DialogFlags.DESTROY_WITH_PARENT,
            Gtk.MessageType.INFO,
            Gtk.ButtonsType.OK,
            _("Import any move effect ASM code. It must follow the rules of a valid move effect code.\n"
              "WARNING: SkyTemple does not check if the code is correct!\n"
              "Also, make sure the code was assembled for the version you are using. "
              ),
            title=_("Import Move Effect ASM Code"))
        md.run()
        md.destroy()
        dialog = Gtk.FileChooserNative.new(_("Import Move Effect ASM Code..."),
                                           MainController.window(),
                                           Gtk.FileChooserAction.OPEN, None,
                                           None)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                with open(fn, 'rb') as file:
                    self.move_effects.set_effect_code(
                        self._get_current_effect(), file.read())
                self.module.mark_move_effects_as_modified()
            except Exception as err:
                display_error(sys.exc_info(), str(err),
                              _("Error importing ASM code."))
Example #2
0
    def _on_cat_item_name_changed(self, store_name: str, path, text: str):
        store: Gtk.Store = self.builder.get_object(store_name)
        match = PATTERN_MD_ENTRY.match(text)
        if match is None:
            return

        item_ids_already_in = []
        for row in store:
            item_ids_already_in.append(int(row[0]))

        try:
            entid = int(match.group(1))
        except ValueError:
            return

        if entid not in self.categories_for_stores[store_name].item_ids():
            display_error(
                None,
                'This item does not belong in this category. Please chose another item.',
                'Invalid item id')
            return

        if entid in item_ids_already_in:
            display_error(None, 'This item is already in the list.',
                          'Can not use this item')
            return

        store[path][0] = entid
        store[path][1] = self._item_names[entid]
        self._save_item_spawn_rates()
Example #3
0
    def _import_tiles(self, layer):
        dialog: Gtk.Dialog = self.parent.builder.get_object(
            'dialog_tiles_import')
        dialog.set_attached_to(MainController.window())
        dialog.set_transient_for(MainController.window())

        # Set dialog settings to map settings
        tiles_import_file: Gtk.FileChooserButton = self.parent.builder.get_object(
            'tiles_import_file')
        tiles_import_file.unselect_all()

        resp = dialog.run()
        dialog.hide()

        if resp == ResponseType.OK:
            try:
                if tiles_import_file.get_filename() is None:
                    md = SkyTempleMessageDialog(
                        MainController.window(),
                        Gtk.DialogFlags.DESTROY_WITH_PARENT,
                        Gtk.MessageType.ERROR, Gtk.ButtonsType.OK,
                        _("An image must be selected."))
                    md.set_position(Gtk.WindowPosition.CENTER)
                    md.run()
                    md.destroy()
                else:
                    with open(tiles_import_file.get_filename(), 'rb') as f:
                        self.parent.bpc.pil_to_tiles(layer, Image.open(f))
            except Exception as err:
                display_error(sys.exc_info(), str(err))
            self.parent.reload_all()
            self.parent.mark_as_modified()
Example #4
0
    def on_btn_apply_size_clicked(self, *args):
        try:
            width = int(self.builder.get_object('settings_width').get_text())
            height = int(self.builder.get_object('settings_height').get_text())
            if width == 0 or height == 0: # 0x0 rooms are allowed to be consistent with the fact that they exist
                width = height = 0
            assert width >= 0 and height >= 0
        except (ValueError, AssertionError):
            display_error(
                sys.exc_info(),
                "Width and height must be numbers >= 0.",
                "Invalid values."
            )
            return

        confirm = True
        if width < self.floor.width or height < self.floor.height:
            md = SkyTempleMessageDialog(
                MainController.window(),
                Gtk.DialogFlags.MODAL,
                Gtk.MessageType.WARNING,
                Gtk.ButtonsType.YES_NO,
                f"You are about to reduce the size of the room. This will delete tiles. Do you want to continue?",
                title="Warning!"
            )
            response = md.run()
            md.destroy()
            confirm = response == Gtk.ResponseType.YES
        if confirm:
            self.floor.resize(width, height)
            self.module.mark_fixed_floor_as_modified(self.floor_id)
            MainController.reload_view()
Example #5
0
    def on_import_icon_clicked(self, *args):
        dialog = FileChooserNative.new(
            _("Import game icon from PNG..."),
            SkyTempleMainController.window(),
            FileChooserAction.OPEN,
            None, None
        )

        add_dialog_png_filter(dialog)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == ResponseType.ACCEPT:
            try:
                self.icon_banner.icon.from_pil(Image.open(fn))
            except Exception as err:
                display_error(
                    sys.exc_info(),
                    _('Failed importing game icon:\n') + str(err),
                    _("Could not import.")
                )
            self.icon_surface = pil_to_cairo_surface(self.icon_banner.icon.to_pil().convert('RGBA'))
            self.builder.get_object('draw_icon').queue_draw()
            # Mark as modified
            self.module.mark_as_modified()
Example #6
0
    def on_btn_remove_clicked(self, *args):
        dialog = self._setup_dialog()
        self.builder.get_object('lbl_add_remove_title').set_text(
            _("Remove Key: "))
        self.builder.get_object('lbl_add_remove_desc').set_text(
            _("""Remove the item key id selected.
This means all keys with id > that one will be decremented by 1.
A key can't be removed if it's still used by one item. """))
        resp = dialog.run()
        dialog.hide()
        if resp == Gtk.ResponseType.OK:
            key = int(self.builder.get_object('id_key_add_remove').get_text())
            tree_store: Gtk.ListStore = self.builder.get_object('tree_store')
            no_key = []
            for l in tree_store:
                if key == l[0]:
                    no_key.append(l[2])
            if len(no_key) > 0:
                display_error(
                    sys.exc_info(),
                    _(f"This key is still used by these items: {', '.join(no_key)}."
                      ), _("Error removing key."))
            else:
                pre_new_list = []
                for l in tree_store:
                    if l[0] > key:
                        pre_new_list.append((l[2], l[0] - 1))
                    else:
                        pre_new_list.append((l[2], l[0]))
                pre_new_list.sort()
                new_list = [x[1] for x in pre_new_list]
                self.module.set_i2n(self._current_lang, new_list)
                self._refresh_list()
Example #7
0
    def import_a_sprite__gfxcrunch(self) -> Optional[bytes]:
        md = SkyTempleMessageDialog(
            MainController.window(),
            Gtk.DialogFlags.DESTROY_WITH_PARENT,
            Gtk.MessageType.INFO,
            Gtk.ButtonsType.OK,
            "To import select the directory of the sprite export. If it "
            "is still zipped, unzip it first.",
            title="SkyTemple")
        md.run()
        md.destroy()

        dialog = Gtk.FileChooserNative.new("Import gfxcrunch sprite...",
                                           MainController.window(),
                                           Gtk.FileChooserAction.SELECT_FOLDER,
                                           None, None)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                return self.get_gfxcrunch().import_sprite(fn)
            except BaseException as e:
                display_error(sys.exc_info(), str(e),
                              "Error importing the sprite.")
                return None
Example #8
0
    def on_men_map_import_activate(self):
        dialog: Gtk.Dialog = self.parent.builder.get_object(
            'dialog_map_import')
        dialog.set_attached_to(MainController.window())
        dialog.set_transient_for(MainController.window())

        # Set dialog settings to map settings
        bg_import_file: Gtk.FileChooserButton = self.parent.builder.get_object(
            'map_import_layer1_file')
        bg_import_file.unselect_all()

        resp = dialog.run()
        dialog.hide()

        if resp == ResponseType.OK:
            try:
                img_path = bg_import_file.get_filename()
                if TYPE_CHECKING:
                    from skytemple.module.dungeon_graphics.controller.dungeon_bg import DungeonBgController
                    assert isinstance(self.parent, DungeonBgController)
                self.parent.dbg.from_pil(self.parent.dpc,
                                         self.parent.dpci, self.parent.dpl,
                                         Image.open(img_path), True)
            except Exception as err:
                display_error(sys.exc_info(), str(err))
            self.parent.reload_all()
            self.parent.mark_as_modified()
Example #9
0
    def on_item_categories_add_clicked(self, *args):
        store: Gtk.Store = self.builder.get_object('item_categories_store')
        dialog: Gtk.Dialog = self.builder.get_object('dialog_category_add')
        dialog.set_attached_to(MainController.window())
        dialog.set_transient_for(MainController.window())

        # Init available categories
        cb_store: Gtk.ListStore = self.builder.get_object('category_add_store')
        cb: Gtk.ComboBoxText = self.builder.get_object('category_add_cb')
        available_categories = self._fill_available_categories_into_store(
            cb_store)
        # Show error if no categories available
        if len(available_categories) < 1:
            display_error(None, 'All categories are already in the list.',
                          'Can not add category')
            return
        cb.set_active_iter(cb_store.get_iter_first())

        resp = dialog.run()
        dialog.hide()
        if resp == Gtk.ResponseType.APPLY:
            row = cb_store[cb.get_active_iter()]
            store.append([row[0], row[1], False, "0%", "0"])
            self._save_item_spawn_rates()
            self._update_cr_item_cat_name_store()
Example #10
0
    def on_spritebot_import_activate(self, *args):
        dialog = Gtk.FileChooserNative.new(
            _("Import portraits from PNG sheet..."), MainController.window(),
            Gtk.FileChooserAction.OPEN, None, None)

        add_dialog_png_filter(dialog)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                for subindex, image in SpriteBotSheet.load(
                        fn, self._get_portrait_name):
                    try:
                        self.kao.set_from_img(self.item_id, subindex, image)
                    except Exception as err:
                        name = self._get_portrait_name(subindex)
                        logger.error(f"Failed importing image '{name}'.",
                                     exc_info=err)
                        display_error(
                            sys.exc_info(),
                            f(_('Failed importing image "{name}":\n{err}')),
                            f(_("Error for '{name}'.")))
            except Exception as err:
                logger.error(f"Failed importing portraits sheet: {err}",
                             exc_info=err)
                display_error(sys.exc_info(),
                              f(_('Failed importing portraits sheet:\n{err}')),
                              _("Could not import."))
            self.re_render()
            # Mark as modified
            self.module.mark_as_modified()
            self._mark_as_modified_cb()
Example #11
0
    def on_btn_add_clicked(self, widget):
        dialog: Gtk.Dialog = self.builder.get_object('dialog_choose_char')

        self.builder.get_object('entry_char_id').set_text(str(0))
        self.builder.get_object('entry_char_id').set_increments(1, 1)
        self.builder.get_object('entry_char_id').set_range(0, 255)

        dialog.set_attached_to(MainController.window())
        dialog.set_transient_for(MainController.window())

        resp = dialog.run()
        dialog.hide()
        if resp == ResponseType.OK:
            cb_store: Gtk.ListStore = self.builder.get_object('table_store')
            cb: Gtk.ComboBoxText = self.builder.get_object('cb_table_select')
            v: int = cb_store[cb.get_active_iter()][0]
            char = int(self.builder.get_object('entry_char_id').get_text())
            try:
                for e in self.entries:
                    if e.get_properties()["char"] == char:
                        raise ValueError(
                            f"Character {char} already exists in the table!")
                entry = self.font.create_entry_for_table(v)
                entry.set_properties({"char": char})
                self.entries.append(entry)

                entry_tree: Gtk.TreeView = self.builder.get_object(
                    'entry_tree')
                store: Gtk.ListStore = entry_tree.get_model()
                self._add_property_row(store, entry)
                self.module.mark_font_as_modified(self.spec)
            except Exception as err:
                display_error(sys.exc_info(), str(err),
                              "Error adding character.")
Example #12
0
    def on_stats_export_clicked(self, *args):
        assert self._level_bin_entry is not None
        dialog = Gtk.FileChooserNative.new(_("Save CSV..."),
                                           MainController.window(),
                                           Gtk.FileChooserAction.SAVE, None,
                                           None)

        self._add_dialog_file_filters(dialog)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            fn = add_extension_if_missing(fn, 'csv')
            try:
                rows: List[List[Any]] = [[
                    CSV_LEVEL, CSV_EXP_POINTS, CSV_HP, CSV_ATK, CSV_SP_ATK,
                    CSV_DEF, CSV_SP_DEF
                ]]
                for i, level in enumerate(self._level_bin_entry.levels):
                    rows.append([
                        i + 1, level.experience_required, level.hp_growth,
                        level.attack_growth, level.special_attack_growth,
                        level.defense_growth, level.special_defense_growth
                    ])
                with open_utf8(fn, 'w', newline='') as file:
                    writer = csv.writer(file)
                    writer.writerows(rows)
            except BaseException as err:
                display_error(sys.exc_info(), str(err),
                              _("Error saving the CSV."))
Example #13
0
    def on_men_tiles_layer1_export_activate(self):
        dialog: Gtk.Dialog = self.parent.builder.get_object(
            'dialog_tiles_export')
        dialog.set_attached_to(MainController.window())
        dialog.set_transient_for(MainController.window())

        resp = dialog.run()
        dialog.hide()
        if resp == Gtk.ResponseType.OK:
            dialog = Gtk.FileChooserNative.new("Export PNG of tiles...",
                                               MainController.window(),
                                               Gtk.FileChooserAction.SAVE,
                                               None, None)

            add_dialog_png_filter(dialog)

            response = dialog.run()
            fn = dialog.get_filename()
            if '.' not in fn:
                fn += '.png'
            dialog.destroy()

            if response == Gtk.ResponseType.ACCEPT:
                try:
                    self.parent.dpci.tiles_to_pil(self.parent.dpl.palettes,
                                                  16).save(fn)
                except BaseException as err:
                    display_error(sys.exc_info(), str(err),
                                  "Error exporting the tileset.")
Example #14
0
    def add_created_with_logo(self):
        """Add a 'Created with SkyTemple' logo to S05P01A."""
        try:
            bma = self.project.open_file_in_rom(f'{MAP_BG_PATH}s05p01a.bma',
                                                FileType.BMA)
            bpc = self.project.open_file_in_rom(f'{MAP_BG_PATH}s05p01a.bpc',
                                                FileType.BPC)
            bpl = self.project.open_file_in_rom(f'{MAP_BG_PATH}s05p01a.bpl',
                                                FileType.BPL)

            AddCreatedWithLogo(bma, bpc, bpl).process()

            self.project.mark_as_modified(f'{MAP_BG_PATH}s05p01a.bma')
            self.project.mark_as_modified(f'{MAP_BG_PATH}s05p01a.bpc')
            self.project.mark_as_modified(f'{MAP_BG_PATH}s05p01a.bpl')
            item_id = -1
            for i, entry in enumerate(self.bgs.level):
                if entry.bma_name == 'S05P01A':
                    item_id = i
                    break
            if item_id != -1:
                row = self._tree_model[self._tree_level_iter[item_id]]
                recursive_up_item_store_mark_as_modified(row)
        except BaseException as err:
            display_error(sys.exc_info(), str(err), "Error adding the logo.")
        else:
            md = SkyTempleMessageDialog(None,
                                        Gtk.DialogFlags.DESTROY_WITH_PARENT,
                                        Gtk.MessageType.INFO,
                                        Gtk.ButtonsType.OK,
                                        "Logo added successfully. Thank you!",
                                        is_success=True)
            md.set_position(Gtk.WindowPosition.CENTER)
            md.run()
            md.destroy()
Example #15
0
    def on_import_clicked(self, *args):
        cb_store: Gtk.ListStore = self.builder.get_object('cb_weather_store')
        cb: Gtk.ComboBoxText = self.builder.get_object('cb_weather')
        v: int = cb_store[cb.get_active_iter()][0]
        
        dialog = Gtk.FileChooserNative.new(
            _("Import current colormap from PNG..."),
            MainController.window(),
            Gtk.FileChooserAction.OPEN,
            None, None
        )

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                img = Image.open(fn, 'r')
                self.colvec.from_pil(v, img)
                self.module.mark_colvec_as_modified()
            except Exception as err:
                display_error(
                    sys.exc_info(),
                    str(err),
                    _("Error importing colormap.")
                )
            self._reinit_image()
Example #16
0
    def on_import_clicked(self, w: Gtk.MenuToolButton):
        dialog = Gtk.FileChooserNative.new(
            _("Import image as indexed PNG..."),
            MainController.window(),
            Gtk.FileChooserAction.OPEN,
            _("_Open"), None
        )

        add_dialog_png_filter(dialog)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                img = Image.open(fn, 'r')
                self.chr.from_pil(img)
            except Exception as err:
                display_error(
                    sys.exc_info(),
                    str(err),
                    _("Error importing chr image.")
                )
            self.module.mark_chr_as_modified(self.filename)
            self._reinit_image()
Example #17
0
    def _on_cat_item_add_clicked(self, store_name: str):
        store: Gtk.ListStore = self.builder.get_object(store_name)

        item_ids_already_in = []
        for row in store:
            item_ids_already_in.append(int(row[0]))

        i = 0
        all_item_ids = self.categories_for_stores[store_name].item_ids()
        while True:
            first_item_id = all_item_ids[i]
            if first_item_id not in item_ids_already_in:
                break
            i += 1
            if i >= len(all_item_ids):
                display_error(None,
                              'All items are already in the list',
                              'Can not add item.',
                              should_report=False)
                return
        item_icon_renderer = ListIconRenderer(5)
        itm, _ = self.module.get_item(first_item_id)
        row_idx = len(store)
        item_icon = item_icon_renderer.load_icon(
            store,
            self.module.project.get_sprite_provider().get_for_item, row_idx,
            row_idx, (itm, ))
        store.append([
            first_item_id, self._item_names[first_item_id], False, "0%", "0",
            item_icon
        ])
        self._save_item_spawn_rates()
Example #18
0
    def _on_cat_item_name_changed(self, store_name: str, path, text: str):
        store: Gtk.Store = self.builder.get_object(store_name)
        match = PATTERN_MD_ENTRY.match(text)
        if match is None:
            return

        item_ids_already_in = []
        for row in store:
            item_ids_already_in.append(int(row[0]))

        try:
            entid = int(match.group(1))
        except ValueError:
            return

        if entid not in self.categories_for_stores[store_name].item_ids():
            display_error(
                None,
                'This item does not belong in this category. Please chose another item.',
                'Invalid item id',
                should_report=False)
            return

        if entid in item_ids_already_in:
            display_error(None,
                          'This item is already in the list.',
                          'Can not use this item',
                          should_report=False)
            return

        store[path][0] = entid
        store[path][1] = self._item_names[entid]
        item_icon_renderer = ListIconRenderer(5)
        itm, _ = self.module.get_item(entid)
        ##################
        ##################
        # DO NOT LOOK
        # this is awful
        liter = store.get_iter_first()
        i = 0
        found = False
        while liter:
            # dear god what is this
            if str(store.get_path(liter)) == path:
                found = True
                break
            liter = store.iter_next(liter)
            i += 1
        row_idx = i
        assert found
        # end of awfulness
        ##################
        ##################
        item_icon = item_icon_renderer.load_icon(
            store,
            self.module.project.get_sprite_provider().get_for_item, row_idx,
            row_idx, (itm, ))
        store[path][5] = item_icon
        self._save_item_spawn_rates()
Example #19
0
 def on_file_opened_error(self, exc_info, exception):
     """Handle errors during file openings."""
     assert current_thread() == main_thread
     logger.error('Error on file open.', exc_info=exception)
     if self._loading_dialog is not None:
         self._loading_dialog.hide()
         self._loading_dialog = None
     display_error(exc_info, str(exception), "Error opening the ROM")
Example #20
0
    def on_file_saved_error(self, exc_info, exception):
        """Handle errors during file saving."""
        logger.error('Error on save open.', exc_info=exception)

        if self._loading_dialog is not None:
            self._loading_dialog.hide()
            self._loading_dialog = None
        display_error(exc_info, str(exception), "Error saving the ROM")
Example #21
0
 def get_monster_sprite_count(self):
     with self.get_monster_bin_ctx() as monster_bin, \
             self.get_attack_bin_ctx() as attack_bin, \
             self.get_ground_bin_ctx() as ground_bin:
         if len(monster_bin) != len(attack_bin) or len(attack_bin) != len(ground_bin):
             display_error(None, _("Error with sprite files: They don't have the same length!"))
             return -1
         return len(monster_bin)
Example #22
0
 def on_cr_name_edited(self, widget, path, text):
     try:
         if len(text) > 10:
             raise ValueError("Object name has more than 10 characters.")
         if max([ord(c) for c in text]) >= 256:
             raise ValueError("Object name has non-ASCII characters.")
         self._list_store[path][1] = text
     except ValueError as err:
         display_error(sys.exc_info(), str(err), _("Invalid object name"))
Example #23
0
 def _error(self, msg, type=Gtk.MessageType.ERROR, exc_info=None):
     if type == Gtk.MessageType.ERROR:
         display_error(exc_info, msg)
     else:
         md = Gtk.MessageDialog(MainAppController.window(),
                                Gtk.DialogFlags.DESTROY_WITH_PARENT, type,
                                Gtk.ButtonsType.OK, msg)
         md.set_position(Gtk.WindowPosition.CENTER)
         md.run()
         md.destroy()
Example #24
0
 def on_btn_remove_effect_clicked(self, *args):
     try:
         effect_id = self._get_current_effect()
         self.sp_effects.del_effect_code(effect_id)
         self._init_combos(min(effect_id, self.sp_effects.nb_effects() - 1))
         self._init_sp_list()
         self.module.mark_sp_effects_as_modified()
     except ValueError as err:
         display_error(sys.exc_info(), str(err),
                       "Cannot delete this effect.")
Example #25
0
    def on_men_map_import_activate(self):
        dialog: Gtk.Dialog = self.parent.builder.get_object(
            'dialog_map_import')
        dialog.set_attached_to(MainController.window())
        dialog.set_transient_for(MainController.window())

        # Set dialog settings to map settings
        map_import_layer1_file: Gtk.FileChooserButton = self.parent.builder.get_object(
            'map_import_layer1_file')
        map_import_layer2_file: Gtk.FileChooserButton = self.parent.builder.get_object(
            'map_import_layer2_file')
        map_import_layer1_file.unselect_all()
        map_import_layer2_file.unselect_all()
        if self.parent.bma.number_of_layers < 2:
            map_import_layer2_file.set_sensitive(False)

        resp = dialog.run()
        dialog.hide()

        if resp == ResponseType.OK:
            try:
                img1_path = map_import_layer1_file.get_filename()
                img2_path = map_import_layer2_file.get_filename()
                palettes_from_lower_layer = self.parent.builder.get_object(
                    'dialog_map_import_palette_config').get_value()
                if self.parent.bma.number_of_layers < 2 and img1_path is not None:
                    with open(img1_path, 'rb') as f:
                        self.parent.bma.from_pil(self.parent.bpc,
                                                 self.parent.bpl,
                                                 Image.open(f), None, True)
                elif img1_path is not None and img2_path is None:
                    with open(img1_path, 'rb') as f1:
                        self.parent.bma.from_pil(self.parent.bpc,
                                                 self.parent.bpl,
                                                 Image.open(f1), None, True)
                elif img1_path is None and img2_path is not None:
                    with open(img2_path, 'rb') as f2:
                        self.parent.bma.from_pil(self.parent.bpc,
                                                 self.parent.bpl, None,
                                                 Image.open(f2), True)
                elif img1_path is not None and img2_path is not None:
                    with open(img1_path, 'rb') as f1:
                        with open(img2_path, 'rb') as f2:
                            self.parent.bma.from_pil(
                                self.parent.bpc,
                                self.parent.bpl,
                                Image.open(f1),
                                Image.open(f2),
                                True,
                                how_many_palettes_lower_layer=int(
                                    palettes_from_lower_layer))
            except Exception as err:
                display_error(sys.exc_info(), str(err))
            self.parent.reload_all()
            self.parent.mark_as_modified()
Example #26
0
    def on_separate_import_activate(self, *args):
        md = SkyTempleMessageDialog(
            MainController.window(),
            Gtk.DialogFlags.DESTROY_WITH_PARENT,
            Gtk.MessageType.INFO,
            Gtk.ButtonsType.OK,
            f(
                _("To import, select a directory to import from. Files with the pattern '{self.item_id + 1}_XX.png'\n"
                  "will be imported, where XX is a number between 0 and 40.")),
            title=_("Import Portraits"))
        md.run()
        md.destroy()
        dialog = Gtk.FileChooserNative.new(_("Import portraits from PNGs..."),
                                           MainController.window(),
                                           Gtk.FileChooserAction.SELECT_FOLDER,
                                           None, None)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            r = re.compile(rf"{self.item_id + 1}_(\d+)\.png", re.IGNORECASE)
            imgs = {
                int(match[1]): name
                for match, name in self._try_match_import(r, os.listdir(fn))
                if match is not None and int(match[1]) <= 40
            }
            for subindex, image_fn in imgs.items():
                try:
                    with open(os.path.join(fn, image_fn), 'rb') as f:
                        image = Image.open(f)
                        kao = self.kao.get(self.item_id, subindex)
                        if kao:
                            # Replace
                            kao.set(image)
                        else:
                            # New
                            self.kao.set(self.item_id, subindex,
                                         KaoImage.new(image))
                except Exception as err:
                    name = self._get_portrait_name(subindex)
                    logger.error(f"Failed importing image '{name}'.",
                                 exc_info=err)
                    display_error(
                        sys.exc_info(),
                        f(_('Failed importing image "{name}":\n{err}')),
                        f(_(f"Error for '{name}'.")))
            # Re-render
            self._portrait_provider.reset()
            for draw in self._draws:
                draw.queue_draw()
            # Mark as modified
            self.module.mark_as_modified()
            self._mark_as_modified_cb()
Example #27
0
 def on_item_categories_remove_clicked(self, *args):
     tree: Gtk.TreeView = self.builder.get_object('item_categories_tree')
     model, treeiter = tree.get_selection().get_selected()
     if len(model) < 2:
         display_error(None, "The last category can not be removed.",
                       "Can't remove category.")
         return
     if model is not None and treeiter is not None:
         model.remove(treeiter)
     self._recalculate_spawn_chances('item_categories_store', 4, 3)
     self._save_item_spawn_rates()
Example #28
0
    def on_import_clicked(self, w: Gtk.MenuToolButton):
        md = SkyTempleMessageDialog(
            MainController.window(),
            Gtk.DialogFlags.DESTROY_WITH_PARENT,
            Gtk.MessageType.INFO,
            Gtk.ButtonsType.OK,
            _("To import, select a folder containing all the files that were created when exporting the font.\n"
              "IMPORTANT: All image files must be indexed PNGs and use the same palette!"
              ),
            title=_("Import Font"))
        md.run()
        md.destroy()
        fdialog = Gtk.FileChooserNative.new(
            _("Import font from folder..."), MainController.window(),
            Gtk.FileChooserAction.SELECT_FOLDER, None, None)

        response = fdialog.run()
        fn = fdialog.get_filename()
        fdialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            assert self.builder
            dialog: Gtk.Dialog = self.builder.get_object('dialog_import')

            self.builder.get_object('nb_entries_import').set_increments(1, 1)
            self.builder.get_object('nb_entries_import').set_range(
                1, MAX_ENTRIES - 1)
            self.builder.get_object('nb_entries_import').set_text(
                str(self.font.get_nb_entries()))  # type: ignore

            dialog.set_attached_to(MainController.window())
            dialog.set_transient_for(MainController.window())

            resp = dialog.run()
            dialog.hide()
            if resp == Gtk.ResponseType.OK:
                try:
                    lst_entries: List[Optional[Image.Image]] = []
                    for i in range(
                            int(
                                self.builder.get_object(
                                    'nb_entries_import').get_text())):
                        path = os.path.join(fn, f'{i:0>4}.png')
                        if os.path.exists(path):
                            lst_entries.append(Image.open(path, 'r'))
                        else:
                            lst_entries.append(None)
                    self.font.set_entries(lst_entries)  # type: ignore
                    self.module.mark_font_as_modified(self.spec)
                except Exception as err:
                    display_error(sys.exc_info(), str(err),
                                  _("Error importing font."))
                self._init_font()
Example #29
0
    def do_import(self, item_id: int, cb=lambda: None):
        is_zip = self._zip_is_active()

        dialog = Gtk.FileChooserNative.new(
            _("Import spritesheet..."), MainController.window(),
            Gtk.FileChooserAction.SELECT_FOLDER
            if not is_zip else Gtk.FileChooserAction.OPEN, None, None)

        if is_zip:
            self._add_zip_filter_to_dialog(dialog)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                wan = FileType.WAN.CHARA.import_sheets(fn) if not is_zip \
                    else FileType.WAN.CHARA.import_sheets_from_zip(fn)
                monster, ground, attack = FileType.WAN.CHARA.split_wan(wan)

                md = SkyTempleMessageDialog(
                    MainController.window(),
                    Gtk.DialogFlags.DESTROY_WITH_PARENT,
                    Gtk.MessageType.INFO,
                    Gtk.ButtonsType.OK,
                    _("The spritesheet was successfully imported."),
                    title=_("Success!"),
                    is_success=True)
                md.run()
                md.destroy()
                self.module.save_monster_monster_sprite(item_id, monster)
                self.module.save_monster_ground_sprite(item_id, ground)
                self.module.save_monster_attack_sprite(item_id, attack)

                # Shadow size
                if not is_zip:
                    tree = ElementTree.parse(os.path.join(
                        fn, 'AnimData.xml')).getroot()
                else:
                    with ZipFile(fn, 'r') as ZipObj:
                        tree = ElementTree.fromstring(
                            ZipObj.read('AnimData.xml'))
                self._set_shadow_size_cb(int(
                    tree.find('ShadowSize').text))  # type: ignore

                cb()
                self._mark_as_modified_cb()
                MainController.reload_view()
            except BaseException as e:
                display_error(sys.exc_info(), str(e),
                              _("Error importing the spritesheet."))
Example #30
0
    def on_btn_import_clicked(self, *args):
        md = SkyTempleMessageDialog(MainController.window(),
                                    Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO,
                                    Gtk.ButtonsType.OK, _("To import select the XML file in the DTEF tileset package. If it "
                                                          "is still zipped, unzip it first."),
                                    title="SkyTemple")
        md.run()
        md.destroy()

        dialog = Gtk.FileChooserNative.new(
            _("Import dungeon tileset..."),
            MainController.window(),
            Gtk.FileChooserAction.OPEN,
            None, None
        )

        filter = Gtk.FileFilter()
        filter.set_name(_("DTEF XML document (*.dtef.xml)"))
        filter.add_pattern("*.dtef.xml")
        dialog.add_filter(filter)
        add_dialog_xml_filter(dialog)

        response = dialog.run()
        fn = dialog.get_filename()
        dialog.destroy()

        if response == Gtk.ResponseType.ACCEPT:
            try:
                dirname = os.path.dirname(fn)
                fn_xml = fn
                fn_var0 = os.path.join(dirname, VAR0_FN)
                fn_var1 = os.path.join(dirname, VAR1_FN)
                fn_var2 = os.path.join(dirname, VAR2_FN)
                dtef_importer = ExplorersDtefImporter(self.dma, self.dpc, self.dpci, self.dpl, self.dpla)
                dtef_importer.do_import(
                    dirname, fn_xml, fn_var0, fn_var1, fn_var2
                )

                md = SkyTempleMessageDialog(MainController.window(),
                                            Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.INFO,
                                            Gtk.ButtonsType.OK, _("The tileset was successfully imported."),
                                            title=_("Success!"), is_success=True)
                md.run()
                md.destroy()
                self.mark_as_modified()
                MainController.reload_view()
            except BaseException as e:
                display_error(
                    sys.exc_info(),
                    str(e),
                    _("Error importing the tileset.")
                )