mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-01-19 09:39:48 +00:00
Make undo/redo store tilemap cell indices and tileset tiles
Fixes issues with cases 0.5 and 5 of auto mode, and should be a better system overall. Only works with BaseDraw tools, needs to be applied everywhere as well.
This commit is contained in:
parent
8d1652dc09
commit
903ea5134a
|
@ -984,7 +984,6 @@ func undo_or_redo(
|
|||
if action_name == "Scale":
|
||||
cel.size_changed(project.size)
|
||||
canvas.update_texture(layer_index, frame_index, project, undo)
|
||||
cel.on_undo_redo(undo)
|
||||
else:
|
||||
for i in project.frames.size():
|
||||
for j in project.layers.size():
|
||||
|
@ -992,7 +991,6 @@ func undo_or_redo(
|
|||
if action_name == "Scale":
|
||||
cel.size_changed(project.size)
|
||||
canvas.update_texture(j, i, project, undo)
|
||||
cel.on_undo_redo(undo)
|
||||
|
||||
canvas.selection.queue_redraw()
|
||||
if action_name == "Scale":
|
||||
|
|
|
@ -269,7 +269,7 @@ func open_pxo_file(path: String, is_backup := false, replace_empty := true) -> v
|
|||
var image := Image.create_from_data(
|
||||
tile_size.x, tile_size.y, false, new_project.get_image_format(), image_data
|
||||
)
|
||||
tileset.add_tile(image, null, 2)
|
||||
tileset.add_tile(image, null)
|
||||
zip_reader.close()
|
||||
new_project.export_directory_path = path.get_base_dir()
|
||||
|
||||
|
@ -844,14 +844,14 @@ func open_image_as_tileset(
|
|||
var frame_width := image.get_size().x / horiz
|
||||
var frame_height := image.get_size().y / vert
|
||||
var tile_size := Vector2i(frame_width, frame_height)
|
||||
var tileset := TileSetCustom.new(tile_size, project, path.get_basename().get_file())
|
||||
var tileset := TileSetCustom.new(tile_size, path.get_basename().get_file())
|
||||
for yy in range(vert):
|
||||
for xx in range(horiz):
|
||||
var cropped_image := image.get_region(
|
||||
Rect2i(frame_width * xx, frame_height * yy, frame_width, frame_height)
|
||||
)
|
||||
@warning_ignore("int_as_enum_without_cast")
|
||||
tileset.add_tile(cropped_image, null, 2)
|
||||
tileset.add_tile(cropped_image, null)
|
||||
project.tilesets.append(tileset)
|
||||
|
||||
|
||||
|
@ -866,7 +866,7 @@ func open_image_as_tileset_smart(
|
|||
if sliced_rects.size() == 0: # Image is empty sprite (manually set data to be consistent)
|
||||
tile_size = image.get_size()
|
||||
sliced_rects.append(Rect2i(Vector2i.ZERO, tile_size))
|
||||
var tileset := TileSetCustom.new(tile_size, project, path.get_basename().get_file())
|
||||
var tileset := TileSetCustom.new(tile_size, path.get_basename().get_file())
|
||||
for rect in sliced_rects:
|
||||
var offset: Vector2 = (0.5 * (tile_size - rect.size)).floor()
|
||||
var cropped_image := Image.create(
|
||||
|
@ -874,7 +874,7 @@ func open_image_as_tileset_smart(
|
|||
)
|
||||
cropped_image.blit_rect(image, rect, offset)
|
||||
@warning_ignore("int_as_enum_without_cast")
|
||||
tileset.add_tile(cropped_image, null, 2)
|
||||
tileset.add_tile(cropped_image, null)
|
||||
project.tilesets.append(tileset)
|
||||
|
||||
|
||||
|
|
|
@ -77,10 +77,6 @@ func update_texture(_undo := false) -> void:
|
|||
cel.texture_changed.emit()
|
||||
|
||||
|
||||
func on_undo_redo(_undo: bool) -> void:
|
||||
pass
|
||||
|
||||
|
||||
## Returns a curated [Dictionary] containing the cel data.
|
||||
func serialize() -> Dictionary:
|
||||
var dict := {"opacity": opacity, "z_index": z_index}
|
||||
|
|
|
@ -29,9 +29,6 @@ var cells: Array[Cell]
|
|||
var horizontal_cells: int
|
||||
## The amount of vertical cells.
|
||||
var vertical_cells: int
|
||||
## Dictionary of [int] and an [Array] of [bool] ([member TileSetPanel.placing_tiles])
|
||||
## and [enum TileSetPanel.TileEditingMode].
|
||||
var undo_redo_modes := {}
|
||||
## Dictionary of [int] and [Array].
|
||||
## The key is the index of the tile in the tileset,
|
||||
## and the value is the index of the tilemap tile that changed first, along with
|
||||
|
@ -147,14 +144,9 @@ func transform_tile(
|
|||
return transformed_tile
|
||||
|
||||
|
||||
func update_tileset(undo: bool) -> void:
|
||||
func update_tileset() -> void:
|
||||
editing_images.clear()
|
||||
var undos := tileset.project.undos
|
||||
if not undo and not _is_redo():
|
||||
undo_redo_modes[undos] = [TileSetPanel.placing_tiles, TileSetPanel.tile_editing_mode]
|
||||
if undo:
|
||||
undos += 1
|
||||
var tile_editing_mode := _get_tile_editing_mode(undos)
|
||||
var tile_editing_mode := TileSetPanel.tile_editing_mode
|
||||
for i in cells.size():
|
||||
var coords := get_cell_coords_in_image(i)
|
||||
var rect := Rect2i(coords, tileset.tile_size)
|
||||
|
@ -171,7 +163,7 @@ func update_tileset(undo: bool) -> void:
|
|||
if index == 0:
|
||||
# If the tileset is empty, only then add a new tile.
|
||||
if tileset.tiles.size() <= 1:
|
||||
tileset.add_tile(image_portion, self, tile_editing_mode)
|
||||
tileset.add_tile(image_portion, self)
|
||||
cells[i].index = tileset.tiles.size() - 1
|
||||
continue
|
||||
if not tiles_equal(i, image_portion, current_tile.image):
|
||||
|
@ -189,12 +181,8 @@ func update_tileset(undo: bool) -> void:
|
|||
found_tile = true
|
||||
break
|
||||
if not found_tile:
|
||||
tileset.add_tile(image_portion, self, tile_editing_mode)
|
||||
tileset.add_tile(image_portion, self)
|
||||
cells[i].index = tileset.tiles.size() - 1
|
||||
if undo:
|
||||
var tile_removed := tileset.remove_unused_tiles(self)
|
||||
if tile_removed:
|
||||
_re_index_all_cells()
|
||||
|
||||
|
||||
## Cases:[br]
|
||||
|
@ -256,7 +244,7 @@ func _handle_auto_editing_mode(i: int, image_portion: Image) -> void:
|
|||
else:
|
||||
# Case 2: The cell is not mapped already,
|
||||
# and it does not exist in the tileset.
|
||||
tileset.add_tile(image_portion, self, TileSetPanel.TileEditingMode.AUTO)
|
||||
tileset.add_tile(image_portion, self)
|
||||
cells[i].index = tileset.tiles.size() - 1
|
||||
else: # If the cell is already mapped.
|
||||
if tiles_equal(i, image_portion, current_tile.image):
|
||||
|
@ -284,7 +272,7 @@ func _handle_auto_editing_mode(i: int, image_portion: Image) -> void:
|
|||
# exist in the tileset as a tile,
|
||||
# and the currently mapped tile still exists in the tileset.
|
||||
tileset.unuse_tile_at_index(index, self)
|
||||
tileset.add_tile(image_portion, self, TileSetPanel.TileEditingMode.AUTO)
|
||||
tileset.add_tile(image_portion, self)
|
||||
cells[i].index = tileset.tiles.size() - 1
|
||||
else:
|
||||
# Case 7: The cell is mapped and it does not
|
||||
|
@ -349,15 +337,6 @@ func _is_redo() -> bool:
|
|||
return Global.control.redone
|
||||
|
||||
|
||||
func _get_tile_editing_mode(undos: int) -> TileSetPanel.TileEditingMode:
|
||||
var tile_editing_mode: TileSetPanel.TileEditingMode
|
||||
if undo_redo_modes.has(undos):
|
||||
tile_editing_mode = undo_redo_modes[undos][1]
|
||||
else:
|
||||
tile_editing_mode = TileSetPanel.tile_editing_mode
|
||||
return tile_editing_mode
|
||||
|
||||
|
||||
## If the tileset has been modified by another tile, make sure to also update it here.
|
||||
func _on_tileset_updated(cel: CelTileMap) -> void:
|
||||
if cel == self or not is_instance_valid(cel):
|
||||
|
@ -415,36 +394,29 @@ func size_changed(new_size: Vector2i) -> void:
|
|||
_re_index_all_cells()
|
||||
|
||||
|
||||
func on_undo_redo(undo: bool) -> void:
|
||||
var undos := tileset.project.undos
|
||||
if undo:
|
||||
undos += 1
|
||||
if (undo or _is_redo()) and undo_redo_modes.has(undos):
|
||||
var placing_tiles: bool = undo_redo_modes[undos][0]
|
||||
if placing_tiles:
|
||||
_re_index_all_cells()
|
||||
return
|
||||
update_tileset(undo)
|
||||
|
||||
|
||||
func serialize_undo_data() -> Dictionary:
|
||||
var dict := {}
|
||||
var cells_serialized := []
|
||||
cells_serialized.resize(cells.size())
|
||||
for i in cells.size():
|
||||
cells_serialized[i] = cells[i].serialize()
|
||||
dict["cells_data"] = cells_serialized
|
||||
var cell_indices := []
|
||||
cell_indices.resize(cells.size())
|
||||
for i in cell_indices.size():
|
||||
cell_indices[i] = cells[i].serialize()
|
||||
dict["cell_indices"] = cell_indices
|
||||
dict["tileset"] = tileset.serialize_undo_data()
|
||||
return dict
|
||||
|
||||
|
||||
func deserialize_undo_data(dict: Dictionary, undo_redo: UndoRedo, undo: bool) -> void:
|
||||
var cells_data = dict["cells_data"]
|
||||
for i in cells_data.size():
|
||||
var cell_data: Dictionary = cells_data[i]
|
||||
var cell_indices = dict["cell_indices"]
|
||||
if undo:
|
||||
for i in cell_indices.size():
|
||||
var cell_data: Dictionary = cell_indices[i]
|
||||
undo_redo.add_undo_method(cells[i].deserialize.bind(cell_data))
|
||||
undo_redo.add_undo_method(tileset.deserialize_undo_data.bind(dict.get("tileset"), self))
|
||||
else:
|
||||
for i in cell_indices.size():
|
||||
var cell_data: Dictionary = cell_indices[i]
|
||||
undo_redo.add_do_method(cells[i].deserialize.bind(cell_data))
|
||||
undo_redo.add_do_method(tileset.deserialize_undo_data.bind(dict.get("tileset"), self))
|
||||
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
|
|
|
@ -352,7 +352,7 @@ func deserialize(dict: Dictionary, zip_reader: ZIPReader = null, file: FileAcces
|
|||
if dict.has("tilesets"):
|
||||
for saved_tileset in dict["tilesets"]:
|
||||
var tile_size = str_to_var("Vector2i" + saved_tileset.get("tile_size"))
|
||||
var tileset := TileSetCustom.new(tile_size, self)
|
||||
var tileset := TileSetCustom.new(tile_size)
|
||||
tileset.deserialize(saved_tileset)
|
||||
tilesets.append(tileset)
|
||||
if dict.has("frames") and dict.has("layers"):
|
||||
|
|
|
@ -10,8 +10,6 @@ extends RefCounted
|
|||
## The [CelTileMap] that the changes are coming from is referenced in the [param cel] parameter.
|
||||
signal updated(cel: CelTileMap)
|
||||
|
||||
## The [Project] the tileset is being used by.
|
||||
var project: Project
|
||||
## The tileset's name.
|
||||
var name := ""
|
||||
## The size of each individual tile.
|
||||
|
@ -24,45 +22,30 @@ var tiles: Array[Tile] = []
|
|||
class Tile:
|
||||
## The [Image] tile itself.
|
||||
var image: Image
|
||||
## The mode that was used when this tile was added to the tileset.
|
||||
var mode_added: TileSetPanel.TileEditingMode
|
||||
## The amount of tiles this tile is being used in tilemaps.
|
||||
var times_used := 1
|
||||
## The step number of undo/redo when this tile was added to the tileset.
|
||||
var undo_step_added := 0
|
||||
|
||||
func _init(
|
||||
_image: Image, _mode_added: TileSetPanel.TileEditingMode, _undo_step_added := 0
|
||||
) -> void:
|
||||
func _init(_image: Image) -> void:
|
||||
image = _image
|
||||
mode_added = _mode_added
|
||||
undo_step_added = _undo_step_added
|
||||
|
||||
## A method that checks if the tile should be removed from the tileset.
|
||||
## Returns [code]true[/code] if the current undo step is less than [member undo_step_added],
|
||||
## which essentially means that the tile always gets removed if the user undos to the point
|
||||
## the tile was added to the tileset.
|
||||
## Otherwise, it returns [code]true[/code] if [member mode_added] is not equal to
|
||||
## [enum TileSetPanel.TileEditingMode.STACK] and the amount of [member times_used] is 0.
|
||||
func can_be_removed(project: Project) -> bool:
|
||||
if project.undos < undo_step_added:
|
||||
return true
|
||||
return mode_added != TileSetPanel.TileEditingMode.STACK and times_used <= 0
|
||||
## Returns [code]true[/code] if the amount of [member times_used] is 0.
|
||||
func can_be_removed() -> bool:
|
||||
return times_used <= 0
|
||||
|
||||
|
||||
func _init(_tile_size: Vector2i, _project: Project, _name := "") -> void:
|
||||
func _init(_tile_size: Vector2i, _name := "") -> void:
|
||||
tile_size = _tile_size
|
||||
project = _project
|
||||
name = _name
|
||||
var empty_image := Image.create_empty(tile_size.x, tile_size.y, false, Image.FORMAT_RGBA8)
|
||||
tiles.append(Tile.new(empty_image, TileSetPanel.tile_editing_mode))
|
||||
tiles.append(Tile.new(empty_image))
|
||||
|
||||
|
||||
## Adds a new [param image] as a tile to the tileset.
|
||||
## The [param cel] parameter references the [CelTileMap] that this change is coming from,
|
||||
## and the [param edit_mode] parameter contains the tile editing mode at the time of this change.
|
||||
func add_tile(image: Image, cel: CelTileMap, edit_mode: TileSetPanel.TileEditingMode) -> void:
|
||||
var tile := Tile.new(image, edit_mode, project.undos)
|
||||
func add_tile(image: Image, cel: CelTileMap) -> void:
|
||||
var tile := Tile.new(image)
|
||||
tiles.append(tile)
|
||||
updated.emit(cel)
|
||||
|
||||
|
@ -70,10 +53,8 @@ func add_tile(image: Image, cel: CelTileMap, edit_mode: TileSetPanel.TileEditing
|
|||
## Adds a new [param image] as a tile in a given [param position] in the tileset.
|
||||
## The [param cel] parameter references the [CelTileMap] that this change is coming from,
|
||||
## and the [param edit_mode] parameter contains the tile editing mode at the time of this change.
|
||||
func insert_tile(
|
||||
image: Image, position: int, cel: CelTileMap, edit_mode: TileSetPanel.TileEditingMode
|
||||
) -> void:
|
||||
var tile := Tile.new(image, edit_mode, project.undos)
|
||||
func insert_tile(image: Image, position: int, cel: CelTileMap) -> void:
|
||||
var tile := Tile.new(image)
|
||||
tiles.insert(position, tile)
|
||||
updated.emit(cel)
|
||||
|
||||
|
@ -86,7 +67,7 @@ func insert_tile(
|
|||
## The [param cel] parameter references the [CelTileMap] that this change is coming from.
|
||||
func unuse_tile_at_index(index: int, cel: CelTileMap) -> bool:
|
||||
tiles[index].times_used -= 1
|
||||
if tiles[index].can_be_removed(project):
|
||||
if tiles[index].can_be_removed():
|
||||
remove_tile_at_index(index, cel)
|
||||
return true
|
||||
return false
|
||||
|
@ -122,7 +103,7 @@ func remove_unused_tiles(cel: CelTileMap) -> bool:
|
|||
var tile_removed := false
|
||||
for i in range(tiles.size() - 1, 0, -1):
|
||||
var tile := tiles[i]
|
||||
if tile.can_be_removed(project):
|
||||
if tile.can_be_removed():
|
||||
remove_tile_at_index(i, cel)
|
||||
tile_removed = true
|
||||
return tile_removed
|
||||
|
@ -139,3 +120,25 @@ func serialize() -> Dictionary:
|
|||
func deserialize(dict: Dictionary) -> void:
|
||||
name = dict.get("name", name)
|
||||
tile_size = str_to_var("Vector2i" + dict.get("tile_size"))
|
||||
|
||||
|
||||
func serialize_undo_data() -> Dictionary:
|
||||
var dict := {}
|
||||
for tile in tiles:
|
||||
var image_data := tile.image.get_data()
|
||||
dict[tile.image] = [image_data.compress(), image_data.size(), tile.times_used]
|
||||
return dict
|
||||
|
||||
|
||||
func deserialize_undo_data(dict: Dictionary, cel: CelTileMap) -> void:
|
||||
tiles.resize(dict.size())
|
||||
var i := 0
|
||||
for image: Image in dict:
|
||||
var tile_data = dict[image]
|
||||
var buffer_size := tile_data[1] as int
|
||||
var image_data := (tile_data[0] as PackedByteArray).decompress(buffer_size)
|
||||
image.set_data(tile_size.x, tile_size.y, false, image.get_format(), image_data)
|
||||
tiles[i] = Tile.new(image)
|
||||
tiles[i].times_used = tile_data[2]
|
||||
i += 1
|
||||
updated.emit(cel)
|
||||
|
|
|
@ -272,6 +272,9 @@ func prepare_undo(action: String) -> void:
|
|||
|
||||
|
||||
func commit_undo() -> void:
|
||||
for cel in _undo_data:
|
||||
if cel is CelTileMap:
|
||||
(cel as CelTileMap).update_tileset()
|
||||
var redo_data := _get_undo_data()
|
||||
var project := Global.current_project
|
||||
var frame := -1
|
||||
|
|
|
@ -14,7 +14,7 @@ func _on_confirmed() -> void:
|
|||
var tile_size := tile_size_slider.value
|
||||
var tileset: TileSetCustom
|
||||
if tileset_option_button.selected == 0:
|
||||
tileset = TileSetCustom.new(tile_size, project, tileset_name)
|
||||
tileset = TileSetCustom.new(tile_size, tileset_name)
|
||||
else:
|
||||
tileset = project.tilesets[tileset_option_button.selected - 1]
|
||||
var layer := LayerTileMap.new(project, tileset, layer_name)
|
||||
|
|
Loading…
Reference in a new issue