Skip to content

Commit

Permalink
Change tileset in a layer from the project properties
Browse files Browse the repository at this point in the history
  • Loading branch information
OverloadedOrama committed Nov 30, 2024
1 parent a944096 commit 2ebad31
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 33 deletions.
86 changes: 60 additions & 26 deletions src/Classes/Cels/CelTileMap.gd
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,7 @@ extends PixelCel
## such as horizontal flipping, vertical flipping, or if it's transposed.

## The [TileSetCustom] that this cel uses, passed down from the cel's [LayerTileMap].
var tileset: TileSetCustom:
set(value):
if is_instance_valid(tileset):
if tileset.updated.is_connected(_on_tileset_updated):
tileset.updated.disconnect(_on_tileset_updated)
tileset = value
if is_instance_valid(tileset):
_resize_cells(get_image().get_size())
if not tileset.updated.is_connected(_on_tileset_updated):
tileset.updated.connect(_on_tileset_updated)
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].
Expand Down Expand Up @@ -76,16 +67,30 @@ class Cell:

func _init(_tileset: TileSetCustom, _image := ImageExtended.new(), _opacity := 1.0) -> void:
super._init(_image, _opacity)
tileset = _tileset
set_tileset(_tileset)


func set_tileset(new_tileset: TileSetCustom, reset_indices := true) -> void:
if tileset == new_tileset:
return
if is_instance_valid(tileset):
if tileset.updated.is_connected(_on_tileset_updated):
tileset.updated.disconnect(_on_tileset_updated)
tileset = new_tileset
if is_instance_valid(tileset):
_resize_cells(get_image().get_size(), reset_indices)
if not tileset.updated.is_connected(_on_tileset_updated):
tileset.updated.connect(_on_tileset_updated)


## 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:
index = clampi(index, 0, tileset.tiles.size() - 1)
var previous_index := cells[cell_position].index

if previous_index != index:
if previous_index > 0:
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
Expand Down Expand Up @@ -215,15 +220,14 @@ func update_tilemap(
tile_editing_mode := TileSetPanel.tile_editing_mode, source_image := image
) -> 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)
var rect := Rect2i(coords, tileset.tile_size)
var image_portion := source_image.get_region(rect)
var index := cells[i].index
if index >= tileset.tiles.size():
printerr("Cell at position ", i + 1, ", mapped to ", index, " is out of bounds!")
index = 0
cells[i].index = 0
var current_tile := tileset.tiles[index]
if tile_editing_mode == TileSetPanel.TileEditingMode.MANUAL:
if image_portion.is_invisible():
Expand All @@ -237,7 +241,7 @@ func update_tilemap(
if not tiles_equal(i, 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)
_handle_auto_editing_mode(i, image_portion, tileset_size_before_update)
else: # Stack
if image_portion.is_invisible():
continue
Expand All @@ -254,6 +258,23 @@ func update_tilemap(
tileset.add_tile(image_portion, self)
cells[i].index = tileset.tiles.size() - 1
cells[i].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)
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
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)


## Gets called by [method update_tilemap]. This method is responsible for handling
Expand Down Expand Up @@ -294,11 +315,17 @@ func update_tilemap(
## 7) Cell 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:
func _handle_auto_editing_mode(
i: int, image_portion: Image, tileset_size_before_update: int
) -> void:
var index := cells[i].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:
return
cells[i].index = 0
cells[i].remove_transformations()
if index > 0:
Expand Down Expand Up @@ -374,10 +401,7 @@ func _update_cell(cell_position: int) -> void:
var cell_data := cells[cell_position]
var index := cell_data.index
if index >= tileset.tiles.size():
printerr(
"Cell at position ", cell_position + 1, ", mapped to ", index, " is out of bounds!"
)
return
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
Expand All @@ -389,7 +413,7 @@ func _update_cell(cell_position: int) -> void:


## Calls [method _update_cell] for all [member cells].
func _update_cel_portions() -> void:
func update_cel_portions() -> void:
for i in cells.size():
_update_cell(i)

Expand All @@ -402,7 +426,11 @@ func _re_index_all_cells() -> void:
var rect := Rect2i(coords, tileset.tile_size)
var image_portion := image.get_region(rect)
if image_portion.is_invisible():
cells[i].index = 0
var index := cells[i].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)
continue
for j in range(1, tileset.tiles.size()):
var tile := tileset.tiles[j]
Expand All @@ -412,12 +440,16 @@ func _re_index_all_cells() -> void:


## Resizes the [member cells] array based on [param new_size].
func _resize_cells(new_size: Vector2i) -> void:
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():
cells[i] = Cell.new()
if reset_indices:
cells[i] = Cell.new()
else:
if not is_instance_valid(cells[i]):
cells[i] = Cell.new()


## Returns [code]true[/code] if the user just did a Redo.
Expand All @@ -429,15 +461,15 @@ func _is_redo() -> bool:
## make sure to also update it here.
## If [param replace_index] is larger than -1, it means that manual mode
## has been used to replace a tile in the tileset in another cel,
## so call [method _update_cel_portions] to update it in this cel as well.
## so call [method update_cel_portions] to update it in this cel as well.
## Otherwise, call [method _re_index_all_cells] to ensure that the cells have correct indices.
func _on_tileset_updated(cel: CelTileMap, replace_index: int) -> void:
if cel == self or not is_instance_valid(cel):
return
if link_set != null and cel in link_set["cels"]:
return
if replace_index > -1: # Manual mode
_update_cel_portions()
update_cel_portions()
else:
_re_index_all_cells()
Global.canvas.update_all_layers = true
Expand Down Expand Up @@ -469,6 +501,8 @@ func update_texture(undo := false) -> void:
for i in cells.size():
var cell_data := cells[i]
var index := cell_data.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)
Expand Down
5 changes: 1 addition & 4 deletions src/Classes/Project.gd
Original file line number Diff line number Diff line change
Expand Up @@ -662,10 +662,7 @@ func get_all_pixel_cels() -> Array[PixelCel]:
## and calls [method CelTileMap.serialize_undo_data] for [CelTileMap]s.
func serialize_cel_undo_data(cels: Array[BaseCel], data: Dictionary) -> void:
var cels_to_serialize := cels
if (
TileSetPanel.tile_editing_mode == TileSetPanel.TileEditingMode.MANUAL
and not TileSetPanel.placing_tiles
):
if not TileSetPanel.placing_tiles:
cels_to_serialize = find_same_tileset_tilemap_cels(cels)
for cel in cels_to_serialize:
if not cel is PixelCel:
Expand Down
42 changes: 41 additions & 1 deletion src/UI/Timeline/LayerProperties.gd
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,30 @@ var layer_indices: PackedInt32Array
@onready var opacity_slider := $GridContainer/OpacitySlider as ValueSlider
@onready var blend_modes_button := $GridContainer/BlendModeOptionButton as OptionButton
@onready var user_data_text_edit := $GridContainer/UserDataTextEdit as TextEdit
@onready var tileset_option_button := $GridContainer/TilesetOptionButton as OptionButton


func _on_visibility_changed() -> void:
if layer_indices.size() == 0:
return
Global.dialog_open(visible)
var first_layer := Global.current_project.layers[layer_indices[0]]
var project := Global.current_project
var first_layer := project.layers[layer_indices[0]]
if visible:
_fill_blend_modes_option_button()
name_line_edit.text = first_layer.name
opacity_slider.value = first_layer.opacity * 100.0
var blend_mode_index := blend_modes_button.get_item_index(first_layer.blend_mode)
blend_modes_button.selected = blend_mode_index
user_data_text_edit.text = first_layer.user_data
get_tree().set_group(&"TilemapLayers", "visible", first_layer is LayerTileMap)
tileset_option_button.clear()
if first_layer is LayerTileMap:
for i in project.tilesets.size():
var tileset := project.tilesets[i]
tileset_option_button.add_item(tileset.get_text_info(i))
if tileset == first_layer.tileset:
tileset_option_button.select(i)
else:
layer_indices = []

Expand Down Expand Up @@ -86,6 +96,7 @@ func _on_blend_mode_option_button_item_selected(index: BaseLayer.BlendModes) ->
Global.canvas.update_all_layers = true
var project := Global.current_project
var current_mode := blend_modes_button.get_item_id(index)
project.undos += 1
project.undo_redo.create_action("Set Blend Mode")
for layer_index in layer_indices:
var layer := project.layers[layer_index]
Expand All @@ -109,3 +120,32 @@ func _on_user_data_text_edit_text_changed() -> void:

func _emit_layer_property_signal() -> void:
layer_property_changed.emit()


func _on_tileset_option_button_item_selected(index: int) -> void:
var project := Global.current_project
var new_tileset := project.tilesets[index]
project.undos += 1
project.undo_redo.create_action("Set Tileset")
for layer_index in layer_indices:
var layer := project.layers[layer_index]
if layer is not LayerTileMap:
continue
var previous_tileset := (layer as LayerTileMap).tileset
project.undo_redo.add_do_property(layer, "tileset", new_tileset)
project.undo_redo.add_undo_property(layer, "tileset", previous_tileset)
for frame in project.frames:
for i in frame.cels.size():
var cel := frame.cels[i]
if cel is CelTileMap and i == layer_index:
project.undo_redo.add_do_method(cel.set_tileset.bind(new_tileset, false))
project.undo_redo.add_do_method(cel.update_cel_portions)
project.undo_redo.add_undo_method(cel.set_tileset.bind(previous_tileset, false))
project.undo_redo.add_undo_method(cel.update_cel_portions)
project.undo_redo.add_do_method(Global.undo_or_redo.bind(false))
project.undo_redo.add_do_method(Global.canvas.draw_layers)
project.undo_redo.add_do_method(func(): Global.cel_switched.emit())
project.undo_redo.add_undo_method(Global.undo_or_redo.bind(true))
project.undo_redo.add_undo_method(Global.canvas.draw_layers)
project.undo_redo.add_undo_method(func(): Global.cel_switched.emit())
project.undo_redo.commit_action()
18 changes: 16 additions & 2 deletions src/UI/Timeline/LayerProperties.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@

[node name="LayerProperties" type="AcceptDialog"]
title = "Layer properties"
size = Vector2i(300, 208)
script = ExtResource("1_54q1t")

[node name="GridContainer" type="GridContainer" parent="."]
offset_right = 40.0
offset_bottom = 40.0
offset_left = 8.0
offset_top = 8.0
offset_right = 292.0
offset_bottom = 159.0
columns = 2

[node name="NameLabel" type="Label" parent="GridContainer"]
Expand Down Expand Up @@ -60,8 +63,19 @@ layout_mode = 2
size_flags_horizontal = 3
scroll_fit_content_height = true

[node name="TilesetLabel" type="Label" parent="GridContainer" groups=["TilemapLayers"]]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
text = "Tileset:"

[node name="TilesetOptionButton" type="OptionButton" parent="GridContainer" groups=["TilemapLayers"]]
layout_mode = 2
mouse_default_cursor_shape = 2

[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="text_changed" from="GridContainer/NameLineEdit" to="." method="_on_name_line_edit_text_changed"]
[connection signal="value_changed" from="GridContainer/OpacitySlider" to="." method="_on_opacity_slider_value_changed"]
[connection signal="item_selected" from="GridContainer/BlendModeOptionButton" to="." method="_on_blend_mode_option_button_item_selected"]
[connection signal="text_changed" from="GridContainer/UserDataTextEdit" to="." method="_on_user_data_text_edit_text_changed"]
[connection signal="item_selected" from="GridContainer/TilesetOptionButton" to="." method="_on_tileset_option_button_item_selected"]

0 comments on commit 2ebad31

Please sign in to comment.