From d95c3f7555decd8a120e2bb69c42ca9460aa195f Mon Sep 17 00:00:00 2001 From: Emmanouil Papadeas <35376950+OverloadedOrama@users.noreply.github.com> Date: Sun, 24 Nov 2024 02:19:04 +0200 Subject: [PATCH] Properly implement the auto tile editing mode Should work well now. --- src/Classes/Cels/CelTileMap.gd | 162 ++++++++++++++++++++++++++------- src/Classes/TileSetCustom.gd | 31 +++++++ 2 files changed, 160 insertions(+), 33 deletions(-) diff --git a/src/Classes/Cels/CelTileMap.gd b/src/Classes/Cels/CelTileMap.gd index 976c8d401..da2c4010f 100644 --- a/src/Classes/Cels/CelTileMap.gd +++ b/src/Classes/Cels/CelTileMap.gd @@ -16,35 +16,13 @@ func _init(_tileset: TileSetCustom, _image: ImageExtended, _opacity := 1.0) -> v func update_tileset() -> void: - var removed_tile_indices: Array[int] = [] - if TileSetPanel.tile_editing_mode == TileSetPanel.TileEditingMode.AUTO: - for j in range(tileset.tiles.size() - 1, 0, -1): - var tile := tileset.tiles[j] - if tile.mode_added == TileSetPanel.TileEditingMode.STACK: - # Don't delete tiles that have been added using the stack mode. - continue - var tile_used := false - for i in indices.size(): - var x_coord := float(tileset.tile_size.x) * (i % indices_x) - var y_coord := float(tileset.tile_size.y) * (i / indices_x) - var rect := Rect2i(Vector2i(x_coord, y_coord), tileset.tile_size) - var image_portion := image.get_region(rect) - if image_portion.is_invisible(): - continue - if image_portion.get_data() == tile.image.get_data(): - tile_used = true - break - if not tile_used: - removed_tile_indices.append(j) - tileset.remove_tile_at_index(j) for i in indices.size(): var x_coord := float(tileset.tile_size.x) * (i % indices_x) var y_coord := float(tileset.tile_size.y) * (i / indices_x) var rect := Rect2i(Vector2i(x_coord, y_coord), tileset.tile_size) var image_portion := image.get_region(rect) - if image_portion.is_invisible(): - continue var index := indices[i] + var current_tile := tileset.tiles[index] if TileSetPanel.tile_editing_mode == TileSetPanel.TileEditingMode.MANUAL: if index == 0 or tileset.tiles.size() <= index: if tileset.tiles.size() <= 1: @@ -54,7 +32,9 @@ func update_tileset() -> void: if image_portion.get_data() != tileset.tiles[index].image.get_data(): tileset.replace_tile_at(image_portion, index) # TODO: Update the rest of the tilemap - else: # Auto or stack + elif TileSetPanel.tile_editing_mode == TileSetPanel.TileEditingMode.AUTO: + handle_auto_editing_mode(i, image_portion) + else: # Stack var found_tile := false for j in range(1, tileset.tiles.size()): var tile := tileset.tiles[j] @@ -63,15 +43,131 @@ func update_tileset() -> void: found_tile = true break if not found_tile: - if removed_tile_indices.is_empty(): - tileset.add_tile(image_portion, TileSetPanel.tile_editing_mode) - indices[i] = tileset.tiles.size() - 1 - else: - var index_position := removed_tile_indices.pop_back() as int - tileset.insert_tile( - image_portion, index_position, TileSetPanel.tile_editing_mode - ) - indices[i] = index_position + tileset.add_tile(image_portion, TileSetPanel.tile_editing_mode) + indices[i] = tileset.tiles.size() - 1 + + +## Cases:[br] +## 0) Portion is transparent. Set its index to 0. +## [br] +## 0.5) Portion is transparent and mapped. +## Set its index to 0 and unuse the mapped tile. +## If the mapped tile is removed, educe the index of all portions that have indices greater or equal +## than the existing tile's index. +## [br] +## 1) Portion not mapped, exists in the tileset. +## Map the portion to the existing tile and increase its times_used by one. +## [br] +## 2) Portion not mapped, does not exist in the tileset. +## Add the portion as a tile in the tileset, set its index to be the tileset's tile size - 1. +## [br] +## 3) Portion mapped, tile did not change. Do nothing. +## [br] +## 4) Portion mapped, exists in the tileset. +## The mapped tile still exists in the tileset. +## Map the portion to the existing tile, increase its times_used by one, +## and reduce the previously mapped tile's times_used by 1. +## [br] +## 5) Portion mapped, exists in the tileset. +## The mapped tile does not exist in the tileset anymore. +## Map the portion to the existing tile and increase its times_used by one. +## Remove the previously mapped tile, +## and reduce the index of all portions that have indices greater or equal +## than the existing tile's index. +## [br] +## 6) Portion mapped, does not exist in the tileset. +## The mapped tile still exists in the tileset. +## Add the portion as a tile in the tileset, set its index to be the tileset's tile size - 1. +## Reduce the previously mapped tile's times_used by 1. +## [br] +## 7) Portion mapped, does not exist in the tileset. +## The mapped tile does not exist in the tileset anymore. +## Simply replace the old tile with the new one, do not change its index. +func handle_auto_editing_mode(i: int, image_portion: Image) -> void: + var index := indices[i] + var current_tile := tileset.tiles[index] + if image_portion.is_invisible(): + # Case 0: The portion is transparent. + indices[i] = 0 + if index > 0: + # Case 0.5: The portion is transparent and mapped to a tile. + var is_removed := tileset.unuse_tile_at_index(index) + if is_removed: + # Re-index all indices that are after the deleted one. + re_index_tiles_after_index(index) + return + var index_in_tileset := tileset.find_tile(image_portion) + if index == 0: # If the portion is not mapped to a tile. + if index_in_tileset > -1: + # Case 1: The portion is not mapped already, + # and it exists in the tileset as a tile. + tileset.tiles[index_in_tileset].times_used += 1 + indices[i] = index_in_tileset + else: + # Case 2: The portion is not mapped already, + # and it does not exist in the tileset. + tileset.add_tile(image_portion, TileSetPanel.tile_editing_mode) + indices[i] = tileset.tiles.size() - 1 + else: # If the portion is already mapped. + if image_portion.get_data() == current_tile.image.get_data(): + # Case 3: The portion is mapped and it did not change. + # Do nothing and move on to the next portion. + return + var previous_tile_index_in_tileset := tileset.find_tile(current_tile.image) + if index_in_tileset > -1: # If the portion exists in the tileset as a tile. + if current_tile.times_used > 1: + # Case 4: The portion is mapped and it exists in the tileset as a tile, + # and the currently mapped tile still exists in the tileset. + tileset.tiles[index_in_tileset].times_used += 1 + indices[i] = index_in_tileset + tileset.unuse_tile_at_index(index) + else: + # Case 5: The portion is mapped and it exists in the tileset as a tile, + # and the currently mapped tile no longer exists in the tileset. + tileset.tiles[index_in_tileset].times_used += 1 + indices[i] = index_in_tileset + tileset.remove_tile_at_index(index) + # Re-index all indices that are after the deleted one. + re_index_tiles_after_index(index) + else: # If the portion does not exist in the tileset as a tile. + if current_tile.times_used > 1: + # Case 6: The portion is mapped and it does not + # exist in the tileset as a tile, + # and the currently mapped tile still exists in the tileset. + tileset.unuse_tile_at_index(index) + tileset.add_tile(image_portion, TileSetPanel.tile_editing_mode) + indices[i] = tileset.tiles.size() - 1 + else: + # Case 7: The portion is mapped and it does not + # exist in the tileset as a tile, + # and the currently mapped tile no longer exists in the tileset. + tileset.replace_tile_at(image_portion, index) + + +## Re-indexes all [member indices] that are larger or equal to [param index], +## by reducing their value by one. +func re_index_tiles_after_index(index: int) -> void: + for i in indices.size(): + var tmp_index := indices[i] + if tmp_index >= index: + indices[i] -= 1 + + +## Unused, should delete. +func re_index_tiles() -> void: + for i in indices.size(): + var x_coord := float(tileset.tile_size.x) * (i % indices_x) + var y_coord := float(tileset.tile_size.y) * (i / indices_x) + var rect := Rect2i(Vector2i(x_coord, y_coord), tileset.tile_size) + var image_portion := image.get_region(rect) + if image_portion.is_invisible(): + indices[i] = 0 + continue + for j in range(1, tileset.tiles.size()): + var tile := tileset.tiles[j] + if image_portion.get_data() == tile.image.get_data(): + indices[i] = j + break func get_class_name() -> String: diff --git a/src/Classes/TileSetCustom.gd b/src/Classes/TileSetCustom.gd index bbeb2e539..8d5867a30 100644 --- a/src/Classes/TileSetCustom.gd +++ b/src/Classes/TileSetCustom.gd @@ -11,11 +11,15 @@ var tiles: Array[Tile] = [] class Tile: var image: Image var mode_added: TileSetPanel.TileEditingMode + var times_used := 1 func _init(_image: Image, _mode_added: TileSetPanel.TileEditingMode) -> void: image = _image mode_added = _mode_added + func can_be_removed() -> bool: + return mode_added != TileSetPanel.TileEditingMode.STACK and times_used <= 0 + func _init(_tile_size: Vector2i, _name := "") -> void: tile_size = _tile_size @@ -39,6 +43,14 @@ func insert_tile(image: Image, position: int, edit_mode: TileSetPanel.TileEditin updated.emit() +func unuse_tile_at_index(index: int) -> bool: + tiles[index].times_used -= 1 + if tiles[index].can_be_removed(): + remove_tile_at_index(index) + return true + return false + + func remove_tile_at_index(index: int) -> void: tiles.remove_at(index) updated.emit() @@ -47,3 +59,22 @@ func remove_tile_at_index(index: int) -> void: func replace_tile_at(new_tile: Image, index: int) -> void: tiles[index].image.copy_from(new_tile) updated.emit() + + +func find_tile(image: Image) -> int: + for i in tiles.size(): + var tile := tiles[i] + if image.get_data() == tile.image.get_data(): + return i + return -1 + + +## Unused, should delete. +func remove_unused_tiles() -> bool: + var tile_removed := false + for i in range(tiles.size() - 1, 0, -1): + var tile := tiles[i] + if tile.can_be_removed(): + tile_removed = true + remove_tile_at_index(i) + return tile_removed