mirror of
https://github.com/Orama-Interactive/Pixelorama.git
synced 2025-03-13 23:05:20 +00:00
Change tilemap offset with the move tool (#1192)
* Begin replacing the cells array with a dictionary * Get rid of some old code and fix selections * Fix selection tools * Create Cells in the cells dictionary when resizing * Initial work for offset * Make the move tool change the tilemap offset When draw tiles mode is active * Fix drawing on negative tile coords * Fixes in the bucket tool * Bucket and magic wand should work properly now with offset * Delete get_cell_index_at_coords_in_tilemap_space() * Format * Fix manual mode not working with offsets * Fix draw tile preview offset snapping * Ignore manual mode if draw tiles mode is active * Remove unused parameter in apply_resizing_to_image() * Offset fixes when using selection tools Resizing not fixed yet * Ignore selection when using the move tool and draw tiles mode is active * Fix resizing selections with offset * Rename "cells_dict" to "cells" * Add some docstrings * Fix pxo save/load * Fix cells not being added when moving up or to the left * Copy tilemap offset when copying frames and layers * Fix frame/layer cloning breaking the tileset when the cloned cels are being edited
This commit is contained in:
parent
273b125f82
commit
50cc83f8ea
17 changed files with 283 additions and 247 deletions
|
@ -14,16 +14,14 @@ extends PixelCel
|
|||
## The [TileSetCustom] that this cel uses, passed down from the cel's [LayerTileMap].
|
||||
var tileset: TileSetCustom
|
||||
|
||||
## The [Array] of type [CelTileMap.Cell] that contains data for each cell of the tilemap.
|
||||
## The array's size is equal to [member horizontal_cells] * [member vertical_cells].
|
||||
var cells: Array[Cell]
|
||||
## The amount of horizontal cells.
|
||||
var horizontal_cells: int
|
||||
## The amount of vertical cells.
|
||||
var vertical_cells: int
|
||||
var cells := {} ## Dictionary of Vector2i and Cell.
|
||||
var vertical_cell_min := 0 ## The minimum vertical cell.
|
||||
var vertical_cell_max := 0 ## The maximum vertical cell.
|
||||
var offset := Vector2i.ZERO ## The offset of the tilemap.
|
||||
var prev_offset := offset ## Used for undo/redo purposes.
|
||||
## 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
|
||||
## and the value is the coords of the tilemap tile that changed first, along with
|
||||
## its image that is being changed when manual mode is enabled.
|
||||
## Gets reset on [method update_tilemap].
|
||||
var editing_images := {}
|
||||
|
@ -86,76 +84,56 @@ func set_tileset(new_tileset: TileSetCustom, reset_indices := true) -> void:
|
|||
|
||||
## Maps the cell at position [param cell_position] to
|
||||
## the [member tileset]'s tile of index [param index].
|
||||
func set_index(cell_position: int, index: int) -> void:
|
||||
func set_index(cell: Cell, index: int) -> void:
|
||||
index = clampi(index, 0, tileset.tiles.size() - 1)
|
||||
var previous_index := cells[cell_position].index
|
||||
var previous_index := cell.index
|
||||
|
||||
if previous_index != index:
|
||||
if previous_index > 0 and previous_index < tileset.tiles.size():
|
||||
tileset.tiles[previous_index].times_used -= 1
|
||||
tileset.tiles[index].times_used += 1
|
||||
cells[cell_position].index = index
|
||||
cells[cell_position].flip_h = TileSetPanel.is_flipped_h
|
||||
cells[cell_position].flip_v = TileSetPanel.is_flipped_v
|
||||
cells[cell_position].transpose = TileSetPanel.is_transposed
|
||||
_update_cell(cell_position)
|
||||
cell.index = index
|
||||
cell.flip_h = TileSetPanel.is_flipped_h
|
||||
cell.flip_v = TileSetPanel.is_flipped_v
|
||||
cell.transpose = TileSetPanel.is_transposed
|
||||
_update_cell(cell)
|
||||
Global.canvas.queue_redraw()
|
||||
|
||||
|
||||
## Returns the pixel coordinates of the tilemap's cell
|
||||
## at position [cell_position] in the cel's image.
|
||||
## The reverse of [method get_cell_position].
|
||||
func get_cell_coords_in_image(cell_position: int) -> Vector2i:
|
||||
var x_coord := float(tileset.tile_size.x) * (cell_position % horizontal_cells)
|
||||
@warning_ignore("integer_division")
|
||||
var y_coord := float(tileset.tile_size.y) * (cell_position / horizontal_cells)
|
||||
return Vector2i(x_coord, y_coord)
|
||||
## Changes the [member offset] of the tilemap. Automatically resizes the cells and redraws the grid.
|
||||
func change_offset(new_offset: Vector2i) -> void:
|
||||
offset = new_offset
|
||||
_resize_cells(get_image().get_size(), false)
|
||||
Global.canvas.grid.queue_redraw()
|
||||
|
||||
|
||||
## Returns the [CelTileMap.Cell] at position [param cell_coords] in tilemap space.
|
||||
func get_cell_at(cell_coords: Vector2i) -> Cell:
|
||||
if not cells.has(cell_coords):
|
||||
cells[cell_coords] = Cell.new()
|
||||
return cells[cell_coords]
|
||||
|
||||
|
||||
## Returns the position of a cell in the tilemap
|
||||
## at pixel coordinates [param coords] in the cel's image.
|
||||
## The reverse of [method get_cell_coords_in_image].
|
||||
func get_cell_position(coords: Vector2i) -> int:
|
||||
@warning_ignore("integer_division")
|
||||
var x := coords.x / tileset.tile_size.x
|
||||
x = clampi(x, 0, horizontal_cells - 1)
|
||||
@warning_ignore("integer_division")
|
||||
var y := coords.y / tileset.tile_size.y
|
||||
y = clampi(y, 0, vertical_cells - 1)
|
||||
y *= horizontal_cells
|
||||
return x + y
|
||||
|
||||
|
||||
## Returns the position of a cell in the tilemap
|
||||
## at tilemap coordinates [param coords] in the cel's image.
|
||||
func get_cell_position_in_tilemap_space(coords: Vector2i) -> int:
|
||||
var x := coords.x
|
||||
x = clampi(x, 0, horizontal_cells - 1)
|
||||
var y := coords.y
|
||||
y = clampi(y, 0, vertical_cells - 1)
|
||||
y *= horizontal_cells
|
||||
return x + y
|
||||
func get_cell_position(coords: Vector2i) -> Vector2i:
|
||||
var offset_coords := coords - offset
|
||||
var x_pos := float(offset_coords.x) / tileset.tile_size.x
|
||||
var y_pos := float(offset_coords.y) / tileset.tile_size.y
|
||||
var cell_position := Vector2i(floori(x_pos), floori(y_pos))
|
||||
return cell_position
|
||||
|
||||
|
||||
## Returns the index of a cell in the tilemap
|
||||
## at pixel coordinates [param coords] in the cel's image.
|
||||
func get_cell_index_at_coords(coords: Vector2i) -> int:
|
||||
return cells[get_cell_position(coords)].index
|
||||
|
||||
|
||||
## Returns the index of a cell in the tilemap
|
||||
## at tilemap coordinates [param coords] in the cel's image.
|
||||
func get_cell_index_at_coords_in_tilemap_space(coords: Vector2i) -> int:
|
||||
return cells[get_cell_position_in_tilemap_space(coords)].index
|
||||
return get_cell_at(get_cell_position(coords)).index
|
||||
|
||||
|
||||
## Returns [code]true[/code] if the tile at cell position [param cell_position]
|
||||
## with image [param image_portion] is equal to [param tile_image].
|
||||
func _tiles_equal(cell_position: int, image_portion: Image, tile_image: Image) -> bool:
|
||||
var cell_data := cells[cell_position]
|
||||
var final_image_portion := transform_tile(
|
||||
tile_image, cell_data.flip_h, cell_data.flip_v, cell_data.transpose
|
||||
)
|
||||
func _tiles_equal(cell: Cell, image_portion: Image, tile_image: Image) -> bool:
|
||||
var final_image_portion := transform_tile(tile_image, cell.flip_h, cell.flip_v, cell.transpose)
|
||||
return image_portion.get_data() == final_image_portion.get_data()
|
||||
|
||||
|
||||
|
@ -191,7 +169,7 @@ func transform_tile(
|
|||
## Given a [param selection_map] and a [param selection_rect],
|
||||
## the method finds the cells that are currently selected and returns them
|
||||
## in the form of a 2D array that contains the serialiazed data
|
||||
##of the selected cells in the form of [Dictionary].
|
||||
## of the selected cells in the form of [Dictionary].
|
||||
func get_selected_cells(selection_map: SelectionMap, selection_rect: Rect2i) -> Array[Array]:
|
||||
var selected_cells: Array[Array] = []
|
||||
for x in range(0, selection_rect.size.x, tileset.tile_size.x):
|
||||
|
@ -278,17 +256,13 @@ func _resize_rows(
|
|||
resized_cells[y] = selected_cells[y_index]
|
||||
|
||||
|
||||
## Applies the [param selected_cells] data to [param target_image] data,
|
||||
## offset by [param selection_rect]. The target image needs to be resized first.
|
||||
## Applies the [param selected_cells] data to [param target_image] data.
|
||||
## The target image needs to be resized first.
|
||||
## This method is used when resizing a selection and draw tiles mode is enabled.
|
||||
func apply_resizing_to_image(
|
||||
target_image: Image, selected_cells: Array[Array], selection_rect: Rect2i
|
||||
) -> void:
|
||||
func apply_resizing_to_image(target_image: Image, selected_cells: Array[Array]) -> void:
|
||||
for x in selected_cells.size():
|
||||
for y in selected_cells[x].size():
|
||||
var pos := Vector2i(x, y) * tileset.tile_size + selection_rect.position
|
||||
var cell_pos := get_cell_position(pos)
|
||||
var coords := get_cell_coords_in_image(cell_pos) - selection_rect.position
|
||||
var coords := Vector2i(x, y) * tileset.tile_size
|
||||
var rect := Rect2i(coords, tileset.tile_size)
|
||||
var image_portion := target_image.get_region(rect)
|
||||
var cell_data := Cell.new()
|
||||
|
@ -310,11 +284,11 @@ func apply_resizing_to_image(
|
|||
## Appends data to a [Dictionary] to be used for undo/redo.
|
||||
func serialize_undo_data() -> Dictionary:
|
||||
var dict := {}
|
||||
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
|
||||
var cell_data := {}
|
||||
for cell_coords: Vector2i in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
cell_data[cell_coords] = cell.serialize()
|
||||
dict["cell_data"] = cell_data
|
||||
dict["tileset"] = tileset.serialize_undo_data()
|
||||
dict["resize"] = false
|
||||
return dict
|
||||
|
@ -341,21 +315,23 @@ func serialize_undo_data_source_image(
|
|||
|
||||
## Reads data from a [param dict] [Dictionary], and uses them to add methods to [param undo_redo].
|
||||
func deserialize_undo_data(dict: Dictionary, undo_redo: UndoRedo, undo: bool) -> void:
|
||||
var cell_indices = dict.cell_indices
|
||||
var cell_data = dict.cell_data
|
||||
if undo:
|
||||
undo_redo.add_undo_method(_deserialize_cell_data.bind(cell_indices, dict.resize))
|
||||
undo_redo.add_undo_method(_deserialize_cell_data.bind(cell_data, dict.resize))
|
||||
if dict.has("tileset"):
|
||||
undo_redo.add_undo_method(tileset.deserialize_undo_data.bind(dict.tileset, self))
|
||||
else:
|
||||
undo_redo.add_do_method(_deserialize_cell_data.bind(cell_indices, dict.resize))
|
||||
undo_redo.add_do_method(_deserialize_cell_data.bind(cell_data, dict.resize))
|
||||
if dict.has("tileset"):
|
||||
undo_redo.add_do_method(tileset.deserialize_undo_data.bind(dict.tileset, self))
|
||||
|
||||
|
||||
## Called when loading a new project. Loops through all [member cells]
|
||||
## and finds the amount of times each tile from the [member tileset] is being used.
|
||||
## Called when loading a new project, or when [method set_content] is called.
|
||||
## Loops through all [member cells] and finds the amount of times
|
||||
## each tile from the [member tileset] is being used.
|
||||
func find_times_used_of_tiles() -> void:
|
||||
for cell in cells:
|
||||
for cell_coords in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
tileset.tiles[cell.index].times_used += 1
|
||||
|
||||
|
||||
|
@ -371,11 +347,12 @@ func update_tilemap(
|
|||
) -> void:
|
||||
editing_images.clear()
|
||||
var tileset_size_before_update := tileset.tiles.size()
|
||||
for i in cells.size():
|
||||
var coords := get_cell_coords_in_image(i)
|
||||
for cell_coords: Vector2i in cells:
|
||||
var cell := get_cell_at(cell_coords)
|
||||
var coords := cell_coords * tileset.tile_size + offset
|
||||
var rect := Rect2i(coords, tileset.tile_size)
|
||||
var image_portion := source_image.get_region(rect)
|
||||
var index := cells[i].index
|
||||
var index := cell.index
|
||||
if index >= tileset.tiles.size():
|
||||
index = 0
|
||||
var current_tile := tileset.tiles[index]
|
||||
|
@ -386,49 +363,50 @@ func update_tilemap(
|
|||
# If the tileset is empty, only then add a new tile.
|
||||
if tileset.tiles.size() <= 1:
|
||||
tileset.add_tile(image_portion, self)
|
||||
cells[i].index = tileset.tiles.size() - 1
|
||||
cell.index = tileset.tiles.size() - 1
|
||||
continue
|
||||
if not _tiles_equal(i, image_portion, current_tile.image):
|
||||
if not _tiles_equal(cell, image_portion, current_tile.image):
|
||||
tileset.replace_tile_at(image_portion, index, self)
|
||||
elif tile_editing_mode == TileSetPanel.TileEditingMode.AUTO:
|
||||
_handle_auto_editing_mode(i, image_portion, tileset_size_before_update)
|
||||
_handle_auto_editing_mode(cell, image_portion, tileset_size_before_update)
|
||||
else: # Stack
|
||||
if image_portion.is_invisible():
|
||||
continue
|
||||
var found_tile := false
|
||||
for j in range(1, tileset.tiles.size()):
|
||||
var tile := tileset.tiles[j]
|
||||
if _tiles_equal(i, image_portion, tile.image):
|
||||
if cells[i].index != j:
|
||||
tileset.tiles[cells[i].index].times_used -= 1
|
||||
cells[i].index = j
|
||||
if _tiles_equal(cell, image_portion, tile.image):
|
||||
if cell.index != j:
|
||||
tileset.tiles[cell.index].times_used -= 1
|
||||
cell.index = j
|
||||
tileset.tiles[j].times_used += 1
|
||||
cells[i].remove_transformations()
|
||||
cell.remove_transformations()
|
||||
found_tile = true
|
||||
break
|
||||
if not found_tile:
|
||||
if cells[i].index > 0:
|
||||
tileset.tiles[cells[i].index].times_used -= 1
|
||||
if cell.index > 0:
|
||||
tileset.tiles[cell.index].times_used -= 1
|
||||
tileset.add_tile(image_portion, self)
|
||||
cells[i].index = tileset.tiles.size() - 1
|
||||
cells[i].remove_transformations()
|
||||
cell.index = tileset.tiles.size() - 1
|
||||
cell.remove_transformations()
|
||||
# Updates transparent cells that have indices higher than 0.
|
||||
# This can happen when switching to another tileset which has less tiles
|
||||
# than the previous one.
|
||||
for i in cells.size():
|
||||
var coords := get_cell_coords_in_image(i)
|
||||
for cell_coords: Vector2i in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
var coords := cell_coords * tileset.tile_size + offset
|
||||
var rect := Rect2i(coords, tileset.tile_size)
|
||||
var image_portion := source_image.get_region(rect)
|
||||
if not image_portion.is_invisible():
|
||||
continue
|
||||
var index := cells[i].index
|
||||
var index := cell.index
|
||||
if index == 0:
|
||||
continue
|
||||
if index >= tileset.tiles.size():
|
||||
index = 0
|
||||
var current_tile := tileset.tiles[index]
|
||||
if not _tiles_equal(i, image_portion, current_tile.image):
|
||||
set_index(i, cells[i].index)
|
||||
if not _tiles_equal(cell, image_portion, current_tile.image):
|
||||
set_index(cell, cell.index)
|
||||
|
||||
|
||||
## Gets called by [method update_tilemap]. This method is responsible for handling
|
||||
|
@ -470,18 +448,18 @@ func update_tilemap(
|
|||
## 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, tileset_size_before_update: int
|
||||
cell: Cell, image_portion: Image, tileset_size_before_update: int
|
||||
) -> void:
|
||||
var index := cells[i].index
|
||||
var index := cell.index
|
||||
if index >= tileset.tiles.size():
|
||||
index = 0
|
||||
var current_tile := tileset.tiles[index]
|
||||
if image_portion.is_invisible():
|
||||
# Case 0: The cell is transparent.
|
||||
if cells[i].index >= tileset_size_before_update:
|
||||
if cell.index >= tileset_size_before_update:
|
||||
return
|
||||
cells[i].index = 0
|
||||
cells[i].remove_transformations()
|
||||
cell.index = 0
|
||||
cell.remove_transformations()
|
||||
if index > 0:
|
||||
# Case 0.5: The cell is transparent and mapped to a tile.
|
||||
var is_removed := tileset.unuse_tile_at_index(index, self)
|
||||
|
@ -495,14 +473,14 @@ func _handle_auto_editing_mode(
|
|||
# Case 1: The cell is not mapped already,
|
||||
# and it exists in the tileset as a tile.
|
||||
tileset.tiles[index_in_tileset].times_used += 1
|
||||
cells[i].index = index_in_tileset
|
||||
cell.index = index_in_tileset
|
||||
else:
|
||||
# Case 2: The cell is not mapped already,
|
||||
# and it does not exist in the tileset.
|
||||
tileset.add_tile(image_portion, self)
|
||||
cells[i].index = tileset.tiles.size() - 1
|
||||
cell.index = tileset.tiles.size() - 1
|
||||
else: # If the cell is already mapped.
|
||||
if _tiles_equal(i, image_portion, current_tile.image):
|
||||
if _tiles_equal(cell, image_portion, current_tile.image):
|
||||
# Case 3: The cell is mapped and it did not change.
|
||||
# Do nothing and move on to the next cell.
|
||||
return
|
||||
|
@ -511,13 +489,13 @@ func _handle_auto_editing_mode(
|
|||
# Case 4: The cell 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
|
||||
cells[i].index = index_in_tileset
|
||||
cell.index = index_in_tileset
|
||||
tileset.unuse_tile_at_index(index, self)
|
||||
else:
|
||||
# Case 5: The cell 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
|
||||
cells[i].index = index_in_tileset
|
||||
cell.index = index_in_tileset
|
||||
tileset.remove_tile_at_index(index, self)
|
||||
# Re-index all indices that are after the deleted one.
|
||||
_re_index_cells_after_index(index)
|
||||
|
@ -528,38 +506,37 @@ func _handle_auto_editing_mode(
|
|||
# and the currently mapped tile still exists in the tileset.
|
||||
tileset.unuse_tile_at_index(index, self)
|
||||
tileset.add_tile(image_portion, self)
|
||||
cells[i].index = tileset.tiles.size() - 1
|
||||
cell.index = tileset.tiles.size() - 1
|
||||
else:
|
||||
# Case 7: The cell 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, self)
|
||||
cells[i].remove_transformations()
|
||||
cell.remove_transformations()
|
||||
|
||||
|
||||
## Re-indexes all [member cells] that are larger or equal to [param index],
|
||||
## by reducing their value by one.
|
||||
func _re_index_cells_after_index(index: int) -> void:
|
||||
for i in cells.size():
|
||||
var tmp_index := cells[i].index
|
||||
for cell_coords: Vector2i in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
var tmp_index := cell.index
|
||||
if tmp_index >= index:
|
||||
cells[i].index -= 1
|
||||
cell.index -= 1
|
||||
|
||||
|
||||
## Updates the [member image] data of the cell of the tilemap in [param cell_position],
|
||||
## to ensure that it is the same as its mapped tile in the [member tileset].
|
||||
func _update_cell(cell_position: int) -> void:
|
||||
var coords := get_cell_coords_in_image(cell_position)
|
||||
func _update_cell(cell: Cell) -> void:
|
||||
var cell_coords := cells.find_key(cell) as Vector2i
|
||||
var coords := cell_coords * tileset.tile_size + offset
|
||||
var rect := Rect2i(coords, tileset.tile_size)
|
||||
var image_portion := image.get_region(rect)
|
||||
var cell_data := cells[cell_position]
|
||||
var index := cell_data.index
|
||||
var index := cell.index
|
||||
if index >= tileset.tiles.size():
|
||||
index = 0
|
||||
var current_tile := tileset.tiles[index].image
|
||||
var transformed_tile := transform_tile(
|
||||
current_tile, cell_data.flip_h, cell_data.flip_v, cell_data.transpose
|
||||
)
|
||||
var transformed_tile := transform_tile(current_tile, cell.flip_h, cell.flip_v, cell.transpose)
|
||||
if image_portion.get_data() != transformed_tile.get_data():
|
||||
var tile_size := transformed_tile.get_size()
|
||||
image.blit_rect(transformed_tile, Rect2i(Vector2i.ZERO, tile_size), coords)
|
||||
|
@ -568,45 +545,65 @@ func _update_cell(cell_position: int) -> void:
|
|||
|
||||
## Calls [method _update_cell] for all [member cells].
|
||||
func update_cel_portions() -> void:
|
||||
for i in cells.size():
|
||||
_update_cell(i)
|
||||
for cell_coords: Vector2i in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
_update_cell(cell)
|
||||
|
||||
|
||||
## Loops through all [member cells] of the tilemap and updates their indices,
|
||||
## so they can remain mapped to the [member tileset]'s tiles.
|
||||
func re_index_all_cells(set_invisible_to_zero := false) -> void:
|
||||
for i in cells.size():
|
||||
var coords := get_cell_coords_in_image(i)
|
||||
for cell_coords: Vector2i in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
var coords := cell_coords * tileset.tile_size + offset
|
||||
var rect := Rect2i(coords, tileset.tile_size)
|
||||
var image_portion := image.get_region(rect)
|
||||
if image_portion.is_invisible():
|
||||
if set_invisible_to_zero:
|
||||
cells[i].index = 0
|
||||
cell.index = 0
|
||||
continue
|
||||
var index := cells[i].index
|
||||
var index := cell.index
|
||||
if index > 0 and index < tileset.tiles.size():
|
||||
var current_tile := tileset.tiles[index]
|
||||
if not _tiles_equal(i, image_portion, current_tile.image):
|
||||
set_index(i, cells[i].index)
|
||||
if not _tiles_equal(cell, image_portion, current_tile.image):
|
||||
set_index(cell, cell.index)
|
||||
continue
|
||||
for j in range(1, tileset.tiles.size()):
|
||||
var tile := tileset.tiles[j]
|
||||
if _tiles_equal(i, image_portion, tile.image):
|
||||
cells[i].index = j
|
||||
if _tiles_equal(cell, image_portion, tile.image):
|
||||
cell.index = j
|
||||
break
|
||||
|
||||
|
||||
## Resizes the [member cells] array based on [param new_size].
|
||||
func _resize_cells(new_size: Vector2i, reset_indices := true) -> void:
|
||||
horizontal_cells = ceili(float(new_size.x) / tileset.tile_size.x)
|
||||
vertical_cells = ceili(float(new_size.y) / tileset.tile_size.y)
|
||||
cells.resize(horizontal_cells * vertical_cells)
|
||||
for i in cells.size():
|
||||
var horizontal_cells := ceili(float(new_size.x) / tileset.tile_size.x)
|
||||
var vertical_cells := ceili(float(new_size.y) / tileset.tile_size.y)
|
||||
if offset.x % tileset.tile_size.x != 0:
|
||||
horizontal_cells += 1
|
||||
if offset.y % tileset.tile_size.y != 0:
|
||||
vertical_cells += 1
|
||||
var offset_in_tiles := Vector2i((Vector2(offset) / Vector2(tileset.tile_size)).ceil())
|
||||
for x in horizontal_cells:
|
||||
for y in vertical_cells:
|
||||
var cell_coords := Vector2i(x, y) - offset_in_tiles
|
||||
if not cells.has(cell_coords):
|
||||
cells[cell_coords] = Cell.new()
|
||||
for cell_coords: Vector2i in cells:
|
||||
if cell_coords.y < vertical_cell_min:
|
||||
vertical_cell_min = cell_coords.y
|
||||
if cell_coords.y > vertical_cell_max:
|
||||
vertical_cell_max = cell_coords.y
|
||||
if not is_zero_approx(fposmod(offset.x, tileset.tile_size.x)):
|
||||
horizontal_cells += 1
|
||||
if not is_zero_approx(fposmod(offset.y, tileset.tile_size.y)):
|
||||
vertical_cells += 1
|
||||
for cell_coords: Vector2i in cells:
|
||||
if reset_indices:
|
||||
cells[i] = Cell.new()
|
||||
cells[cell_coords] = Cell.new()
|
||||
else:
|
||||
if not is_instance_valid(cells[i]):
|
||||
cells[i] = Cell.new()
|
||||
if not is_instance_valid(cells[cell_coords]):
|
||||
cells[cell_coords] = Cell.new()
|
||||
|
||||
|
||||
## Returns [code]true[/code] if the user just did a Redo.
|
||||
|
@ -633,34 +630,44 @@ func _on_tileset_updated(cel: CelTileMap, replace_index: int) -> void:
|
|||
Global.canvas.queue_redraw()
|
||||
|
||||
|
||||
func _deserialize_cell_data(cell_indices: Array, resize: bool) -> void:
|
||||
func _deserialize_cell_data(cell_data: Dictionary, resize: bool) -> void:
|
||||
if resize:
|
||||
_resize_cells(image.get_size())
|
||||
for i in cell_indices.size():
|
||||
var cell_data: Dictionary = cell_indices[i]
|
||||
cells[i].deserialize(cell_data)
|
||||
for cell_coords in cell_data:
|
||||
var cell_data_serialized: Dictionary = cell_data[cell_coords]
|
||||
get_cell_at(cell_coords).deserialize(cell_data_serialized)
|
||||
|
||||
|
||||
# Overridden Methods:
|
||||
func set_content(content, texture: ImageTexture = null) -> void:
|
||||
for cell_coords in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
if cell.index > 0:
|
||||
tileset.tiles[cell.index].times_used -= 1
|
||||
super.set_content(content, texture)
|
||||
_resize_cells(image.get_size())
|
||||
re_index_all_cells()
|
||||
find_times_used_of_tiles()
|
||||
|
||||
|
||||
func update_texture(undo := false) -> void:
|
||||
var tile_editing_mode := TileSetPanel.tile_editing_mode
|
||||
if undo or _is_redo() or tile_editing_mode != TileSetPanel.TileEditingMode.MANUAL:
|
||||
if (
|
||||
undo
|
||||
or _is_redo()
|
||||
or tile_editing_mode != TileSetPanel.TileEditingMode.MANUAL
|
||||
or Tools.is_placing_tiles()
|
||||
):
|
||||
super.update_texture(undo)
|
||||
editing_images.clear()
|
||||
return
|
||||
|
||||
for i in cells.size():
|
||||
var cell_data := cells[i]
|
||||
var index := cell_data.index
|
||||
for cell_coords: Vector2i in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
var coords := cell_coords * tileset.tile_size + offset
|
||||
var index := cell.index
|
||||
if index >= tileset.tiles.size():
|
||||
index = 0
|
||||
var coords := get_cell_coords_in_image(i)
|
||||
var rect := Rect2i(coords, tileset.tile_size)
|
||||
var image_portion := image.get_region(rect)
|
||||
var current_tile := tileset.tiles[index]
|
||||
|
@ -671,30 +678,30 @@ func update_texture(undo := false) -> void:
|
|||
image.blit_rect(current_tile.image, Rect2i(Vector2i.ZERO, tile_size), coords)
|
||||
continue
|
||||
if not editing_images.has(index):
|
||||
if not _tiles_equal(i, image_portion, current_tile.image):
|
||||
if not _tiles_equal(cell, image_portion, current_tile.image):
|
||||
var transformed_image := transform_tile(
|
||||
image_portion, cell_data.flip_h, cell_data.flip_v, cell_data.transpose, true
|
||||
image_portion, cell.flip_h, cell.flip_v, cell.transpose, true
|
||||
)
|
||||
editing_images[index] = [i, transformed_image]
|
||||
editing_images[index] = [cell_coords, transformed_image]
|
||||
|
||||
for i in cells.size():
|
||||
var cell_data := cells[i]
|
||||
var index := cell_data.index
|
||||
for cell_coords: Vector2i in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
var coords := cell_coords * tileset.tile_size + offset
|
||||
var index := cell.index
|
||||
if index >= tileset.tiles.size():
|
||||
index = 0
|
||||
var coords := get_cell_coords_in_image(i)
|
||||
var rect := Rect2i(coords, tileset.tile_size)
|
||||
var image_portion := image.get_region(rect)
|
||||
if editing_images.has(index):
|
||||
var editing_portion := editing_images[index][0] as int
|
||||
if i == editing_portion:
|
||||
var editing_portion := editing_images[index][0] as Vector2i
|
||||
if cell_coords == editing_portion:
|
||||
var transformed_image := transform_tile(
|
||||
image_portion, cell_data.flip_h, cell_data.flip_v, cell_data.transpose, true
|
||||
image_portion, cell.flip_h, cell.flip_v, cell.transpose, true
|
||||
)
|
||||
editing_images[index] = [i, transformed_image]
|
||||
editing_images[index] = [cell_coords, transformed_image]
|
||||
var editing_image := editing_images[index][1] as Image
|
||||
var transformed_editing_image := transform_tile(
|
||||
editing_image, cell_data.flip_h, cell_data.flip_v, cell_data.transpose
|
||||
editing_image, cell.flip_h, cell.flip_v, cell.transpose
|
||||
)
|
||||
if not image_portion.get_data() == transformed_editing_image.get_data():
|
||||
var tile_size := image_portion.get_size()
|
||||
|
@ -704,19 +711,26 @@ func update_texture(undo := false) -> void:
|
|||
|
||||
func serialize() -> Dictionary:
|
||||
var dict := super.serialize()
|
||||
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
|
||||
var cell_data := {}
|
||||
for cell_coords: Vector2i in cells:
|
||||
var cell := cells[cell_coords] as Cell
|
||||
cell_data[cell_coords] = cell.serialize()
|
||||
dict["cell_data"] = cell_data
|
||||
dict["offset"] = offset
|
||||
return dict
|
||||
|
||||
|
||||
func deserialize(dict: Dictionary) -> void:
|
||||
super.deserialize(dict)
|
||||
var cell_indices = dict.get("cell_indices")
|
||||
for i in cell_indices.size():
|
||||
cells[i].deserialize(cell_indices[i])
|
||||
var cell_data = dict.get("cell_data", [])
|
||||
for cell_coords_str in cell_data:
|
||||
var cell_data_serialized: Dictionary = cell_data[cell_coords_str]
|
||||
var cell_coords := str_to_var("Vector2i" + cell_coords_str) as Vector2i
|
||||
get_cell_at(cell_coords).deserialize(cell_data_serialized)
|
||||
var new_offset_str = dict.get("offset", "Vector2i(0, 0)")
|
||||
var new_offset := str_to_var("Vector2i" + new_offset_str) as Vector2i
|
||||
if new_offset != offset:
|
||||
change_offset(new_offset)
|
||||
|
||||
|
||||
func get_class_name() -> String:
|
||||
|
|
|
@ -326,7 +326,7 @@ func draw_end(pos: Vector2i) -> void:
|
|||
func draw_tile(pos: Vector2i) -> void:
|
||||
var tile_index := 0 if _is_eraser else TileSetPanel.selected_tile_index
|
||||
var mirrored_positions := Tools.get_mirrored_positions(pos, Global.current_project)
|
||||
var tile_positions := PackedInt32Array()
|
||||
var tile_positions: Array[Vector2i] = []
|
||||
tile_positions.resize(mirrored_positions.size() + 1)
|
||||
tile_positions[0] = get_cell_position(pos)
|
||||
for i in mirrored_positions.size():
|
||||
|
@ -336,7 +336,8 @@ func draw_tile(pos: Vector2i) -> void:
|
|||
if cel is not CelTileMap:
|
||||
return
|
||||
for tile_position in tile_positions:
|
||||
(cel as CelTileMap).set_index(tile_position, tile_index)
|
||||
var cell := (cel as CelTileMap).get_cell_at(tile_position)
|
||||
(cel as CelTileMap).set_index(cell, tile_index)
|
||||
|
||||
|
||||
func _prepare_tool() -> void:
|
||||
|
@ -525,11 +526,13 @@ func draw_indicator(left: bool) -> void:
|
|||
var color := Global.left_tool_color if left else Global.right_tool_color
|
||||
var snapped_position := snap_position(_cursor)
|
||||
if Tools.is_placing_tiles():
|
||||
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
|
||||
var tilemap_cel := Global.current_project.get_current_cel() as CelTileMap
|
||||
var tileset := tilemap_cel.tileset
|
||||
var grid_size := tileset.tile_size
|
||||
snapped_position = _snap_to_rectangular_grid_center(
|
||||
snapped_position, grid_size, Vector2i.ZERO, -1
|
||||
)
|
||||
var offset := tilemap_cel.offset % grid_size
|
||||
var offset_pos := snapped_position - Vector2(grid_size / 2) - Vector2(offset)
|
||||
var grid_center := offset_pos.snapped(grid_size) + Vector2(grid_size / 2) + Vector2(offset)
|
||||
snapped_position = grid_center.floor()
|
||||
draw_indicator_at(snapped_position, Vector2i.ZERO, color)
|
||||
if (
|
||||
Global.current_project.has_selection
|
||||
|
|
|
@ -153,9 +153,11 @@ func draw_move(pos: Vector2i) -> void:
|
|||
return
|
||||
|
||||
if Tools.is_placing_tiles():
|
||||
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
|
||||
var cel := Global.current_project.get_current_cel() as CelTileMap
|
||||
var tileset := cel.tileset
|
||||
var grid_size := tileset.tile_size
|
||||
pos = Tools.snap_to_rectangular_grid_boundary(pos, grid_size)
|
||||
var offset := cel.offset % grid_size
|
||||
pos = Tools.snap_to_rectangular_grid_boundary(pos, grid_size, offset)
|
||||
if Input.is_action_pressed("transform_snap_axis"): # Snap to axis
|
||||
var angle := Vector2(pos).angle_to_point(_start_pos)
|
||||
if absf(angle) <= PI / 4 or absf(angle) >= 3 * PI / 4:
|
||||
|
@ -216,9 +218,9 @@ func apply_selection(_position: Vector2i) -> void:
|
|||
|
||||
|
||||
func select_tilemap_cell(
|
||||
cel: CelTileMap, cell_position: int, selection: SelectionMap, select: bool
|
||||
cel: CelTileMap, cell_position: Vector2i, selection: SelectionMap, select: bool
|
||||
) -> void:
|
||||
var rect := Rect2i(cel.get_cell_coords_in_image(cell_position), cel.tileset.tile_size)
|
||||
var rect := Rect2i(cell_position + cel.offset, cel.tileset.tile_size)
|
||||
selection.select_rect(rect, select)
|
||||
|
||||
|
||||
|
|
|
@ -79,8 +79,8 @@ func draw_end(_pos: Vector2i) -> void:
|
|||
project.can_undo = true
|
||||
|
||||
|
||||
func get_cell_position(pos: Vector2i) -> int:
|
||||
var tile_pos := 0
|
||||
func get_cell_position(pos: Vector2i) -> Vector2i:
|
||||
var tile_pos := Vector2i.ZERO
|
||||
if Global.current_project.get_current_cel() is not CelTileMap:
|
||||
return tile_pos
|
||||
var cel := Global.current_project.get_current_cel() as CelTileMap
|
||||
|
|
|
@ -187,8 +187,8 @@ func draw_end(pos: Vector2i) -> void:
|
|||
|
||||
|
||||
func draw_tile(pos: Vector2i, cel: CelTileMap) -> void:
|
||||
var tile_position := get_cell_position(pos)
|
||||
cel.set_index(tile_position, TileSetPanel.selected_tile_index)
|
||||
var cell := cel.get_cell_at(pos)
|
||||
cel.set_index(cell, TileSetPanel.selected_tile_index)
|
||||
|
||||
|
||||
func fill(pos: Vector2i) -> void:
|
||||
|
@ -210,10 +210,10 @@ func fill_in_color(pos: Vector2i) -> void:
|
|||
continue
|
||||
var tilemap_cel := cel as CelTileMap
|
||||
var tile_index := tilemap_cel.get_cell_index_at_coords(pos)
|
||||
for i in tilemap_cel.cells.size():
|
||||
var cell := tilemap_cel.cells[i]
|
||||
for cell_coords: Vector2i in tilemap_cel.cells:
|
||||
var cell := tilemap_cel.get_cell_at(cell_coords)
|
||||
if cell.index == tile_index:
|
||||
tilemap_cel.set_index(i, TileSetPanel.selected_tile_index)
|
||||
tilemap_cel.set_index(cell, TileSetPanel.selected_tile_index)
|
||||
return
|
||||
var color := project.get_current_cel().get_image().get_pixelv(pos)
|
||||
var images := _get_selected_draw_images()
|
||||
|
@ -335,11 +335,12 @@ func _flood_fill(pos: Vector2i) -> void:
|
|||
for cel in _get_selected_draw_cels():
|
||||
if cel is not CelTileMap:
|
||||
continue
|
||||
var tile_index := (cel as CelTileMap).get_cell_index_at_coords(pos)
|
||||
var cell_pos := (cel as CelTileMap).get_cell_position(pos)
|
||||
var tile_index := (cel as CelTileMap).get_cell_at(cell_pos).index
|
||||
# init flood data structures
|
||||
_allegro_flood_segments = []
|
||||
_allegro_image_segments = []
|
||||
_compute_segments_for_tilemap(pos, cel, tile_index)
|
||||
_compute_segments_for_tilemap(cell_pos, cel, tile_index)
|
||||
_color_segments_tilemap(cel)
|
||||
return
|
||||
|
||||
|
@ -523,7 +524,7 @@ func _set_pixel_pattern(image: ImageExtended, x: int, y: int, pattern_size: Vect
|
|||
|
||||
func _compute_segments_for_tilemap(pos: Vector2i, cel: CelTileMap, src_index: int) -> void:
|
||||
# initially allocate at least 1 segment per line of the tilemap
|
||||
for j in cel.vertical_cells:
|
||||
for j in range(cel.vertical_cell_min, cel.vertical_cell_max):
|
||||
_add_new_segment(j)
|
||||
pos /= cel.tileset.tile_size
|
||||
# start flood algorithm
|
||||
|
@ -555,16 +556,13 @@ func _compute_segments_for_tilemap(pos: Vector2i, cel: CelTileMap, src_index: in
|
|||
## Τhis method is called by [method _flood_fill] after the required data structures
|
||||
## have been initialized.
|
||||
func _flood_line_around_point_tilemap(pos: Vector2i, cel: CelTileMap, src_index: int) -> int:
|
||||
if cel.get_cell_index_at_coords_in_tilemap_space(pos) != src_index:
|
||||
if cel.get_cell_at(pos).index != src_index:
|
||||
return pos.x + 1
|
||||
var west := pos
|
||||
var east := pos
|
||||
while west.x >= 0 && cel.get_cell_index_at_coords_in_tilemap_space(west) == src_index:
|
||||
while cel.cells.has(west) && cel.get_cell_at(west).index == src_index:
|
||||
west += Vector2i.LEFT
|
||||
while (
|
||||
east.x < cel.horizontal_cells
|
||||
&& cel.get_cell_index_at_coords_in_tilemap_space(east) == src_index
|
||||
):
|
||||
while cel.cells.has(east) && cel.get_cell_at(east).index == src_index:
|
||||
east += Vector2i.RIGHT
|
||||
# Make a note of the stuff we processed
|
||||
var c := pos.y
|
||||
|
@ -592,8 +590,8 @@ func _flood_line_around_point_tilemap(pos: Vector2i, cel: CelTileMap, src_index:
|
|||
# test will be performed later anyway.
|
||||
# On the other hand, this test we described is the same `project.can_pixel_get_drawn` does if
|
||||
# there is no selection, so we don't need branching here.
|
||||
segment.todo_above = pos.y > 0
|
||||
segment.todo_below = pos.y < cel.vertical_cells - 1
|
||||
segment.todo_above = pos.y > cel.vertical_cell_min
|
||||
segment.todo_below = pos.y < cel.vertical_cell_max
|
||||
# this is an actual segment we should be coloring, so we add it to the results for the
|
||||
# current image
|
||||
if segment.right_position >= segment.left_position:
|
||||
|
@ -627,7 +625,7 @@ func _color_segments_tilemap(cel: CelTileMap) -> void:
|
|||
for c in _allegro_image_segments.size():
|
||||
var p := _allegro_image_segments[c]
|
||||
for px in range(p.left_position, p.right_position + 1):
|
||||
draw_tile(Vector2i(px, p.y) * cel.tileset.tile_size, cel)
|
||||
draw_tile(Vector2i(px, p.y), cel)
|
||||
|
||||
|
||||
func commit_undo() -> void:
|
||||
|
|
|
@ -50,16 +50,16 @@ func apply_selection(pos: Vector2i) -> void:
|
|||
continue
|
||||
var tilemap_cel := cel as CelTileMap
|
||||
var tile_index := tilemap_cel.get_cell_index_at_coords(pos)
|
||||
for i in tilemap_cel.cells.size():
|
||||
var cell := tilemap_cel.cells[i]
|
||||
for cell_coords: Vector2i in tilemap_cel.cells:
|
||||
var cell := tilemap_cel.get_cell_at(cell_coords)
|
||||
var p := cell_coords * tilemap_cel.tileset.tile_size
|
||||
if cell.index == tile_index:
|
||||
if _intersect:
|
||||
var p := (cel as CelTileMap).get_cell_coords_in_image(i)
|
||||
select_tilemap_cell(
|
||||
cel, i, project.selection_map, prev_selection_map.is_pixel_selected(p)
|
||||
cel, p, project.selection_map, prev_selection_map.is_pixel_selected(p)
|
||||
)
|
||||
else:
|
||||
select_tilemap_cell(cel, i, project.selection_map, !_subtract)
|
||||
select_tilemap_cell(cel, p, project.selection_map, !_subtract)
|
||||
else:
|
||||
var cel_image := Image.new()
|
||||
cel_image.copy_from(_get_draw_image())
|
||||
|
|
|
@ -134,10 +134,12 @@ func set_ellipse(selection_map: SelectionMap, pos: Vector2i) -> void:
|
|||
# where the shape will be drawn and what is its size
|
||||
func _get_result_rect(origin: Vector2i, dest: Vector2i) -> Rect2i:
|
||||
if Tools.is_placing_tiles():
|
||||
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
|
||||
var cel := Global.current_project.get_current_cel() as CelTileMap
|
||||
var tileset := cel.tileset
|
||||
var grid_size := tileset.tile_size
|
||||
origin = Tools.snap_to_rectangular_grid_boundary(origin, grid_size)
|
||||
dest = Tools.snap_to_rectangular_grid_boundary(dest, grid_size)
|
||||
var offset := cel.offset % grid_size
|
||||
origin = Tools.snap_to_rectangular_grid_boundary(origin, grid_size, offset)
|
||||
dest = Tools.snap_to_rectangular_grid_boundary(dest, grid_size, offset)
|
||||
var rect := Rect2i()
|
||||
# Center the rect on the mouse
|
||||
if _expand_from_center:
|
||||
|
|
|
@ -115,7 +115,7 @@ func lasso_selection(
|
|||
func select_pixel(point: Vector2i, project: Project, select: bool) -> void:
|
||||
if Tools.is_placing_tiles():
|
||||
var tilemap := project.get_current_cel() as CelTileMap
|
||||
var cell_position := tilemap.get_cell_position(point)
|
||||
var cell_position := tilemap.get_cell_position(point) * tilemap.tileset.tile_size
|
||||
select_tilemap_cell(tilemap, cell_position, project.selection_map, select)
|
||||
project.selection_map.select_pixel(point, select)
|
||||
|
||||
|
|
|
@ -75,11 +75,12 @@ func _flood_fill(
|
|||
for cel in _get_selected_draw_cels():
|
||||
if cel is not CelTileMap:
|
||||
continue
|
||||
var tile_index := (cel as CelTileMap).get_cell_index_at_coords(pos)
|
||||
var cell_pos := (cel as CelTileMap).get_cell_position(pos)
|
||||
var tile_index := (cel as CelTileMap).get_cell_at(cell_pos).index
|
||||
# init flood data structures
|
||||
_allegro_flood_segments = []
|
||||
_allegro_image_segments = []
|
||||
_compute_segments_for_tilemap(pos, cel, tile_index)
|
||||
_compute_segments_for_tilemap(cell_pos, cel, tile_index)
|
||||
_select_segments_tilemap(project, previous_selection_map)
|
||||
return
|
||||
var color := image.get_pixelv(pos)
|
||||
|
@ -220,9 +221,8 @@ func _set_bit(p: Vector2i, selection_map: SelectionMap, prev_selection_map: Sele
|
|||
|
||||
func _compute_segments_for_tilemap(pos: Vector2i, cel: CelTileMap, src_index: int) -> void:
|
||||
# initially allocate at least 1 segment per line of the tilemap
|
||||
for j in cel.vertical_cells:
|
||||
for j in range(cel.vertical_cell_min, cel.vertical_cell_max):
|
||||
_add_new_segment(j)
|
||||
pos /= cel.tileset.tile_size
|
||||
# start flood algorithm
|
||||
_flood_line_around_point_tilemap(pos, cel, src_index)
|
||||
# test all segments while also discovering more
|
||||
|
@ -252,16 +252,13 @@ func _compute_segments_for_tilemap(pos: Vector2i, cel: CelTileMap, src_index: in
|
|||
## Τhis method is called by [method _flood_fill] after the required data structures
|
||||
## have been initialized.
|
||||
func _flood_line_around_point_tilemap(pos: Vector2i, cel: CelTileMap, src_index: int) -> int:
|
||||
if cel.get_cell_index_at_coords_in_tilemap_space(pos) != src_index:
|
||||
if cel.get_cell_at(pos).index != src_index:
|
||||
return pos.x + 1
|
||||
var west := pos
|
||||
var east := pos
|
||||
while west.x >= 0 && cel.get_cell_index_at_coords_in_tilemap_space(west) == src_index:
|
||||
while cel.cells.has(west) && cel.get_cell_at(west).index == src_index:
|
||||
west += Vector2i.LEFT
|
||||
while (
|
||||
east.x < cel.horizontal_cells
|
||||
&& cel.get_cell_index_at_coords_in_tilemap_space(east) == src_index
|
||||
):
|
||||
while cel.cells.has(east) && cel.get_cell_at(east).index == src_index:
|
||||
east += Vector2i.RIGHT
|
||||
# Make a note of the stuff we processed
|
||||
var c := pos.y
|
||||
|
@ -289,8 +286,8 @@ func _flood_line_around_point_tilemap(pos: Vector2i, cel: CelTileMap, src_index:
|
|||
# test will be performed later anyway.
|
||||
# On the other hand, this test we described is the same `project.can_pixel_get_drawn` does if
|
||||
# there is no selection, so we don't need branching here.
|
||||
segment.todo_above = pos.y > 0
|
||||
segment.todo_below = pos.y < cel.vertical_cells - 1
|
||||
segment.todo_above = pos.y > cel.vertical_cell_min
|
||||
segment.todo_below = pos.y < cel.vertical_cell_max
|
||||
# this is an actual segment we should be coloring, so we add it to the results for the
|
||||
# current image
|
||||
if segment.right_position >= segment.left_position:
|
||||
|
@ -332,14 +329,10 @@ func _select_segments_tilemap(project: Project, previous_selection_map: Selectio
|
|||
func _set_bit_rect(p: Vector2i, project: Project, prev_selection_map: SelectionMap) -> void:
|
||||
var selection_map := project.selection_map
|
||||
var tilemap := project.get_current_cel() as CelTileMap
|
||||
var cell_position := tilemap.get_cell_position_in_tilemap_space(p)
|
||||
var pixel_coords := p * tilemap.tileset.tile_size
|
||||
if _intersect:
|
||||
var image_coords := tilemap.get_cell_coords_in_image(cell_position)
|
||||
select_tilemap_cell(
|
||||
tilemap,
|
||||
cell_position,
|
||||
project.selection_map,
|
||||
prev_selection_map.is_pixel_selected(image_coords)
|
||||
tilemap, pixel_coords, project.selection_map, prev_selection_map.is_pixel_selected(p)
|
||||
)
|
||||
else:
|
||||
select_tilemap_cell(tilemap, cell_position, project.selection_map, !_subtract)
|
||||
select_tilemap_cell(tilemap, pixel_coords, project.selection_map, !_subtract)
|
||||
|
|
|
@ -130,7 +130,7 @@ func paint_selection(
|
|||
func select_pixel(point: Vector2i, project: Project, select: bool) -> void:
|
||||
if Tools.is_placing_tiles():
|
||||
var tilemap := project.get_current_cel() as CelTileMap
|
||||
var cell_position := tilemap.get_cell_position(point)
|
||||
var cell_position := tilemap.get_cell_position(point) * tilemap.tileset.tile_size
|
||||
select_tilemap_cell(tilemap, cell_position, project.selection_map, select)
|
||||
project.selection_map.select_pixel(point, select)
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ func lasso_selection(
|
|||
func select_pixel(point: Vector2i, project: Project, select: bool) -> void:
|
||||
if Tools.is_placing_tiles():
|
||||
var tilemap := project.get_current_cel() as CelTileMap
|
||||
var cell_position := tilemap.get_cell_position(point)
|
||||
var cell_position := tilemap.get_cell_position(point) * tilemap.tileset.tile_size
|
||||
select_tilemap_cell(tilemap, cell_position, project.selection_map, select)
|
||||
project.selection_map.select_pixel(point, select)
|
||||
|
||||
|
|
|
@ -102,10 +102,12 @@ func apply_selection(pos: Vector2i) -> void:
|
|||
## where the shape will be drawn and what is its size
|
||||
func _get_result_rect(origin: Vector2i, dest: Vector2i) -> Rect2i:
|
||||
if Tools.is_placing_tiles():
|
||||
var tileset := (Global.current_project.get_current_cel() as CelTileMap).tileset
|
||||
var cel := Global.current_project.get_current_cel() as CelTileMap
|
||||
var tileset := cel.tileset
|
||||
var grid_size := tileset.tile_size
|
||||
origin = Tools.snap_to_rectangular_grid_boundary(origin, grid_size)
|
||||
dest = Tools.snap_to_rectangular_grid_boundary(dest, grid_size)
|
||||
var offset := cel.offset % grid_size
|
||||
origin = Tools.snap_to_rectangular_grid_boundary(origin, grid_size, offset)
|
||||
dest = Tools.snap_to_rectangular_grid_boundary(dest, grid_size, offset)
|
||||
var rect := Rect2i()
|
||||
|
||||
# Center the rect on the mouse
|
||||
|
|
|
@ -43,8 +43,14 @@ func draw_start(pos: Vector2i) -> void:
|
|||
_start_pos = pos
|
||||
_offset = pos
|
||||
_undo_data = _get_undo_data()
|
||||
if Global.current_project.has_selection:
|
||||
selection_node.transform_content_start()
|
||||
if Tools.is_placing_tiles():
|
||||
for cel in _get_selected_draw_cels():
|
||||
if cel is not CelTileMap:
|
||||
continue
|
||||
(cel as CelTileMap).prev_offset = (cel as CelTileMap).offset
|
||||
else:
|
||||
if Global.current_project.has_selection:
|
||||
selection_node.transform_content_start()
|
||||
_content_transformation_check = selection_node.is_moving_content
|
||||
Global.canvas.sprite_changed_this_frame = true
|
||||
Global.canvas.measurements.update_measurement(Global.MeasurementMode.MOVE)
|
||||
|
@ -60,10 +66,17 @@ func draw_move(pos: Vector2i) -> void:
|
|||
return
|
||||
pos = _snap_position(pos)
|
||||
|
||||
if Global.current_project.has_selection:
|
||||
selection_node.move_content(pos - _offset)
|
||||
else:
|
||||
if Tools.is_placing_tiles():
|
||||
for cel in _get_selected_draw_cels():
|
||||
if cel is not CelTileMap:
|
||||
continue
|
||||
(cel as CelTileMap).change_offset(cel.offset + pos - _offset)
|
||||
Global.canvas.move_preview_location = pos - _start_pos
|
||||
else:
|
||||
if Global.current_project.has_selection:
|
||||
selection_node.move_content(pos - _offset)
|
||||
else:
|
||||
Global.canvas.move_preview_location = pos - _start_pos
|
||||
_offset = pos
|
||||
Global.canvas.sprite_changed_this_frame = true
|
||||
Global.canvas.measurements.update_measurement(Global.MeasurementMode.MOVE)
|
||||
|
@ -78,7 +91,7 @@ func draw_end(pos: Vector2i) -> void:
|
|||
and _content_transformation_check == selection_node.is_moving_content
|
||||
):
|
||||
pos = _snap_position(pos)
|
||||
if Global.current_project.has_selection:
|
||||
if Global.current_project.has_selection and not Tools.is_placing_tiles():
|
||||
selection_node.move_borders_end()
|
||||
else:
|
||||
var pixel_diff := pos - _start_pos
|
||||
|
@ -141,6 +154,12 @@ func _commit_undo(action: String) -> void:
|
|||
|
||||
project.undos += 1
|
||||
project.undo_redo.create_action(action)
|
||||
if Tools.is_placing_tiles():
|
||||
for cel in _get_selected_draw_cels():
|
||||
if cel is not CelTileMap:
|
||||
continue
|
||||
project.undo_redo.add_do_method(cel.change_offset.bind(cel.offset))
|
||||
project.undo_redo.add_undo_method(cel.change_offset.bind(cel.prev_offset))
|
||||
project.deserialize_cel_undo_data(redo_data, _undo_data)
|
||||
project.undo_redo.add_do_method(Global.undo_or_redo.bind(false, frame, layer))
|
||||
project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true, frame, layer))
|
||||
|
|
|
@ -39,7 +39,7 @@ func _draw_cartesian_grid(grid_index: int, target_rect: Rect2i) -> void:
|
|||
var cel := Global.current_project.get_current_cel()
|
||||
if cel is CelTileMap and grid_index == 0:
|
||||
grid_size = (cel as CelTileMap).tileset.tile_size
|
||||
grid_offset = Vector2i.ZERO
|
||||
grid_offset = (cel as CelTileMap).offset
|
||||
var grid_multiline_points := PackedVector2Array()
|
||||
|
||||
var x: float = (
|
||||
|
|
|
@ -338,7 +338,8 @@ func _gizmo_resize() -> void:
|
|||
var mouse_pos := image_current_pixel
|
||||
if Tools.is_placing_tiles():
|
||||
var tilemap := Global.current_project.get_current_cel() as CelTileMap
|
||||
mouse_pos = mouse_pos.snapped(tilemap.tileset.tile_size)
|
||||
var offset := tilemap.offset % tilemap.tileset.tile_size
|
||||
mouse_pos = mouse_pos.snapped(tilemap.tileset.tile_size) + Vector2(offset)
|
||||
if Input.is_action_pressed("shape_center"):
|
||||
# Code inspired from https://github.com/GDQuest/godot-open-rpg
|
||||
if dir.x != 0 and dir.y != 0: # Border gizmos
|
||||
|
@ -419,9 +420,7 @@ func resize_selection() -> void:
|
|||
original_selected_tilemap_cells, horizontal_size, vertical_size
|
||||
)
|
||||
preview_image.crop(size.x, size.y)
|
||||
tilemap.apply_resizing_to_image(
|
||||
preview_image, selected_cells, big_bounding_rectangle
|
||||
)
|
||||
tilemap.apply_resizing_to_image(preview_image, selected_cells)
|
||||
else:
|
||||
var params := {"transformation_matrix": transformation_matrix, "pivot": content_pivot}
|
||||
preview_image.resize(size.x, size.y, Image.INTERPOLATE_NEAREST)
|
||||
|
@ -553,7 +552,7 @@ func transform_content_start() -> void:
|
|||
original_big_bounding_rectangle = big_bounding_rectangle
|
||||
original_offset = project.selection_offset
|
||||
var current_cel := project.get_current_cel()
|
||||
if current_cel is CelTileMap:
|
||||
if current_cel is CelTileMap and Tools.is_placing_tiles():
|
||||
original_selected_tilemap_cells = (current_cel as CelTileMap).get_selected_cells(
|
||||
project.selection_map, big_bounding_rectangle
|
||||
)
|
||||
|
@ -586,7 +585,7 @@ func transform_content_confirm() -> void:
|
|||
original_selected_tilemap_cells, horizontal_size, vertical_size
|
||||
)
|
||||
src.crop(preview_image.get_width(), preview_image.get_height())
|
||||
tilemap.apply_resizing_to_image(src, selected_cells, big_bounding_rectangle)
|
||||
tilemap.apply_resizing_to_image(src, selected_cells)
|
||||
else:
|
||||
var transformation_matrix := Transform2D(angle, Vector2.ZERO)
|
||||
var params := {
|
||||
|
|
|
@ -13,14 +13,16 @@ func _draw() -> void:
|
|||
draw_set_transform(position, rotation, Vector2(0.5, 0.5))
|
||||
if current_cel is CelTileMap and Input.is_action_pressed("ctrl"):
|
||||
var tilemap_cel := current_cel as CelTileMap
|
||||
for i in tilemap_cel.cells.size():
|
||||
var tile_data := tilemap_cel.cells[i]
|
||||
if tile_data.index == 0:
|
||||
var tile_size := tilemap_cel.tileset.tile_size
|
||||
var font := Themes.get_font()
|
||||
for cell_coords: Vector2i in tilemap_cel.cells:
|
||||
var cell := tilemap_cel.get_cell_at(cell_coords)
|
||||
if cell.index == 0:
|
||||
continue
|
||||
var pos := tilemap_cel.get_cell_coords_in_image(i)
|
||||
pos.y += tilemap_cel.tileset.tile_size.y
|
||||
var text := tile_data.to_string()
|
||||
draw_multiline_string(
|
||||
Themes.get_font(), pos * 2, text, HORIZONTAL_ALIGNMENT_LEFT, -1, FONT_SIZE
|
||||
var text := cell.to_string()
|
||||
var pos := cell_coords * tilemap_cel.tileset.tile_size + tilemap_cel.offset
|
||||
pos.y += tile_size.y - font.get_ascent(FONT_SIZE * 0.5) * 0.5
|
||||
draw_string(
|
||||
font, pos * 2, text, HORIZONTAL_ALIGNMENT_CENTER, tile_size.x * 2, FONT_SIZE
|
||||
)
|
||||
draw_set_transform(position, rotation, scale)
|
||||
|
|
|
@ -486,6 +486,7 @@ func copy_frames(
|
|||
selected_id = src_cel.selected.id
|
||||
elif src_cel is CelTileMap:
|
||||
new_cel = CelTileMap.new(src_cel.tileset)
|
||||
new_cel.offset = src_cel.offset
|
||||
else:
|
||||
new_cel = src_cel.get_script().new()
|
||||
|
||||
|
@ -939,6 +940,7 @@ func _on_CloneLayer_pressed() -> void:
|
|||
)
|
||||
elif src_cel is CelTileMap:
|
||||
new_cel = CelTileMap.new(src_cel.tileset)
|
||||
new_cel.offset = src_cel.offset
|
||||
else:
|
||||
new_cel = src_cel.get_script().new()
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue