From a5a1c752522da06261a15b60a90170b5e185ce9e Mon Sep 17 00:00:00 2001 From: JayTheBusinessGoose Date: Sat, 23 Dec 2023 22:50:12 -0700 Subject: [PATCH] Store tiles with a Tile type instead of as arrays of data. --- .../custom_levels/custom_level_editor.py | 151 ++++++++--------- .../ui/levels/custom_levels/save_level.py | 12 +- .../ui/levels/shared/palette_panel.py | 113 ++++++------- src/modlunky2/ui/levels/shared/tile.py | 12 +- .../multi_room/multi_room_editor_tab.py | 43 +++-- .../vanilla_levels/vanilla_level_editor.py | 156 +++++++++--------- 6 files changed, 231 insertions(+), 256 deletions(-) diff --git a/src/modlunky2/ui/levels/custom_levels/custom_level_editor.py b/src/modlunky2/ui/levels/custom_levels/custom_level_editor.py index 25515550..5e9d44db 100644 --- a/src/modlunky2/ui/levels/custom_levels/custom_level_editor.py +++ b/src/modlunky2/ui/levels/custom_levels/custom_level_editor.py @@ -16,6 +16,7 @@ from modlunky2.ui.levels.shared.biomes import BIOME from modlunky2.ui.levels.shared.files_tree import FilesTree, PACK_LIST_TYPE, LEVEL_TYPE from modlunky2.ui.levels.shared.level_canvas import CANVAS_MODE +from modlunky2.ui.levels.shared.tile import Tile from modlunky2.ui.levels.shared.multi_canvas_container import ( MultiCanvasContainer, CanvasIndex, @@ -299,17 +300,17 @@ def read_lvl_file(self, lvl, theme=None): hard_floor_code = None # Populate the tile palette from the tile codes listed in the level file. for tilecode in level.tile_codes.all(): - tilecode_item = [] - tilecode_item.append(str(tilecode.name) + " " + str(tilecode.value)) - img = self.texture_fetcher.get_texture( tilecode.name, theme, lvl, self.zoom_level ) img_select = self.texture_fetcher.get_texture(tilecode.name, theme, lvl, 40) - tilecode_item.append(ImageTk.PhotoImage(img)) - tilecode_item.append(ImageTk.PhotoImage(img_select)) - + tilecode_item = Tile( + tilecode.name, + tilecode.value, + ImageTk.PhotoImage(img), + ImageTk.PhotoImage(img_select), + ) if tilecode.value in self.usable_codes: self.usable_codes.remove(tilecode.value) self.tile_palette_ref_in_use.append(tilecode_item) @@ -329,8 +330,9 @@ def read_lvl_file(self, lvl, theme=None): else: hard_floor_code = self.usable_codes[0] self.usable_codes.remove(hard_floor_code) - tilecode_item = [ - "floor_hard " + str(hard_floor_code), + tilecode_item = Tile( + "floor_hard", + str(hard_floor_code), ImageTk.PhotoImage( self.texture_fetcher.get_texture( "floor_hard", theme, lvl, self.zoom_level @@ -339,7 +341,7 @@ def read_lvl_file(self, lvl, theme=None): ImageTk.PhotoImage( self.texture_fetcher.get_texture("floor_hard", theme, lvl, 40) ), - ] + ) self.tile_palette_ref_in_use.append(tilecode_item) self.tile_palette_map[hard_floor_code] = tilecode_item @@ -349,28 +351,28 @@ def read_lvl_file(self, lvl, theme=None): if "1" in self.tile_palette_map: # If there is a "1" tile code, guess it is a good default tile since it is often the floor. tile = self.tile_palette_map["1"] - self.palette_panel.select_tile(tile[0], tile[2], True) + self.palette_panel.select_tile(tile, True) elif len(self.tile_palette_ref_in_use) > 0: # If there is no "1" tile, just populate with the first tile. tile = self.tile_palette_ref_in_use[0] - self.palette_panel.select_tile(tile[0], tile[2], True) + self.palette_panel.select_tile(tile, True) secondary_backup_index = 1 # Populate the default tile code for right clicks. if "0" in self.tile_palette_map: # If there is a "0" tile code, guess it is a good default secondary tile since it is often the empty tile. tile = self.tile_palette_map["0"] - self.palette_panel.select_tile(tile[0], tile[2], False) + self.palette_panel.select_tile(tile, False) elif len(self.tile_palette_ref_in_use) > secondary_backup_index: # If there is not a "0" tile code, populate with the second tile code if the # primary tile code was populated from the first one. tile = self.tile_palette_ref_in_use[secondary_backup_index] - self.palette_panel.select_tile(tile[0], tile[2], False) + self.palette_panel.select_tile(tile, False) elif len(self.tile_palette_ref_in_use) > 0: # If there are only one tile code available, populate both right and # left click with it. tile = self.tile_palette_ref_in_use[0] - self.palette_panel.select_tile(tile[0], tile[2], False) + self.palette_panel.select_tile(tile, False) # Populate the list of suggested tiles based on the current theme. self.tile_palette_suggestions = suggested_tiles_for_theme(theme) @@ -546,12 +548,11 @@ def draw_layer(canvas_index, tile_codes): if tile_index >= self.lvl_width * 10: continue tilecode = self.tile_palette_map[tile] - tile_name = tilecode[0].split(" ", 1)[0] - tile_image = tilecode[1] + tile_image = tilecode.image x_offset, y_offset = self.texture_fetcher.adjust_texture_xy( tile_image.width(), tile_image.height(), - tile_name, + tilecode.name, self.zoom_level, ) self.canvas.replace_tile_at( @@ -572,33 +573,33 @@ def draw_layer(canvas_index, tile_codes): # Click event on a canvas for either left or right click to replace the tile at the cursor's position with # the selected tile. def canvas_click(self, canvas_index, row, column, is_primary): - tile_name, tile_code = self.palette_panel.selected_tile(is_primary) - x_offset, y_offset = self.offset_for_tile(tile_name, tile_code, self.zoom_level) + tile = self.palette_panel.selected_tile(is_primary) + x_offset, y_offset = self.offset_for_tile(tile, self.zoom_level) self.canvas.replace_tile_at( canvas_index, row, column, - self.tile_palette_map[tile_code][1], + self.tile_palette_map[tile.code].image, x_offset, y_offset, ) - self.tile_codes[canvas_index.tab_index][row][column] = tile_code + self.tile_codes[canvas_index.tab_index][row][column] = tile.code self.changes_made() def canvas_shiftclick(self, index, row, column, is_primary): tile_code = self.tile_codes[index.tab_index][row][column] tile = self.tile_palette_map[tile_code] - self.palette_panel.select_tile(tile[0], tile[2], is_primary) + self.palette_panel.select_tile(tile, is_primary) def canvas_fill(self, canvas_index, tiles, is_primary): for tile in tiles: self.canvas_click(canvas_index, tile.y, tile.x, is_primary) def canvas_fill_type(self, canvas_index, row, column, is_primary): - tile_name, tile_code = self.palette_panel.selected_tile(is_primary) - x_offset, y_offset = self.offset_for_tile(tile_name, tile_code, self.zoom_level) + tile = self.palette_panel.selected_tile(is_primary) + x_offset, y_offset = self.offset_for_tile(tile, self.zoom_level) replace_code = self.tile_codes[canvas_index.tab_index][row][column] @@ -611,11 +612,11 @@ def canvas_fill_type(self, canvas_index, row, column, is_primary): canvas_index, r, c, - self.tile_palette_map[tile_code][1], + self.tile_palette_map[tile.code].image, x_offset, y_offset, ) - self.tile_codes[canvas_index.tab_index][r][c] = tile_code + self.tile_codes[canvas_index.tab_index][r][c] = tile.code self.changes_made() def canvas_move(self, canvas_index, tiles, dist_x, dist_y): @@ -625,10 +626,8 @@ def canvas_move(self, canvas_index, tiles, dist_x, dist_y): ] empty = None for tile_ref in self.tile_palette_ref_in_use: - tile_name = str(tile_ref[0].split(" ", 2)[0]) - tile_code = str(tile_ref[0].split(" ", 2)[1]) - if tile_name == "empty": - empty = tile_code + if tile_ref.name == "empty": + empty = tile_ref # If we didn't find an "empty" tile code, create one and use it. if not empty: @@ -636,15 +635,11 @@ def canvas_move(self, canvas_index, tiles, dist_x, dist_y): "empty", "100", "empty", - )[0].split( - " ", 2 - )[1] + ) for origin_x, origin_y, tile_code in replacement_tiles: - self.canvas.replace_tile_at( - canvas_index, origin_y, origin_x, self.tile_palette_map[empty][1] - ) - self.tile_codes[canvas_index.tab_index][origin_y][origin_x] = empty + self.canvas.replace_tile_at(canvas_index, origin_y, origin_x, empty.image) + self.tile_codes[canvas_index.tab_index][origin_y][origin_x] = empty.code for origin_x, origin_y, tile_code in replacement_tiles: new_x = origin_x + dist_x @@ -657,15 +652,13 @@ def canvas_move(self, canvas_index, tiles, dist_x, dist_y): ): continue tile = self.tile_palette_map[tile_code] - x_offset, y_offset = self.offset_for_tile( - tile[0].split(" ", 2)[0], tile_code, self.zoom_level - ) + x_offset, y_offset = self.offset_for_tile(tile, self.zoom_level) self.canvas.replace_tile_at( canvas_index, new_y, new_x, - tile[1], + tile.image, x_offset, y_offset, ) @@ -677,17 +670,17 @@ def select_tool(self, tool): self.canvas.set_mode(tool) # Looks up the expected offset type and tile image size and computes the offset of the tile's anchor in the grid. - def offset_for_tile(self, tile_name, tile_code, tile_size): - logger.debug("Applying custom anchor for %s", tile_name) - tile_ref = self.tile_palette_map[tile_code] - if tile_ref: - logger.debug("Found %s", tile_ref[0]) - img = tile_ref[1] - return self.texture_fetcher.adjust_texture_xy( - img.width(), img.height(), tile_name, tile_size - ) + def offset_for_tile(self, tile, tile_size): + if tile is None: + return - return 0, 0 + logger.debug("Applying custom anchor for %s", tile.name) + + logger.debug("Found %s", tile.name) + img = tile.image + return self.texture_fetcher.adjust_texture_xy( + img.width(), img.height(), tile.name, tile_size + ) def add_tilecode(self, tile, percent, alt_tile): usable_code = None @@ -731,7 +724,7 @@ def add_tilecode(self, tile, percent, alt_tile): # compares tile id to tile ids in palette list for palette_tile in self.tile_palette_ref_in_use: - palette_tile = palette_tile[0].split()[0].strip() + palette_tile = palette_tile.name if new_tile_code == palette_tile: tkMessageBox.showinfo("Uh Oh!", "You already have that!") return @@ -747,10 +740,7 @@ def add_tilecode(self, tile, percent, alt_tile): ) return - ref_tile = [] - ref_tile.append(new_tile_code + " " + str(usable_code)) - ref_tile.append(tile_image) - ref_tile.append(tile_image_picker) + ref_tile = Tile(new_tile_code, str(usable_code), tile_image, tile_image_picker) self.tile_palette_ref_in_use.append(ref_tile) self.tile_palette_map[usable_code] = ref_tile @@ -759,8 +749,8 @@ def add_tilecode(self, tile, percent, alt_tile): self.changes_made() return ref_tile - def delete_tilecode(self, tile_name, tile_code): - if tile_name == r"empty": + def delete_tilecode(self, tile): + if tile.name == r"empty": tkMessageBox.showinfo("Uh Oh!", "Can't delete empty!") return False @@ -774,19 +764,22 @@ def delete_tilecode(self, tile_name, tile_code): for matrix_index, tile_code_matrix in enumerate(self.tile_codes): for row in range(len(tile_code_matrix)): for column in range(len(tile_code_matrix[row])): - if str(tile_code_matrix[row][column]) == str(tile_code): + if str(tile_code_matrix[row][column]) == tile.code: self.canvas.replace_tile_at( - CanvasIndex(matrix_index, 0), row, column, new_tile[1] + CanvasIndex(matrix_index, 0), + row, + column, + new_tile.image, ) tile_code_matrix[row][column] = "0" - self.usable_codes.append(str(tile_code)) - logger.debug("%s is now available for use.", tile_code) + self.usable_codes.append(tile.code) + logger.debug("%s is now available for use.", tile.code) # Adds tilecode back to list to be reused. for id_ in self.tile_palette_ref_in_use: - if str(tile_code) == str(id_[0].split(" ", 2)[1]): + if tile.code == id_.code: self.tile_palette_ref_in_use.remove(id_) - logger.debug("Deleted %s", tile_name) + logger.debug("Deleted %s", tile.name) self.populate_tilecode_palette() @@ -812,11 +805,15 @@ def select_theme(self, theme): # Retexture all of the tiles in use for tilecode_item in self.tile_palette_ref_in_use: - tile_name = tilecode_item[0].split(" ", 2)[0] + tile_name = tilecode_item.name img = self.texture_fetcher.get_texture( tile_name, theme, self.lvl, self.zoom_level ) - tilecode_item[1] = ImageTk.PhotoImage(img) + img_select = self.texture_fetcher.get_texture( + tile_name, theme, self.lvl, 40 + ) + tilecode_item.image = ImageTk.PhotoImage(img) + tilecode_item.picker_image = ImageTk.PhotoImage(img_select) # Load suggested tiles for the new theme. self.tile_palette_suggestions = suggested_tiles_for_theme(theme) @@ -860,12 +857,10 @@ def fill_to_size_with_tile(tile_matrix, tile, width, height): # Try to find a tile code for the empty tile and the hard floor to use as the # default tile codes of the front and back layers, respectively. for tile_ref in self.tile_palette_ref_in_use: - tile_name = str(tile_ref[0].split(" ", 2)[0]) - tile_code = str(tile_ref[0].split(" ", 2)[1]) - if tile_name == "empty": - empty = tile_code - elif tile_name == "floor_hard": - hard_floor = tile_code + if tile_ref.name == "empty": + empty = tile_ref.code + elif tile_ref.name == "floor_hard": + hard_floor = tile_ref.code # If we didn't find an "empty" tile code, create one and use it. if not empty: @@ -873,18 +868,14 @@ def fill_to_size_with_tile(tile_matrix, tile, width, height): "empty", "100", "empty", - )[0].split( - " ", 2 - )[1] + ).code # If we did not find a "hard_floor" tile code, create one and use it. if not hard_floor: hard_floor = self.add_tilecode( "hard_floor", "100", "empty", - )[0].split( - " ", 2 - )[1] + ).code self.tile_codes = [ fill_to_size_with_tile(self.tile_codes[LAYER.FRONT], empty, width, height), @@ -902,8 +893,8 @@ def update_zoom(self, zoom): self.zoom_level = zoom if self.lvl: for tile in self.tile_palette_ref_in_use: - tile_name = tile[0].split(" ", 2)[0] - tile[1] = ImageTk.PhotoImage( + tile_name = tile.name + tile.image = ImageTk.PhotoImage( self.texture_fetcher.get_texture( tile_name, self.lvl_biome, diff --git a/src/modlunky2/ui/levels/custom_levels/save_level.py b/src/modlunky2/ui/levels/custom_levels/save_level.py index 8c0e8aa2..b6fce3a7 100644 --- a/src/modlunky2/ui/levels/custom_levels/save_level.py +++ b/src/modlunky2/ui/levels/custom_levels/save_level.py @@ -80,15 +80,15 @@ def save_level( for tilecode in used_tiles: tile_codes.set_obj( TileCode( - name=tilecode[0].split(" ", 1)[0], - value=tilecode[0].split(" ", 1)[1], + name=tilecode.name, + value=tilecode.code, comment="", ) ) - if tilecode[0].split(" ", 1)[0] == "floor_hard": - hard_floor_code = tilecode[0].split(" ", 1)[1] - elif tilecode[0].split(" ", 1)[0] == "empty": - air_code = tilecode[0].split(" ", 1)[1] + if tilecode.name == "floor_hard": + hard_floor_code = tilecode.code + elif tilecode.name == "empty": + air_code = tilecode.code def write_vanilla_room( x, diff --git a/src/modlunky2/ui/levels/shared/palette_panel.py b/src/modlunky2/ui/levels/shared/palette_panel.py index 0cc2720d..9937a503 100644 --- a/src/modlunky2/ui/levels/shared/palette_panel.py +++ b/src/modlunky2/ui/levels/shared/palette_panel.py @@ -3,6 +3,7 @@ from PIL import Image, ImageDraw, ImageEnhance, ImageTk from modlunky2.levels.tile_codes import VALID_TILE_CODES +from modlunky2.ui.levels.shared.tile import Tile from modlunky2.ui.widgets import PopupWindow, ScrollableFrameLegacy, Tab @@ -98,7 +99,6 @@ def __init__( self.sprite_fetcher = sprite_fetcher self.title_prefix = title_prefix - self.name = None self.delete_button = tk.Button( self, @@ -106,19 +106,21 @@ def __init__( bg="red", fg="white", width=10, - command=lambda: on_delete(self.tile_name(), self.tile_code()), + command=lambda: on_delete(self.tile), ) # Shows selected tile. Important because this is used for more than just user # convenience; we can grab the currently used tile here self.title_label = ttk.Label(self, text=title_prefix + " empty 0") - self.img = None + self.tile = None self.img_empty = ImageTk.PhotoImage( sprite_fetcher.get("empty").resize((40, 40), Image.Resampling.LANCZOS) ) self.img_view = ttk.Label(self, image=self.img_empty, width=50) + self.img = None + self.columnconfigure(0, minsize=8) self.columnconfigure(2, weight=1) self.columnconfigure(4, minsize=10) @@ -128,23 +130,26 @@ def __init__( self.reset() - def select_tile(self, name, image): - self.title_label["text"] = self.title_prefix + " " + name - if image: - self.img_view["image"] = image + def select_tile(self, tile): + self.tile = tile + self.refresh() + + def refresh(self): + tile = self.tile + if tile is None: + self.title_label["text"] = self.title_prefix + else: + self.title_label["text"] = ( + self.title_prefix + " " + tile.name + " " + tile.code + ) + if tile is not None and tile.image: + self.img_view["image"] = tile.picker_image + self.img = tile.picker_image else: self.img_view["image"] = self.img_empty - self.img = image - self.name = name - - def tile_name(self): - return self.name.split(" ", 1)[0] - - def tile_code(self): - return self.name.split(" ", 1)[1] def reset(self, disable=True): - self.select_tile("empty 0", None) + self.select_tile(Tile("empty", "0", self.img_empty, self.img_empty)) if disable: self.disable() @@ -197,12 +202,12 @@ def __init__( self.palette.grid(row=2, column=0, sticky="swne") self.new_tile_panel.grid(row=3, column=0, sticky="swne") - def delete_tilecode(self, tile_name, tile_code): - deleted = self.on_delete_tilecode(tile_name, tile_code) + def delete_tilecode(self, tile): + deleted = self.on_delete_tilecode(tile) if deleted: - if self.primary_tile_view.tile_code() == tile_code: + if self.primary_tile_view.tile.code == tile.code: self.primary_tile_view.reset(disable=False) - if self.secondary_tile_view.tile_code() == tile_code: + if self.secondary_tile_view.tile.code == tile.code: self.secondary_tile_view.reset(disable=False) def update_with_palette( @@ -224,30 +229,24 @@ def update_with_palette( count_col = 0 count_row = count_row + 1 - tile_name = tile_keep[0].split(" ", 2)[0] - used_tile_names.append(tile_name) + used_tile_names.append(tile_keep.name) - self.tile_images.append(tile_keep[2]) + self.tile_images.append(tile_keep.picker_image) new_tile = tk.Button( self.palette.scrollable_frame, - text=tile_keep[0], + text=tile_keep.name, width=40, height=40, - # image=tile_image, - image=tile_keep[2], + image=tile_keep.picker_image, ) new_tile.grid(row=count_row, column=count_col) new_tile.bind( "", - lambda event, r=count_row, c=count_col: self.tile_pick( - event, r, c, True - ), + lambda event, t=tile_keep: self.tile_pick(event, t, True), ) new_tile.bind( "", - lambda event, r=count_row, c=count_col: self.tile_pick( - event, r, c, False - ), + lambda event, t=tile_keep: self.tile_pick(event, t, False), ) # Bind first ten tiles to number keys @@ -255,15 +254,11 @@ def update_with_palette( if tile_index <= 10: self.bind_all( f"{tile_index%10}", - lambda event, r=count_row, c=count_col: self.tile_pick( - event, r, c, True - ), + lambda event, t=tile_keep: self.tile_pick(event, t, True), ) self.bind_all( f"", - lambda event, r=count_row, c=count_col: self.tile_pick( - event, r, c, False - ), + lambda event, t=tile_keep: self.tile_pick(event, t, False), ) if suggestions and len(suggestions): @@ -300,15 +295,11 @@ def update_with_palette( new_tile.grid(row=count_row, column=count_col) new_tile.bind( "", - lambda event, ts=suggestion, ti=tile_image: self.suggested_tile_pick( - event, ts, ti - ), + lambda event, ts=suggestion: self.suggested_tile_pick(event, ts), ) new_tile.bind( "", - lambda event, ts=suggestion, ti=tile_image: self.suggested_tile_pick( - event, ts, ti - ), + lambda event, ts=suggestion: self.suggested_tile_pick(event, ts), ) if dependency_tiles and len(dependency_tiles): @@ -332,12 +323,12 @@ def update_with_palette( count_col = 0 count_row += 1 - tile_image = tile[2] + tile_image = tile.picker_image self.tile_images.append(tile_image) new_tile = tk.Button( self.palette.scrollable_frame, - text=tile[0], + text=tile.name, width=40, height=40, image=tile_image, @@ -358,37 +349,34 @@ def update_with_palette( self.primary_tile_view.enable() self.secondary_tile_view.enable() + self.primary_tile_view.refresh() + self.secondary_tile_view.refresh() self.new_tile_panel.reset() self.new_tile_panel.enable() - def tile_pick(self, event, row, col, is_primary): - if not self.palette.scrollable_frame.grid_slaves(row, col): - return - selected_tile = self.palette.scrollable_frame.grid_slaves(row, col)[0] - self.select_tile( - selected_tile["text"], selected_tile["image"], is_primary, True - ) + def tile_pick(self, event, tile, is_primary): + self.select_tile(tile, is_primary, True) - def suggested_tile_pick(self, event, suggested_tile, tile_image): + def suggested_tile_pick(self, event, suggested_tile): tile = self.on_add_tilecode(suggested_tile, 100, "empty") if not tile: return - self.select_tile(tile[0], tile_image, event.num == 1, True) + self.select_tile(tile, event.num == 1, True) def dependency_tile_pick(self, event, tile, dependency): if self.on_use_dependency_tile: self.on_use_dependency_tile(tile, dependency) - self.select_tile(tile[0], tile[2], event.num == 1, True) + self.select_tile(tile, event.num == 1, True) - def select_tile(self, tile_name, tile_image, is_primary, tell_delegate=False): + def select_tile(self, tile, is_primary, tell_delegate=False): tile_view = self.primary_tile_view if not is_primary: tile_view = self.secondary_tile_view - tile_view.select_tile(tile_name, tile_image) + tile_view.select_tile(tile) if tell_delegate and self.on_select_tile: - self.on_select_tile(tile_name, tile_image, is_primary) + self.on_select_tile(tile, is_primary) def reset(self): for widget in self.palette.scrollable_frame.winfo_children(): @@ -402,10 +390,7 @@ def selected_tile(self, is_primary): return self.primary_tile() if is_primary else self.secondary_tile() def primary_tile(self): - return self.primary_tile_view.tile_name(), self.primary_tile_view.tile_code() + return self.primary_tile_view.tile def secondary_tile(self): - return ( - self.secondary_tile_view.tile_name(), - self.secondary_tile_view.tile_code(), - ) + return self.secondary_tile_view.tile diff --git a/src/modlunky2/ui/levels/shared/tile.py b/src/modlunky2/ui/levels/shared/tile.py index 4e91569d..a1f47c3a 100644 --- a/src/modlunky2/ui/levels/shared/tile.py +++ b/src/modlunky2/ui/levels/shared/tile.py @@ -1,8 +1,14 @@ from dataclasses import dataclass -from typing import List, Optional +from typing import List +from PIL import ImageTk -from modlunky2.levels.level_templates import TemplateSetting -from modlunky2.ui.levels.shared.setrooms import MatchedSetroom + +@dataclass +class Tile: + name: str + code: str + image: ImageTk.PhotoImage + picker_image: ImageTk.PhotoImage @dataclass diff --git a/src/modlunky2/ui/levels/vanilla_levels/multi_room/multi_room_editor_tab.py b/src/modlunky2/ui/levels/vanilla_levels/multi_room/multi_room_editor_tab.py index 10de1096..d96b9724 100644 --- a/src/modlunky2/ui/levels/vanilla_levels/multi_room/multi_room_editor_tab.py +++ b/src/modlunky2/ui/levels/vanilla_levels/multi_room/multi_room_editor_tab.py @@ -167,7 +167,7 @@ def toggle_panel_hidden(): side_panel_tab_control.add(self.options_panel, text="Settings") def image_for_tile_code(self, tile_code): - tile_name = self.tile_palette_map[tile_code][0].split(" ", 2)[0] + tile_name = self.tile_palette_map[tile_code].name if tile_name in self.tile_image_map: return self.tile_image_map[tile_name] @@ -249,7 +249,6 @@ def open_lvl(self, lvl, biome, tile_palette_map, room_templates): self.room_templates = room_templates self.default_draw_map = find_roommap(room_templates) - # self.template_draw_map = find_roommap(room_templates) self.use_roommap_at( self.modlunky_config.default_custom_room_maps.get(self.lvl), True @@ -265,8 +264,6 @@ def toggle_reverse_layers(self, reverse): self.redraw() def update_templates(self, templates=None): - # if templates is not None: - # self.room_templates = templates self.options_panel.set_templates(self.template_draw_map, self.room_templates) def hide_grid_lines(self, hide_grid_lines): @@ -731,17 +728,20 @@ def populate_tilecode_palette(self, tile_palette, suggestions, dependency_tiles) self.lvl, ) - def palette_selected_tile(self, tile_name, image, is_primary): - self.on_select_palette_tile(tile_name, image, is_primary) + def palette_selected_tile(self, tile, is_primary): + self.on_select_palette_tile(tile, is_primary) - def select_tile(self, tile_name, image, is_primary): - self.palette_panel.select_tile(tile_name, image, is_primary) + def select_tile(self, tile, is_primary): + self.palette_panel.select_tile(tile, is_primary) def add_tilecode(self, tile, percent, alt_tile): self.on_add_tilecode(tile, percent, alt_tile) - def delete_tilecode(self, tile_name, tile_code): - self.on_delete_tilecode(tile_name, tile_code) + def delete_tilecode(self, tile): + deleted = self.on_delete_tilecode(tile) + if deleted: + self.redraw() + return deleted def room_was_deleted(self, template_index, chunk_index): replaced = False @@ -793,8 +793,8 @@ def canvas_click(self, canvas_index, row, column, is_primary): # Do not draw on backlayer of non-dual room. return - tile_name, tile_code = self.palette_panel.selected_tile(is_primary) - x_offset, y_offset = self.offset_for_tile(tile_name, tile_code, self.zoom_level) + tile = self.palette_panel.selected_tile(is_primary) + x_offset, y_offset = self.offset_for_tile(tile, self.zoom_level) layers = [chunk.front, chunk.back] if self.reverse_layers and template_draw_item.template.name in REVERSED_ROOMS: @@ -805,7 +805,7 @@ def canvas_click(self, canvas_index, row, column, is_primary): data_tile_col = tile_col if TemplateSetting.ONLYFLIP in chunk.settings: data_tile_col = template_draw_item.width_in_rooms * 10 - 1 - tile_col - layer[tile_row][data_tile_col] = tile_code + layer[tile_row][data_tile_col] = tile.code self.on_modify_room(template_draw_item) for map_index, template_map in enumerate(self.template_draw_map): @@ -823,7 +823,7 @@ def canvas_click(self, canvas_index, row, column, is_primary): CanvasIndex(canvas_index.tab_index, map_index), tile_row + r * 8, tile_col + c * 10, - self.image_for_tile_code(tile_code), + self.image_for_tile_code(tile.code), x_offset, y_offset, ) @@ -863,15 +863,15 @@ def canvas_shiftclick(self, canvas_index, row, column, is_primary): tile_code = layer[tile_row][tile_col] tile = self.tile_palette_map[tile_code] - self.palette_panel.select_tile(tile[0], tile[2], is_primary) - self.on_select_palette_tile(tile[0], tile[2], is_primary) + self.palette_panel.select_tile(tile, is_primary) + self.on_select_palette_tile(tile, is_primary) # Looks up the expected offset type and tile image size and computes the offset of the tile's anchor in the grid. - def offset_for_tile(self, tile_name, tile_code, tile_size): - img = self.image_for_tile_code(tile_code) + def offset_for_tile(self, tile, tile_size): + img = self.image_for_tile_code(tile.code) if img: return self.texture_fetcher.adjust_texture_xy( - img.width(), img.height(), tile_name, tile_size + img.width(), img.height(), tile.name, tile_size ) return 0, 0 @@ -925,7 +925,6 @@ def draw_canvas(self, fresh): ) ) - # self.canvas.draw_room_grid(2, [grid_sizes]) self.canvas.draw_canvas_room_grid(CanvasIndex(0, map_index), 2, grid_sizes) self.canvas.draw_canvas_room_grid(CanvasIndex(1, map_index), 2, grid_sizes) @@ -940,13 +939,11 @@ def draw_chunk(canvas_index, chunk_start_x, chunk_start_y, tile_codes): if tile_index + chunk_start_x >= width * 10: continue tilecode = self.tile_palette_map[tile] - tile_name = tilecode[0].split(" ", 1)[0] - # tile_image = tilecode[1] tile_image = self.image_for_tile_code(tile) x_offset, y_offset = self.texture_fetcher.adjust_texture_xy( tile_image.width(), tile_image.height(), - tile_name, + tilecode.name, self.zoom_level, ) self.canvas.replace_tile_at( diff --git a/src/modlunky2/ui/levels/vanilla_levels/vanilla_level_editor.py b/src/modlunky2/ui/levels/vanilla_levels/vanilla_level_editor.py index 87d7cb0f..97cebf03 100644 --- a/src/modlunky2/ui/levels/vanilla_levels/vanilla_level_editor.py +++ b/src/modlunky2/ui/levels/vanilla_levels/vanilla_level_editor.py @@ -29,7 +29,7 @@ ) from modlunky2.ui.levels.shared.palette_panel import PalettePanel from modlunky2.ui.levels.shared.setrooms import Setroom, MatchedSetroom -from modlunky2.ui.levels.shared.tile import DependencyPalette +from modlunky2.ui.levels.shared.tile import Tile, DependencyPalette from modlunky2.ui.levels.vanilla_levels.dual_util import make_dual, remove_dual from modlunky2.ui.levels.vanilla_levels.level_list_panel import LevelListPanel from modlunky2.ui.levels.vanilla_levels.level_settings_bar import LevelSettingsBar @@ -300,11 +300,9 @@ def toggle_panel_hidden(): nonlocal side_panel_hidden side_panel_hidden = not side_panel_hidden if side_panel_hidden: - # self.palette_panel.grid_remove() side_panel_container.grid_remove() side_panel_hide_button.configure(text="<<") else: - # self.palette_panel.grid() side_panel_container.grid() side_panel_hide_button.configure(text=">>") @@ -322,10 +320,9 @@ def zoom_changed(_): self.canvas.set_zoom(self.mag) if self.tile_palette_ref_in_use: for tile in self.tile_palette_ref_in_use: - tile_name = tile[0].split(" ", 2)[0] - tile[1] = ImageTk.PhotoImage( + tile.image = ImageTk.PhotoImage( self.texture_fetcher.get_texture( - tile_name, + tile.name, self.lvl_biome, self.lvl, self.mag, @@ -335,10 +332,9 @@ def zoom_changed(_): if self.dependency_tile_palette_ref_in_use: for palette in self.dependency_tile_palette_ref_in_use: for tile in palette.tiles: - tile_name = tile[0].split(" ", 2)[0] - tile[1] = ImageTk.PhotoImage( + tile.image = ImageTk.PhotoImage( self.texture_fetcher.get_texture( - tile_name, + tile.name, self.lvl_biome, self.lvl, self.mag, @@ -400,9 +396,6 @@ def get_level_tilecodes(level): level_tilecodes = level.tile_codes.all() def tilecode_item(tilecode): - tilecode_item = [] - tilecode_item.append(str(tilecode.name) + " " + str(tilecode.value)) - img = self.texture_fetcher.get_texture( tilecode.name, self.lvl_biome, lvl, self.mag ) @@ -410,27 +403,28 @@ def tilecode_item(tilecode): tilecode.name, self.lvl_biome, lvl, 40 ) - tilecode_item.append(ImageTk.PhotoImage(img)) - tilecode_item.append(ImageTk.PhotoImage(selection_img)) - - return tilecode_item + return Tile( + str(tilecode.name), + str(tilecode.value), + ImageTk.PhotoImage(img), + ImageTk.PhotoImage(selection_img), + ) return [tilecode_item(tc) for tc in level_tilecodes] def clear_tile_from_dependencies(tile): for palette in self.dependency_tile_palette_ref_in_use: for i in palette.tiles: - if str(i[0]).split(" ", 1)[1] == str(tile[0]).split(" ", 1)[1]: + if i.code == tile.code: palette.tiles.remove(i) def register_tile_code(tile): self.select_palette_tile(tile, True) self.select_palette_tile(tile, False) - code = tile[0].split(" ", 1)[1] - if code in self.usable_codes: - self.usable_codes.remove(code) + if tile.code in self.usable_codes: + self.usable_codes.remove(tile.code) - self.tile_palette_map[code] = tile + self.tile_palette_map[tile.code] = tile level_dependencies = LevelDependencies.dependencies_for_level(lvl) @@ -498,14 +492,12 @@ def register_tile_code(tile): for need in generic_needs: for code in self.usable_codes: if str(code) == need[0] and not any( - need[0] in str(code_in_use[0].split(" ", 3)[1]) + need[0] in code_in_use.code for code_in_use in self.tile_palette_ref_in_use ): for i in self.usable_codes: if str(i) == str(need[0]): self.usable_codes.remove(i) - tilecode_item = [] - tilecode_item.append(str(need[1]) + " " + str(need[0])) img = self.texture_fetcher.get_texture( str(need[1]), self.lvl_biome, lvl, self.mag @@ -514,10 +506,14 @@ def register_tile_code(tile): str(need[1]), self.lvl_biome, lvl, 40 ) - tilecode_item.append(ImageTk.PhotoImage(img)) - tilecode_item.append(ImageTk.PhotoImage(img_select)) - self.tile_palette_ref_in_use.append(tilecode_item) - self.tile_palette_map[need[0]] = tilecode_item + tile = Tile( + str(need[1]), + str(need[0]), + ImageTk.PhotoImage(img), + ImageTk.PhotoImage(img_select), + ) + self.tile_palette_ref_in_use.append(tile) + self.tile_palette_map[need[0]] = tile self.populate_tilecode_palette() self.rules_tab.load_level_settings(level.level_settings) @@ -551,11 +547,11 @@ def populate_tilecode_palette(self): self.tile_palette_ref_in_use, None, self.dependency_tile_palette_ref_in_use ) - def palette_selected_tile(self, tile_name, image, is_primary): - self.multi_room_editor_tab.select_tile(tile_name, image, is_primary) + def palette_selected_tile(self, tile, is_primary): + self.multi_room_editor_tab.select_tile(tile, is_primary) - def multiroom_editor_selected_tile(self, tile_name, image, is_primary): - self.palette_panel.select_tile(tile_name, image, is_primary) + def multiroom_editor_selected_tile(self, tile, is_primary): + self.palette_panel.select_tile(tile, is_primary) def multiroom_editor_modified_room(self, template_draw_item): if template_draw_item.room_chunk == self.current_selected_room: @@ -645,8 +641,8 @@ def save_changes(self): for tilecode in self.tile_palette_ref_in_use: tile_codes.set_obj( TileCode( - name=tilecode[0].split(" ", 1)[0], - value=tilecode[0].split(" ", 1)[1], + name=tilecode.name, + value=tilecode.code, comment="", ) ) @@ -706,19 +702,19 @@ def canvas_click( column, is_primary, ): - tile_name, tile_code = self.palette_panel.selected_tile(is_primary) - x_offset, y_offset = self.offset_for_tile(tile_name, tile_code, self.mag) + tile = self.palette_panel.selected_tile(is_primary) + x_offset, y_offset = self.offset_for_tile(tile, self.mag) self.canvas.replace_tile_at( canvas_index, row, column, - self.tile_palette_map[tile_code][1], + self.tile_palette_map[tile.code].image, x_offset, y_offset, ) - self.tile_codes[canvas_index.canvas_index][row][column] = tile_code + self.tile_codes[canvas_index.canvas_index][row][column] = tile.code self.changes_made() def canvas_shiftclick(self, canvas_index, row, column, is_primary): @@ -728,21 +724,18 @@ def canvas_shiftclick(self, canvas_index, row, column, is_primary): self.select_palette_tile(tile, is_primary) def select_palette_tile(self, tile, is_primary): - self.palette_panel.select_tile(tile[0], tile[2], is_primary) - self.multi_room_editor_tab.select_tile(tile[0], tile[2], is_primary) + self.palette_panel.select_tile(tile, is_primary) + self.multi_room_editor_tab.select_tile(tile, is_primary) # Looks up the expected offset type and tile image size and computes the offset of the tile's anchor in the grid. - def offset_for_tile(self, tile_name, tile_code, tile_size): - logger.debug("Applying custom anchor for %s", tile_name) - tile_ref = self.tile_palette_map[tile_code] - if tile_ref: - logger.debug("Found %s", tile_ref[0]) - img = tile_ref[1] - return self.texture_fetcher.adjust_texture_xy( - img.width(), img.height(), tile_name, tile_size - ) - - return 0, 0 + def offset_for_tile(self, tile, tile_size): + if not tile: + return 0, 0 + logger.debug("Applying custom anchor for %s", tile.name) + img = tile.image + return self.texture_fetcher.adjust_texture_xy( + img.width(), img.height(), tile.name, tile_size + ) def toggle_list_hide(self): if self.button_hide_tree["text"] == "<<": @@ -760,7 +753,7 @@ def replace_tiles_dia(self): replacees = [] for tile in self.tile_palette_ref_in_use: - replacees.append(str(tile[0])) + replacees.append(tile.name) col1_lbl = ttk.Label(win, text="Replace all ") col1_lbl.grid(row=0, column=0) @@ -787,7 +780,7 @@ def replace_tiles_dia(self): def update_then_destroy(): if ( - str(combo_replace.get().split(" ", 1)[0]) != "empty" + str(combo_replace.get()) != "empty" and combo_replace.get() != "" and combo_replacer.get() != "" ): @@ -795,17 +788,22 @@ def update_then_destroy(): error_lbl["text"] = "Invalid parameter" error_lbl.grid() return - valid_1 = False - valid_2 = False - for valid_tile in replacees: - if str(combo_replace.get()) == valid_tile: - valid_1 = True - if str(combo_replacer.get()) == valid_tile: - valid_2 = True - if valid_1 is False or valid_2 is False: + replace_index = combo_replace.current() + replacer_index = combo_replacer.current() + valid_1 = replace_index != -1 and replace_index < len( + self.tile_palette_ref_in_use + ) + valid_2 = replacer_index != -1 and replacer_index < len( + self.tile_palette_ref_in_use + ) + + if not valid_1 or not valid_2: error_lbl["text"] = "Invalid parameter" error_lbl.grid() return + + replacee_tile = self.tile_palette_ref_in_use[replace_index] + replacer_tile = self.tile_palette_ref_in_use[replacer_index] if ( str(combo_where.get()) == "current room" and self.current_selected_room is None @@ -814,8 +812,8 @@ def update_then_destroy(): error_lbl.grid() return self.replace_tiles( - str(combo_replace.get().split(" ", 1)[1]), - str(combo_replacer.get().split(" ", 1)[1]), + replacee_tile.code, + replacer_tile.code, str(combo_where.get()), ) win.destroy() @@ -917,12 +915,11 @@ def room_select(self): # Loads room when click if not parent node. for row_index, row in enumerate(layer_tile_codes): for column_index, tile_code in enumerate(row): tile = self.tile_palette_map[tile_code] - tile_name = str(tile[0]).split(" ", 1)[0] - tile_image = tile[1] + tile_image = tile.image x_coord, y_coord = self.texture_fetcher.adjust_texture_xy( tile_image.width(), tile_image.height(), - tile_name, + tile.name, ) self.canvas.replace_tile_at( CanvasIndex(0, canvas_index), @@ -940,34 +937,37 @@ def room_select(self): # Loads room when click if not parent node. self.show_intro() self.button_clear["state"] = tk.NORMAL - def delete_tilecode(self, tile_name, tile_code): + def delete_tilecode(self, tile): msg_box = tk.messagebox.askquestion( "Delete Tilecode?", "Are you sure you want to delete this Tilecode?\nAll of its placements will be replaced with air.", icon="warning", ) if msg_box == "yes": - if tile_name == r"empty": + if tile.name == r"empty": tkMessageBox.showinfo("Uh Oh!", "Can't delete empty!") return - self.replace_tiles(tile_code, "0", "all rooms") - logger.debug("Replaced %s in all rooms with air/empty", tile_name) + self.replace_tiles(tile.code, "0", "all rooms") + logger.debug("Replaced %s in all rooms with air/empty", tile.name) - self.usable_codes.append(str(tile_code)) - logger.debug("%s is now available for use.", tile_code) + self.usable_codes.append(str(tile.code)) + logger.debug("%s is now available for use.", tile.code) # Adds tilecode back to list to be reused. for id_ in self.tile_palette_ref_in_use: - if str(tile_code) == str(id_[0].split(" ", 2)[1]): + if tile.code == id_.code: self.tile_palette_ref_in_use.remove(id_) - logger.debug("Deleted %s", tile_name) + logger.debug("Deleted %s", tile.name) self.populate_tilecode_palette() self.log_codes_left() self.changes_made() self.variables_tab.check_dependencies() + return True + else: + return False def log_codes_left(self): codes = "" @@ -1031,8 +1031,7 @@ def add_tilecode( # Compares tile id to tile ids in palette list. for _, palette_tile in self.tile_palette_map.items(): - palette_tile = palette_tile[0].split()[0].strip() - print(palette_tile) + palette_tile = palette_tile.name if new_tile_code == palette_tile: tkMessageBox.showinfo("Uh Oh!", "You already have that!") return @@ -1048,10 +1047,7 @@ def add_tilecode( ) return - ref_tile = [] - ref_tile.append(new_tile_code + " " + str(usable_code)) - ref_tile.append(tile_image) - ref_tile.append(tile_image_picker) + ref_tile = Tile(new_tile_code, str(usable_code), tile_image, tile_image_picker) self.tile_palette_ref_in_use.append(ref_tile) self.tile_palette_map[usable_code] = ref_tile