mirror of
https://github.com/JHDev2006/Super-Mario-Bros.-Remastered-Public.git
synced 2025-10-22 23:48:11 +00:00
added the game
This commit is contained in:
939
addons/better-terrain/editor/Dock.gd
Executable file
939
addons/better-terrain/editor/Dock.gd
Executable file
@@ -0,0 +1,939 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
signal update_overlay
|
||||
signal force_show_terrains
|
||||
|
||||
# The maximum individual tiles the overlay will draw before shortcutting the display
|
||||
# To prevent editor lag when drawing large rectangles or filling large areas
|
||||
const MAX_CANVAS_RENDER_TILES = 1500
|
||||
const TERRAIN_PROPERTIES_SCENE := preload("res://addons/better-terrain/editor/TerrainProperties.tscn")
|
||||
const TERRAIN_ENTRY_SCENE := preload("res://addons/better-terrain/editor/TerrainEntry.tscn")
|
||||
const MIN_ZOOM_SETTING := "editor/better_terrain/min_zoom_amount"
|
||||
const MAX_ZOOM_SETTING := "editor/better_terrain/max_zoom_amount"
|
||||
|
||||
|
||||
# Buttons
|
||||
@onready var draw_button: Button = $VBox/Toolbar/Draw
|
||||
@onready var line_button: Button = $VBox/Toolbar/Line
|
||||
@onready var rectangle_button: Button = $VBox/Toolbar/Rectangle
|
||||
@onready var fill_button: Button = $VBox/Toolbar/Fill
|
||||
@onready var replace_button: Button = $VBox/Toolbar/Replace
|
||||
|
||||
@onready var paint_type: Button = $VBox/Toolbar/PaintType
|
||||
@onready var paint_terrain: Button = $VBox/Toolbar/PaintTerrain
|
||||
@onready var select_tiles: Button = $VBox/Toolbar/SelectTiles
|
||||
|
||||
@onready var paint_symmetry: Button = $VBox/Toolbar/PaintSymmetry
|
||||
@onready var symmetry_options: OptionButton = $VBox/Toolbar/SymmetryOptions
|
||||
|
||||
@onready var shuffle_random: Button = $VBox/Toolbar/ShuffleRandom
|
||||
@onready var zoom_slider_container: VBoxContainer = $VBox/Toolbar/ZoomContainer
|
||||
|
||||
@onready var source_selector: MenuBar = $VBox/Toolbar/Sources
|
||||
@onready var source_selector_popup: PopupMenu = $VBox/Toolbar/Sources/Sources
|
||||
|
||||
@onready var clean_button: Button = $VBox/Toolbar/Clean
|
||||
@onready var layer_up: Button = $VBox/Toolbar/LayerUp
|
||||
@onready var layer_down: Button = $VBox/Toolbar/LayerDown
|
||||
@onready var layer_highlight: Button = $VBox/Toolbar/LayerHighlight
|
||||
@onready var layer_grid: Button = $VBox/Toolbar/LayerGrid
|
||||
|
||||
@onready var grid_mode_button: Button = $VBox/HSplit/Terrains/LowerToolbar/GridMode
|
||||
@onready var quick_mode_button: Button = $VBox/HSplit/Terrains/LowerToolbar/QuickMode
|
||||
|
||||
@onready var edit_tool_buttons: HBoxContainer = $VBox/HSplit/Terrains/LowerToolbar/EditTools
|
||||
@onready var add_terrain_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/AddTerrain
|
||||
@onready var edit_terrain_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/EditTerrain
|
||||
@onready var pick_icon_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/PickIcon
|
||||
@onready var move_up_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/MoveUp
|
||||
@onready var move_down_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/MoveDown
|
||||
@onready var remove_terrain_button: Button = $VBox/HSplit/Terrains/LowerToolbar/EditTools/RemoveTerrain
|
||||
|
||||
@onready var scroll_container: ScrollContainer = $VBox/HSplit/Terrains/Panel/ScrollContainer
|
||||
@onready var terrain_list: HFlowContainer = $VBox/HSplit/Terrains/Panel/ScrollContainer/TerrainList
|
||||
@onready var tile_view: Control = $VBox/HSplit/Panel/ScrollArea/TileView
|
||||
|
||||
|
||||
var selected_entry := -2
|
||||
|
||||
var tilemap : TileMapLayer
|
||||
var tileset : TileSet
|
||||
|
||||
var undo_manager : EditorUndoRedoManager
|
||||
var terrain_undo
|
||||
|
||||
var draw_overlay := false
|
||||
var initial_click : Vector2i
|
||||
var prev_position : Vector2i
|
||||
var current_position : Vector2i
|
||||
var tileset_dirty := false
|
||||
var zoom_slider : HSlider
|
||||
|
||||
enum PaintMode {
|
||||
NO_PAINT,
|
||||
PAINT,
|
||||
ERASE
|
||||
}
|
||||
|
||||
enum PaintAction {
|
||||
NO_ACTION,
|
||||
LINE,
|
||||
RECT
|
||||
}
|
||||
|
||||
enum SourceSelectors {
|
||||
ALL = 1000000,
|
||||
NONE = 1000001,
|
||||
}
|
||||
|
||||
var paint_mode := PaintMode.NO_PAINT
|
||||
|
||||
var paint_action := PaintAction.NO_ACTION
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
draw_button.icon = get_theme_icon("Edit", "EditorIcons")
|
||||
line_button.icon = get_theme_icon("Line", "EditorIcons")
|
||||
rectangle_button.icon = get_theme_icon("Rectangle", "EditorIcons")
|
||||
fill_button.icon = get_theme_icon("Bucket", "EditorIcons")
|
||||
select_tiles.icon = get_theme_icon("ToolSelect", "EditorIcons")
|
||||
add_terrain_button.icon = get_theme_icon("Add", "EditorIcons")
|
||||
edit_terrain_button.icon = get_theme_icon("Tools", "EditorIcons")
|
||||
pick_icon_button.icon = get_theme_icon("ColorPick", "EditorIcons")
|
||||
move_up_button.icon = get_theme_icon("ArrowUp", "EditorIcons")
|
||||
move_down_button.icon = get_theme_icon("ArrowDown", "EditorIcons")
|
||||
remove_terrain_button.icon = get_theme_icon("Remove", "EditorIcons")
|
||||
grid_mode_button.icon = get_theme_icon("FileThumbnail", "EditorIcons")
|
||||
quick_mode_button.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
|
||||
layer_up.icon = get_theme_icon("MoveUp", "EditorIcons")
|
||||
layer_down.icon = get_theme_icon("MoveDown", "EditorIcons")
|
||||
layer_highlight.icon = get_theme_icon("TileMapHighlightSelected", "EditorIcons")
|
||||
layer_grid.icon = get_theme_icon("Grid", "EditorIcons")
|
||||
|
||||
select_tiles.button_group.pressed.connect(_on_bit_button_pressed)
|
||||
|
||||
terrain_undo = load("res://addons/better-terrain/editor/TerrainUndo.gd").new()
|
||||
add_child(terrain_undo)
|
||||
tile_view.undo_manager = undo_manager
|
||||
tile_view.terrain_undo = terrain_undo
|
||||
|
||||
tile_view.paste_occurred.connect(_on_paste_occurred)
|
||||
tile_view.change_zoom_level.connect(_on_change_zoom_level)
|
||||
tile_view.terrain_updated.connect(_on_terrain_updated)
|
||||
|
||||
# Zoom slider is manipulated by settings, make it at runtime
|
||||
zoom_slider = HSlider.new()
|
||||
zoom_slider.custom_minimum_size = Vector2(100, 0)
|
||||
zoom_slider.value_changed.connect(tile_view._on_zoom_value_changed)
|
||||
zoom_slider_container.add_child(zoom_slider)
|
||||
|
||||
# Init settings if needed
|
||||
if !ProjectSettings.has_setting(MIN_ZOOM_SETTING):
|
||||
ProjectSettings.set(MIN_ZOOM_SETTING, 1.0)
|
||||
ProjectSettings.add_property_info({
|
||||
"name": MIN_ZOOM_SETTING,
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "0.1,1.0,0.1"
|
||||
})
|
||||
ProjectSettings.set_initial_value(MIN_ZOOM_SETTING, 1.0)
|
||||
ProjectSettings.set_as_basic(MIN_ZOOM_SETTING, true)
|
||||
|
||||
if !ProjectSettings.has_setting(MAX_ZOOM_SETTING):
|
||||
ProjectSettings.set(MAX_ZOOM_SETTING, 8.0)
|
||||
ProjectSettings.add_property_info({
|
||||
"name": MAX_ZOOM_SETTING,
|
||||
"type": TYPE_FLOAT,
|
||||
"hint": PROPERTY_HINT_RANGE,
|
||||
"hint_string": "2.0,32.0,1.0"
|
||||
})
|
||||
ProjectSettings.set_initial_value(MAX_ZOOM_SETTING, 8.0)
|
||||
ProjectSettings.set_as_basic(MAX_ZOOM_SETTING, true)
|
||||
ProjectSettings.set_order(MAX_ZOOM_SETTING, ProjectSettings.get_order(MIN_ZOOM_SETTING) + 1)
|
||||
|
||||
ProjectSettings.settings_changed.connect(_on_adjust_settings)
|
||||
_on_adjust_settings()
|
||||
zoom_slider.value = 1.0
|
||||
|
||||
|
||||
func _process(delta):
|
||||
scroll_container.scroll_horizontal = 0
|
||||
|
||||
|
||||
func _on_adjust_settings():
|
||||
zoom_slider.min_value = ProjectSettings.get_setting(MIN_ZOOM_SETTING, 1.0)
|
||||
zoom_slider.max_value = ProjectSettings.get_setting(MAX_ZOOM_SETTING, 8.0)
|
||||
zoom_slider.step = (zoom_slider.max_value - zoom_slider.min_value) / 100.0
|
||||
|
||||
|
||||
func _get_fill_cells(target: Vector2i) -> Array:
|
||||
var pick := BetterTerrain.get_cell(tilemap, target)
|
||||
var bounds := tilemap.get_used_rect()
|
||||
var neighbors = BetterTerrain.data.cells_adjacent_for_fill(tileset)
|
||||
|
||||
# No sets yet, so use a dictionary
|
||||
var checked := {}
|
||||
var pending := [target]
|
||||
var goal := []
|
||||
|
||||
while !pending.is_empty():
|
||||
var p = pending.pop_front()
|
||||
if checked.has(p):
|
||||
continue
|
||||
checked[p] = true
|
||||
if !bounds.has_point(p) or BetterTerrain.get_cell(tilemap, p) != pick:
|
||||
continue
|
||||
|
||||
goal.append(p)
|
||||
pending.append_array(BetterTerrain.data.neighboring_coords(tilemap, p, neighbors))
|
||||
|
||||
return goal
|
||||
|
||||
|
||||
func tiles_about_to_change() -> void:
|
||||
if tileset and tileset.changed.is_connected(queue_tiles_changed):
|
||||
tileset.changed.disconnect(queue_tiles_changed)
|
||||
|
||||
|
||||
func tiles_changed() -> void:
|
||||
# ensure up to date
|
||||
BetterTerrain._update_terrain_data(tileset)
|
||||
|
||||
# clear terrains
|
||||
for c in terrain_list.get_children():
|
||||
terrain_list.remove_child(c)
|
||||
c.queue_free()
|
||||
|
||||
# load terrains from tileset
|
||||
var terrain_count := BetterTerrain.terrain_count(tileset)
|
||||
var item_count = terrain_count + 1
|
||||
for i in terrain_count:
|
||||
var terrain := BetterTerrain.get_terrain(tileset, i)
|
||||
if i >= terrain_list.get_child_count():
|
||||
add_terrain_entry(terrain, i)
|
||||
|
||||
if item_count > terrain_list.get_child_count():
|
||||
var terrain := BetterTerrain.get_terrain(tileset, BetterTerrain.TileCategory.EMPTY)
|
||||
if terrain.valid:
|
||||
add_terrain_entry(terrain, item_count - 1)
|
||||
|
||||
while item_count < terrain_list.get_child_count():
|
||||
var child = terrain_list.get_child(terrain_list.get_child_count() - 1)
|
||||
terrain_list.remove_child(child)
|
||||
child.free()
|
||||
|
||||
source_selector_popup.clear()
|
||||
source_selector_popup.add_item("All", SourceSelectors.ALL)
|
||||
source_selector_popup.add_item("None", SourceSelectors.NONE)
|
||||
var source_count = tileset.get_source_count() if tileset else 0
|
||||
for s in source_count:
|
||||
var source_id = tileset.get_source_id(s)
|
||||
var source := tileset.get_source(source_id)
|
||||
if !(source is TileSetAtlasSource):
|
||||
continue
|
||||
|
||||
var name := source.resource_name
|
||||
if name.is_empty():
|
||||
var texture := (source as TileSetAtlasSource).texture
|
||||
var texture_name := texture.resource_name if texture else ""
|
||||
if !texture_name.is_empty():
|
||||
name = texture_name
|
||||
else:
|
||||
var texture_path := texture.resource_path if texture else ""
|
||||
if !texture_path.is_empty():
|
||||
name = texture_path.get_file()
|
||||
|
||||
if !name.is_empty():
|
||||
name += " "
|
||||
name += " (ID: %d)" % source_id
|
||||
|
||||
source_selector_popup.add_check_item(name, source_id)
|
||||
source_selector_popup.set_item_checked(source_selector_popup.get_item_index(source_id), true)
|
||||
source_selector.visible = source_selector_popup.item_count > 3 # All, None and more than one source
|
||||
|
||||
update_tile_view_paint()
|
||||
tile_view.refresh_tileset(tileset)
|
||||
|
||||
if tileset and !tileset.changed.is_connected(queue_tiles_changed):
|
||||
tileset.changed.connect(queue_tiles_changed)
|
||||
|
||||
clean_button.visible = BetterTerrain._has_invalid_peering_types(tileset)
|
||||
|
||||
tileset_dirty = false
|
||||
_on_grid_mode_pressed()
|
||||
_on_quick_mode_pressed()
|
||||
|
||||
|
||||
func about_to_be_visible(visible: bool) -> void:
|
||||
if !visible:
|
||||
return
|
||||
|
||||
if tileset != tilemap.tile_set:
|
||||
tiles_about_to_change()
|
||||
tileset = tilemap.tile_set
|
||||
tiles_changed()
|
||||
|
||||
var settings := EditorInterface.get_editor_settings()
|
||||
layer_highlight.set_pressed_no_signal(settings.get_setting("editors/tiles_editor/highlight_selected_layer"))
|
||||
layer_grid.set_pressed_no_signal(settings.get_setting("editors/tiles_editor/display_grid"))
|
||||
|
||||
|
||||
func queue_tiles_changed() -> void:
|
||||
# Bring terrain data up to date with complex tileset changes
|
||||
if !tileset or tileset_dirty:
|
||||
return
|
||||
|
||||
tileset_dirty = true
|
||||
tiles_changed.call_deferred()
|
||||
|
||||
|
||||
func _on_entry_select(index:int):
|
||||
selected_entry = index
|
||||
if selected_entry >= BetterTerrain.terrain_count(tileset):
|
||||
selected_entry = BetterTerrain.TileCategory.EMPTY
|
||||
for i in range(terrain_list.get_child_count()):
|
||||
if i != index:
|
||||
terrain_list.get_child(i).set_selected(false)
|
||||
update_tile_view_paint()
|
||||
|
||||
|
||||
func _on_clean_pressed() -> void:
|
||||
var confirmed := [false]
|
||||
var popup := ConfirmationDialog.new()
|
||||
popup.dialog_text = tr("Tile set changes have caused terrain to become invalid. Remove invalid terrain data?")
|
||||
popup.dialog_hide_on_ok = false
|
||||
popup.confirmed.connect(func():
|
||||
confirmed[0] = true
|
||||
popup.hide()
|
||||
)
|
||||
EditorInterface.popup_dialog_centered(popup)
|
||||
await popup.visibility_changed
|
||||
popup.queue_free()
|
||||
|
||||
if confirmed[0]:
|
||||
undo_manager.create_action("Clean invalid terrain peering data", UndoRedo.MERGE_DISABLE, tileset)
|
||||
undo_manager.add_do_method(BetterTerrain, &"_clear_invalid_peering_types", tileset)
|
||||
undo_manager.add_do_method(self, &"tiles_changed")
|
||||
terrain_undo.create_peering_restore_point(undo_manager, tileset)
|
||||
undo_manager.add_undo_method(self, &"tiles_changed")
|
||||
undo_manager.commit_action()
|
||||
|
||||
|
||||
func _on_grid_mode_pressed() -> void:
|
||||
for c in terrain_list.get_children():
|
||||
c.grid_mode = grid_mode_button.button_pressed
|
||||
c.update_style()
|
||||
|
||||
|
||||
func _on_quick_mode_pressed() -> void:
|
||||
edit_tool_buttons.visible = !quick_mode_button.button_pressed
|
||||
for c in terrain_list.get_children():
|
||||
c.visible = !quick_mode_button.button_pressed or c.terrain.type in [BetterTerrain.TerrainType.MATCH_TILES, BetterTerrain.TerrainType.MATCH_VERTICES]
|
||||
|
||||
|
||||
func update_tile_view_paint() -> void:
|
||||
tile_view.paint = selected_entry
|
||||
tile_view.queue_redraw()
|
||||
|
||||
var editable = tile_view.paint != BetterTerrain.TileCategory.EMPTY
|
||||
edit_terrain_button.disabled = !editable
|
||||
move_up_button.disabled = !editable or tile_view.paint == 0
|
||||
move_down_button.disabled = !editable or tile_view.paint == BetterTerrain.terrain_count(tileset) - 1
|
||||
remove_terrain_button.disabled = !editable
|
||||
pick_icon_button.disabled = !editable
|
||||
|
||||
|
||||
func _on_add_terrain_pressed() -> void:
|
||||
if !tileset:
|
||||
return
|
||||
|
||||
var popup := TERRAIN_PROPERTIES_SCENE.instantiate()
|
||||
popup.set_category_data(BetterTerrain.get_terrain_categories(tileset))
|
||||
popup.terrain_name = "New terrain"
|
||||
popup.terrain_color = Color.from_hsv(randf(), 0.3 + 0.7 * randf(), 0.6 + 0.4 * randf())
|
||||
popup.terrain_icon = ""
|
||||
popup.terrain_type = 0
|
||||
EditorInterface.popup_dialog_centered(popup)
|
||||
await popup.visibility_changed
|
||||
if popup.accepted:
|
||||
undo_manager.create_action("Add terrain type", UndoRedo.MERGE_DISABLE, tileset)
|
||||
undo_manager.add_do_method(self, &"perform_add_terrain", popup.terrain_name, popup.terrain_color, popup.terrain_type, popup.terrain_categories, {path = popup.terrain_icon})
|
||||
undo_manager.add_undo_method(self, &"perform_remove_terrain", terrain_list.get_child_count() - 1)
|
||||
undo_manager.commit_action()
|
||||
popup.queue_free()
|
||||
|
||||
|
||||
func _on_edit_terrain_pressed() -> void:
|
||||
if !tileset:
|
||||
return
|
||||
|
||||
if selected_entry < 0:
|
||||
return
|
||||
|
||||
var t := BetterTerrain.get_terrain(tileset, selected_entry)
|
||||
var categories = BetterTerrain.get_terrain_categories(tileset)
|
||||
categories = categories.filter(func(x): return x.id != selected_entry)
|
||||
|
||||
var popup := TERRAIN_PROPERTIES_SCENE.instantiate()
|
||||
popup.set_category_data(categories)
|
||||
|
||||
t.icon = t.icon.duplicate()
|
||||
|
||||
popup.terrain_name = t.name
|
||||
popup.terrain_type = t.type
|
||||
popup.terrain_color = t.color
|
||||
if t.has("icon") and t.icon.has("path"):
|
||||
popup.terrain_icon = t.icon.path
|
||||
popup.terrain_categories = t.categories
|
||||
EditorInterface.popup_dialog_centered(popup)
|
||||
await popup.visibility_changed
|
||||
if popup.accepted:
|
||||
undo_manager.create_action("Edit terrain details", UndoRedo.MERGE_DISABLE, tileset)
|
||||
undo_manager.add_do_method(self, &"perform_edit_terrain", selected_entry, popup.terrain_name, popup.terrain_color, popup.terrain_type, popup.terrain_categories, {path = popup.terrain_icon})
|
||||
undo_manager.add_undo_method(self, &"perform_edit_terrain", selected_entry, t.name, t.color, t.type, t.categories, t.icon)
|
||||
if t.type != popup.terrain_type:
|
||||
terrain_undo.create_terrain_type_restore_point(undo_manager, tileset)
|
||||
terrain_undo.create_peering_restore_point_specific(undo_manager, tileset, selected_entry)
|
||||
undo_manager.commit_action()
|
||||
popup.queue_free()
|
||||
|
||||
|
||||
func _on_pick_icon_pressed():
|
||||
if selected_entry < 0:
|
||||
return
|
||||
tile_view.pick_icon_terrain = selected_entry
|
||||
|
||||
|
||||
func _on_pick_icon_focus_exited():
|
||||
tile_view.pick_icon_terrain_cancel = true
|
||||
pick_icon_button.button_pressed = false
|
||||
|
||||
|
||||
func _on_move_pressed(down: bool) -> void:
|
||||
if !tileset:
|
||||
return
|
||||
|
||||
if selected_entry < 0:
|
||||
return
|
||||
|
||||
var index1 = selected_entry
|
||||
var index2 = index1 + (1 if down else -1)
|
||||
if index2 < 0 or index2 >= terrain_list.get_child_count():
|
||||
return
|
||||
|
||||
undo_manager.create_action("Reorder terrains", UndoRedo.MERGE_DISABLE, tileset)
|
||||
undo_manager.add_do_method(self, &"perform_swap_terrain", index1, index2)
|
||||
undo_manager.add_undo_method(self, &"perform_swap_terrain", index1, index2)
|
||||
undo_manager.commit_action()
|
||||
|
||||
|
||||
func _on_remove_terrain_pressed() -> void:
|
||||
if !tileset:
|
||||
return
|
||||
|
||||
if selected_entry < 0:
|
||||
return
|
||||
|
||||
# store confirmation in array to pass by ref
|
||||
var t := BetterTerrain.get_terrain(tileset, selected_entry)
|
||||
var confirmed := [false]
|
||||
var popup := ConfirmationDialog.new()
|
||||
popup.dialog_text = tr("Are you sure you want to remove {0}?").format([t.name])
|
||||
popup.dialog_hide_on_ok = false
|
||||
popup.confirmed.connect(func():
|
||||
confirmed[0] = true
|
||||
popup.hide()
|
||||
)
|
||||
EditorInterface.popup_dialog_centered(popup)
|
||||
await popup.visibility_changed
|
||||
popup.queue_free()
|
||||
|
||||
if confirmed[0]:
|
||||
undo_manager.create_action("Remove terrain type", UndoRedo.MERGE_DISABLE, tileset)
|
||||
undo_manager.add_do_method(self, &"perform_remove_terrain", selected_entry)
|
||||
undo_manager.add_undo_method(self, &"perform_add_terrain", t.name, t.color, t.type, t.categories, t.icon)
|
||||
for n in range(terrain_list.get_child_count() - 2, selected_entry, -1):
|
||||
undo_manager.add_undo_method(self, &"perform_swap_terrain", n, n - 1)
|
||||
if t.type == BetterTerrain.TerrainType.CATEGORY:
|
||||
terrain_undo.create_terrain_type_restore_point(undo_manager, tileset)
|
||||
terrain_undo.create_peering_restore_point_specific(undo_manager, tileset, selected_entry)
|
||||
undo_manager.commit_action()
|
||||
|
||||
|
||||
func add_terrain_entry(terrain:Dictionary, index:int = -1):
|
||||
if index < 0:
|
||||
index = terrain_list.get_child_count()
|
||||
|
||||
var entry = TERRAIN_ENTRY_SCENE.instantiate()
|
||||
entry.tileset = tileset
|
||||
entry.terrain = terrain
|
||||
entry.grid_mode = grid_mode_button.button_pressed
|
||||
entry.select.connect(_on_entry_select)
|
||||
|
||||
terrain_list.add_child(entry)
|
||||
terrain_list.move_child(entry, index)
|
||||
|
||||
|
||||
func remove_terrain_entry(index: int):
|
||||
terrain_list.get_child(index).free()
|
||||
for i in range(index, terrain_list.get_child_count()):
|
||||
var child = terrain_list.get_child(i)
|
||||
child.terrain = BetterTerrain.get_terrain(tileset, i)
|
||||
child.update()
|
||||
|
||||
|
||||
func perform_add_terrain(name: String, color: Color, type: int, categories: Array, icon:Dictionary = {}) -> void:
|
||||
if BetterTerrain.add_terrain(tileset, name, color, type, categories, icon):
|
||||
var index = BetterTerrain.terrain_count(tileset) - 1
|
||||
var terrain = BetterTerrain.get_terrain(tileset, index)
|
||||
add_terrain_entry(terrain, index)
|
||||
|
||||
|
||||
func perform_remove_terrain(index: int) -> void:
|
||||
if index >= BetterTerrain.terrain_count(tileset):
|
||||
return
|
||||
if BetterTerrain.remove_terrain(tileset, index):
|
||||
remove_terrain_entry(index)
|
||||
update_tile_view_paint()
|
||||
|
||||
|
||||
func perform_swap_terrain(index1: int, index2: int) -> void:
|
||||
var lower := min(index1, index2)
|
||||
var higher := max(index1, index2)
|
||||
if lower >= terrain_list.get_child_count() or higher >= terrain_list.get_child_count():
|
||||
return
|
||||
var item1 = terrain_list.get_child(lower)
|
||||
var item2 = terrain_list.get_child(higher)
|
||||
if BetterTerrain.swap_terrains(tileset, lower, higher):
|
||||
terrain_list.move_child(item1, higher)
|
||||
item1.terrain = BetterTerrain.get_terrain(tileset, higher)
|
||||
item1.update()
|
||||
item2.terrain = BetterTerrain.get_terrain(tileset, lower)
|
||||
item2.update()
|
||||
selected_entry = index2
|
||||
terrain_list.get_child(index2).set_selected(true)
|
||||
update_tile_view_paint()
|
||||
|
||||
|
||||
func perform_edit_terrain(index: int, name: String, color: Color, type: int, categories: Array, icon: Dictionary = {}) -> void:
|
||||
if index >= terrain_list.get_child_count():
|
||||
return
|
||||
var entry = terrain_list.get_child(index)
|
||||
# don't overwrite empty icon
|
||||
var valid_icon = icon
|
||||
if icon.has("path") and icon.path.is_empty():
|
||||
var terrain = BetterTerrain.get_terrain(tileset, index)
|
||||
valid_icon = terrain.icon
|
||||
if BetterTerrain.set_terrain(tileset, index, name, color, type, categories, valid_icon):
|
||||
entry.terrain = BetterTerrain.get_terrain(tileset, index)
|
||||
entry.update()
|
||||
tile_view.queue_redraw()
|
||||
|
||||
|
||||
func _on_shuffle_random_pressed():
|
||||
BetterTerrain.use_seed = !shuffle_random.button_pressed
|
||||
|
||||
|
||||
func _on_bit_button_pressed(button: BaseButton) -> void:
|
||||
match select_tiles.button_group.get_pressed_button():
|
||||
select_tiles: tile_view.paint_mode = tile_view.PaintMode.SELECT
|
||||
paint_type: tile_view.paint_mode = tile_view.PaintMode.PAINT_TYPE
|
||||
paint_terrain: tile_view.paint_mode = tile_view.PaintMode.PAINT_PEERING
|
||||
paint_symmetry: tile_view.paint_mode = tile_view.PaintMode.PAINT_SYMMETRY
|
||||
_: tile_view.paint_mode = tile_view.PaintMode.NO_PAINT
|
||||
tile_view.queue_redraw()
|
||||
|
||||
symmetry_options.visible = paint_symmetry.button_pressed
|
||||
|
||||
|
||||
func _on_symmetry_selected(index):
|
||||
tile_view.paint_symmetry = index
|
||||
|
||||
|
||||
func _on_paste_occurred():
|
||||
select_tiles.button_pressed = true
|
||||
|
||||
|
||||
func _on_change_zoom_level(value):
|
||||
zoom_slider.value = value
|
||||
|
||||
|
||||
func _on_terrain_updated(index):
|
||||
var entry = terrain_list.get_child(index)
|
||||
entry.terrain = BetterTerrain.get_terrain(tileset, index)
|
||||
entry.update()
|
||||
|
||||
|
||||
func canvas_tilemap_transform() -> Transform2D:
|
||||
var transform := tilemap.get_viewport_transform() * tilemap.global_transform
|
||||
|
||||
# Handle subviewport
|
||||
var editor_viewport := EditorInterface.get_editor_viewport_2d()
|
||||
if tilemap.get_viewport() != editor_viewport:
|
||||
var container = tilemap.get_viewport().get_parent() as SubViewportContainer
|
||||
if container:
|
||||
transform = editor_viewport.global_canvas_transform * container.get_transform() * transform
|
||||
|
||||
return transform
|
||||
|
||||
|
||||
func canvas_draw(overlay: Control) -> void:
|
||||
if !draw_overlay:
|
||||
return
|
||||
|
||||
if selected_entry < 0:
|
||||
return
|
||||
|
||||
var type = selected_entry
|
||||
var terrain := BetterTerrain.get_terrain(tileset, type)
|
||||
if !terrain.valid:
|
||||
return
|
||||
|
||||
var tiles := []
|
||||
var transform := canvas_tilemap_transform()
|
||||
|
||||
if paint_action == PaintAction.RECT and paint_mode != PaintMode.NO_PAINT:
|
||||
var area := Rect2i(initial_click, current_position - initial_click).abs()
|
||||
|
||||
# Shortcut fill for large areas
|
||||
if area.size.x > 1 and area.size.y > 1 and area.size.x * area.size.y > MAX_CANVAS_RENDER_TILES:
|
||||
var shortcut := PackedVector2Array([
|
||||
tilemap.map_to_local(area.position),
|
||||
tilemap.map_to_local(Vector2i(area.end.x, area.position.y)),
|
||||
tilemap.map_to_local(area.end),
|
||||
tilemap.map_to_local(Vector2i(area.position.x, area.end.y))
|
||||
])
|
||||
overlay.draw_colored_polygon(transform * shortcut, Color(terrain.color, 0.5))
|
||||
return
|
||||
|
||||
for y in range(area.position.y, area.end.y + 1):
|
||||
for x in range(area.position.x, area.end.x + 1):
|
||||
tiles.append(Vector2i(x, y))
|
||||
elif paint_action == PaintAction.LINE and paint_mode != PaintMode.NO_PAINT:
|
||||
var cells := _get_tileset_line(initial_click, current_position, tileset)
|
||||
var shape = BetterTerrain.data.cell_polygon(tileset)
|
||||
for c in cells:
|
||||
var tile_transform := Transform2D(0.0, tilemap.tile_set.tile_size, 0.0, tilemap.map_to_local(c))
|
||||
overlay.draw_colored_polygon(transform * tile_transform * shape, Color(terrain.color, 0.5))
|
||||
elif fill_button.button_pressed:
|
||||
tiles = _get_fill_cells(current_position)
|
||||
if tiles.size() > MAX_CANVAS_RENDER_TILES:
|
||||
tiles.resize(MAX_CANVAS_RENDER_TILES)
|
||||
else:
|
||||
tiles.append(current_position)
|
||||
|
||||
var shape = BetterTerrain.data.cell_polygon(tileset)
|
||||
for t in tiles:
|
||||
var tile_transform := Transform2D(0.0, tilemap.tile_set.tile_size, 0.0, tilemap.map_to_local(t))
|
||||
overlay.draw_colored_polygon(transform * tile_transform * shape, Color(terrain.color, 0.5))
|
||||
|
||||
|
||||
func canvas_input(event: InputEvent) -> bool:
|
||||
if selected_entry < 0:
|
||||
return false
|
||||
|
||||
draw_overlay = true
|
||||
if event is InputEventMouseMotion:
|
||||
var tr := canvas_tilemap_transform()
|
||||
var pos := tr.affine_inverse() * Vector2(event.position)
|
||||
var event_position := tilemap.local_to_map(pos)
|
||||
prev_position = current_position
|
||||
if event_position == current_position:
|
||||
return false
|
||||
current_position = event_position
|
||||
update_overlay.emit()
|
||||
|
||||
var replace_mode = replace_button.button_pressed
|
||||
|
||||
var released : bool = event is InputEventMouseButton and !event.pressed
|
||||
if released:
|
||||
terrain_undo.finish_action()
|
||||
var type = selected_entry
|
||||
if paint_action == PaintAction.RECT and paint_mode != PaintMode.NO_PAINT:
|
||||
var area := Rect2i(initial_click, current_position - initial_click).abs()
|
||||
# Fill from initial_target to target
|
||||
undo_manager.create_action(tr("Draw terrain rectangle"), UndoRedo.MERGE_DISABLE, tilemap)
|
||||
for y in range(area.position.y, area.end.y + 1):
|
||||
for x in range(area.position.x, area.end.x + 1):
|
||||
var coord := Vector2i(x, y)
|
||||
if paint_mode == PaintMode.PAINT:
|
||||
if replace_mode:
|
||||
undo_manager.add_do_method(BetterTerrain, &"replace_cell", tilemap, coord, type)
|
||||
else:
|
||||
undo_manager.add_do_method(BetterTerrain, &"set_cell", tilemap, coord, type)
|
||||
else:
|
||||
undo_manager.add_do_method(tilemap, &"erase_cell", coord)
|
||||
|
||||
undo_manager.add_do_method(BetterTerrain, &"update_terrain_area", tilemap, area)
|
||||
terrain_undo.create_tile_restore_point_area(undo_manager, tilemap, area)
|
||||
undo_manager.commit_action()
|
||||
update_overlay.emit()
|
||||
elif paint_action == PaintAction.LINE and paint_mode != PaintMode.NO_PAINT:
|
||||
undo_manager.create_action(tr("Draw terrain line"), UndoRedo.MERGE_DISABLE, tilemap)
|
||||
var cells := _get_tileset_line(initial_click, current_position, tileset)
|
||||
if paint_mode == PaintMode.PAINT:
|
||||
if replace_mode:
|
||||
undo_manager.add_do_method(BetterTerrain, &"replace_cells", tilemap, cells, type)
|
||||
else:
|
||||
undo_manager.add_do_method(BetterTerrain, &"set_cells", tilemap, cells, type)
|
||||
elif paint_mode == PaintMode.ERASE:
|
||||
for c in cells:
|
||||
undo_manager.add_do_method(tilemap, &"erase_cell", c)
|
||||
undo_manager.add_do_method(BetterTerrain, &"update_terrain_cells", tilemap, cells)
|
||||
terrain_undo.create_tile_restore_point(undo_manager, tilemap, cells)
|
||||
undo_manager.commit_action()
|
||||
update_overlay.emit()
|
||||
|
||||
paint_mode = PaintMode.NO_PAINT
|
||||
return true
|
||||
|
||||
var clicked : bool = event is InputEventMouseButton and event.pressed
|
||||
if clicked:
|
||||
paint_mode = PaintMode.NO_PAINT
|
||||
|
||||
if (event.is_command_or_control_pressed() and !event.shift_pressed):
|
||||
var pick = BetterTerrain.get_cell(tilemap, current_position)
|
||||
if pick >= 0:
|
||||
terrain_list.get_children()[pick]._on_focus_entered()
|
||||
#_on_entry_select(pick)
|
||||
return true
|
||||
|
||||
paint_action = PaintAction.NO_ACTION
|
||||
if rectangle_button.button_pressed:
|
||||
paint_action = PaintAction.RECT
|
||||
elif line_button.button_pressed:
|
||||
paint_action = PaintAction.LINE
|
||||
elif draw_button.button_pressed:
|
||||
if event.shift_pressed:
|
||||
paint_action = PaintAction.LINE
|
||||
if event.is_command_or_control_pressed():
|
||||
paint_action = PaintAction.RECT
|
||||
|
||||
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||
paint_mode = PaintMode.PAINT
|
||||
elif event.button_index == MOUSE_BUTTON_RIGHT:
|
||||
paint_mode = PaintMode.ERASE
|
||||
else:
|
||||
return false
|
||||
|
||||
if (clicked or event is InputEventMouseMotion) and paint_mode != PaintMode.NO_PAINT:
|
||||
if clicked:
|
||||
initial_click = current_position
|
||||
terrain_undo.action_index += 1
|
||||
terrain_undo.action_count = 0
|
||||
var type = selected_entry
|
||||
|
||||
if paint_action == PaintAction.LINE or paint_action == PaintAction.RECT:
|
||||
# if painting as line, execution happens on release.
|
||||
# prevent other painting actions from running.
|
||||
pass
|
||||
elif draw_button.button_pressed:
|
||||
undo_manager.create_action(tr("Draw terrain") + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tilemap, true)
|
||||
var cells := _get_tileset_line(prev_position, current_position, tileset)
|
||||
if paint_mode == PaintMode.PAINT:
|
||||
if replace_mode:
|
||||
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"replace_cells", [tilemap, cells, type])
|
||||
else:
|
||||
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_cells", [tilemap, cells, type])
|
||||
elif paint_mode == PaintMode.ERASE:
|
||||
for c in cells:
|
||||
terrain_undo.add_do_method(undo_manager, tilemap, &"erase_cell", [c])
|
||||
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"update_terrain_cells", [tilemap, cells])
|
||||
terrain_undo.create_tile_restore_point(undo_manager, tilemap, cells)
|
||||
undo_manager.commit_action()
|
||||
terrain_undo.action_count += 1
|
||||
elif fill_button.button_pressed:
|
||||
var cells := _get_fill_cells(current_position)
|
||||
undo_manager.create_action(tr("Fill terrain"), UndoRedo.MERGE_DISABLE, tilemap)
|
||||
if paint_mode == PaintMode.PAINT:
|
||||
if replace_mode:
|
||||
undo_manager.add_do_method(BetterTerrain, &"replace_cells", tilemap, cells, type)
|
||||
else:
|
||||
undo_manager.add_do_method(BetterTerrain, &"set_cells", tilemap, cells, type)
|
||||
elif paint_mode == PaintMode.ERASE:
|
||||
for c in cells:
|
||||
undo_manager.add_do_method(tilemap, &"erase_cell", c)
|
||||
undo_manager.add_do_method(BetterTerrain, &"update_terrain_cells", tilemap, cells)
|
||||
terrain_undo.create_tile_restore_point(undo_manager, tilemap, cells)
|
||||
undo_manager.commit_action()
|
||||
|
||||
update_overlay.emit()
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func canvas_mouse_exit() -> void:
|
||||
draw_overlay = false
|
||||
update_overlay.emit()
|
||||
|
||||
|
||||
func _shortcut_input(event) -> void:
|
||||
if event is InputEventKey:
|
||||
if event.keycode == KEY_C and (event.is_command_or_control_pressed() and not event.echo):
|
||||
get_viewport().set_input_as_handled()
|
||||
tile_view.copy_selection()
|
||||
if event.keycode == KEY_V and (event.is_command_or_control_pressed() and not event.echo):
|
||||
get_viewport().set_input_as_handled()
|
||||
tile_view.paste_selection()
|
||||
|
||||
|
||||
## bresenham alg ported from Geometry2D::bresenham_line()
|
||||
func _get_line(from:Vector2i, to:Vector2i) -> Array[Vector2i]:
|
||||
if from == to:
|
||||
return [to]
|
||||
|
||||
var points:Array[Vector2i] = []
|
||||
var delta := (to - from).abs() * 2
|
||||
var step := (to - from).sign()
|
||||
var current := from
|
||||
|
||||
if delta.x > delta.y:
|
||||
var err:int = delta.x / 2
|
||||
while current.x != to.x:
|
||||
points.push_back(current);
|
||||
err -= delta.y
|
||||
if err < 0:
|
||||
current.y += step.y
|
||||
err += delta.x
|
||||
current.x += step.x
|
||||
else:
|
||||
var err:int = delta.y / 2
|
||||
while current.y != to.y:
|
||||
points.push_back(current)
|
||||
err -= delta.x
|
||||
if err < 0:
|
||||
current.x += step.x
|
||||
err += delta.y
|
||||
current.y += step.y
|
||||
|
||||
points.push_back(current);
|
||||
return points;
|
||||
|
||||
|
||||
## half-offset bresenham alg ported from TileMapEditor::get_line
|
||||
func _get_tileset_line(from:Vector2i, to:Vector2i, tileset:TileSet) -> Array[Vector2i]:
|
||||
if tileset.tile_shape == TileSet.TILE_SHAPE_SQUARE:
|
||||
return _get_line(from, to)
|
||||
|
||||
var points:Array[Vector2i] = []
|
||||
|
||||
var transposed := tileset.get_tile_offset_axis() == TileSet.TILE_OFFSET_AXIS_VERTICAL
|
||||
if transposed:
|
||||
from = Vector2i(from.y, from.x)
|
||||
to = Vector2i(to.y, to.x)
|
||||
|
||||
var delta:Vector2i = to - from
|
||||
delta = Vector2i(2 * delta.x + abs(posmod(to.y, 2)) - abs(posmod(from.y, 2)), delta.y)
|
||||
var sign:Vector2i = delta.sign()
|
||||
|
||||
var current := from;
|
||||
points.push_back(Vector2i(current.y, current.x) if transposed else current)
|
||||
|
||||
var err := 0
|
||||
if abs(delta.y) < abs(delta.x):
|
||||
var err_step:Vector2i = 3 * delta.abs()
|
||||
while current != to:
|
||||
err += err_step.y
|
||||
if err > abs(delta.x):
|
||||
if sign.x == 0:
|
||||
current += Vector2i(sign.y, 0)
|
||||
else:
|
||||
current += Vector2i(sign.x if bool(current.y % 2) != (sign.x < 0) else 0, sign.y)
|
||||
err -= err_step.x
|
||||
else:
|
||||
current += Vector2i(sign.x, 0)
|
||||
err += err_step.y
|
||||
points.push_back(Vector2i(current.y, current.x) if transposed else current)
|
||||
else:
|
||||
var err_step:Vector2i = delta.abs()
|
||||
while current != to:
|
||||
err += err_step.x
|
||||
if err > 0:
|
||||
if sign.x == 0:
|
||||
current += Vector2i(0, sign.y)
|
||||
else:
|
||||
current += Vector2i(sign.x if bool(current.y % 2) != (sign.x < 0) else 0, sign.y)
|
||||
err -= err_step.y;
|
||||
else:
|
||||
if sign.x == 0:
|
||||
current += Vector2i(0, sign.y)
|
||||
else:
|
||||
current += Vector2i(-sign.x if bool(current.y % 2) != (sign.x > 0) else 0, sign.y)
|
||||
err += err_step.y
|
||||
points.push_back(Vector2i(current.y, current.x) if transposed else current)
|
||||
|
||||
return points
|
||||
|
||||
|
||||
func _on_terrain_enable_id_pressed(id):
|
||||
if id in [SourceSelectors.ALL, SourceSelectors.NONE]:
|
||||
for i in source_selector_popup.item_count:
|
||||
if source_selector_popup.is_item_checkable(i):
|
||||
source_selector_popup.set_item_checked(i, id == SourceSelectors.ALL)
|
||||
else:
|
||||
var index = source_selector_popup.get_item_index(id)
|
||||
var checked = source_selector_popup.is_item_checked(index)
|
||||
source_selector_popup.set_item_checked(index, !checked)
|
||||
|
||||
var disabled_sources : Array[int]
|
||||
for i in source_selector_popup.item_count:
|
||||
if source_selector_popup.is_item_checkable(i) and !source_selector_popup.is_item_checked(i):
|
||||
disabled_sources.append(source_selector_popup.get_item_id(i))
|
||||
tile_view.disabled_sources = disabled_sources
|
||||
|
||||
|
||||
func corresponding_tilemap_editor_button(similar: Button) -> Button:
|
||||
var editors = EditorInterface.get_base_control().find_children("*", "TileMapLayerEditor", true, false)
|
||||
var tile_map_layer_editor = editors[0]
|
||||
var buttons = tile_map_layer_editor.find_children("*", "Button", true, false)
|
||||
for button: Button in buttons:
|
||||
if button.icon == similar.icon:
|
||||
return button
|
||||
return null
|
||||
|
||||
|
||||
func _on_layer_up_or_down_pressed(button: Button) -> void:
|
||||
var matching_button = corresponding_tilemap_editor_button(button)
|
||||
if !matching_button:
|
||||
return
|
||||
|
||||
# Major hack, to reduce flicker hide the tileset editor briefly
|
||||
var editors = EditorInterface.get_base_control().find_children("*", "TileSetEditor", true, false)
|
||||
var tile_set_editor = editors[0]
|
||||
|
||||
matching_button.pressed.emit()
|
||||
tile_set_editor.modulate = Color.TRANSPARENT
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
force_show_terrains.emit()
|
||||
tile_set_editor.modulate = Color.WHITE
|
||||
|
||||
|
||||
|
||||
func _on_layer_up_pressed() -> void:
|
||||
_on_layer_up_or_down_pressed(layer_up)
|
||||
|
||||
|
||||
func _on_layer_down_pressed() -> void:
|
||||
_on_layer_up_or_down_pressed(layer_down)
|
||||
|
||||
|
||||
func _on_layer_highlight_toggled(toggled: bool) -> void:
|
||||
var settings = EditorInterface.get_editor_settings()
|
||||
settings.set_setting("editors/tiles_editor/highlight_selected_layer", toggled)
|
||||
|
||||
var highlight = corresponding_tilemap_editor_button(layer_highlight)
|
||||
if highlight:
|
||||
highlight.toggled.emit(toggled)
|
||||
|
||||
|
||||
func _on_layer_grid_toggled(toggled: bool) -> void:
|
||||
var settings = EditorInterface.get_editor_settings()
|
||||
settings.set_setting("editors/tiles_editor/display_grid", toggled)
|
||||
|
||||
var grid = corresponding_tilemap_editor_button(layer_grid)
|
||||
if grid:
|
||||
grid.toggled.emit(toggled)
|
1
addons/better-terrain/editor/Dock.gd.uid
Executable file
1
addons/better-terrain/editor/Dock.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://ynajlxcomlkc
|
399
addons/better-terrain/editor/Dock.tscn
Executable file
399
addons/better-terrain/editor/Dock.tscn
Executable file
@@ -0,0 +1,399 @@
|
||||
[gd_scene load_steps=32 format=3 uid="uid://de8b6h6ieal7r"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://ynajlxcomlkc" path="res://addons/better-terrain/editor/Dock.gd" id="1_raoha"]
|
||||
[ext_resource type="Texture2D" uid="uid://c6lxq2y7mpb18" path="res://addons/better-terrain/icons/EditType.svg" id="2_cpm2t"]
|
||||
[ext_resource type="Texture2D" uid="uid://y3xy6qdckht6" path="res://addons/better-terrain/icons/Replace.svg" id="2_fvmt6"]
|
||||
[ext_resource type="Texture2D" uid="uid://bo2cjv08jkvf8" path="res://addons/better-terrain/icons/EditTerrain.svg" id="3_pqb1p"]
|
||||
[ext_resource type="Texture2D" uid="uid://b0es228gfcykd" path="res://addons/better-terrain/icons/Warning.svg" id="4_6ahwe"]
|
||||
[ext_resource type="Script" uid="uid://cpm7dq6r0n0sn" path="res://addons/better-terrain/editor/TileView.gd" id="4_nqppq"]
|
||||
[ext_resource type="Texture2D" uid="uid://co6gwwmog0pjy" path="res://addons/better-terrain/icons/EditSymmetry.svg" id="5_kfjwu"]
|
||||
[ext_resource type="Texture2D" uid="uid://cs4mdmluiydj6" path="res://addons/better-terrain/icons/ShuffleRandom.svg" id="5_n3owo"]
|
||||
[ext_resource type="Texture2D" uid="uid://5hm3bfj3dvej" path="res://addons/better-terrain/icons/SymmetryMirror.svg" id="6_mofuh"]
|
||||
[ext_resource type="Texture2D" uid="uid://dqmc1jp56or8m" path="res://addons/better-terrain/icons/SymmetryFlip.svg" id="7_ojxs0"]
|
||||
[ext_resource type="Texture2D" uid="uid://cxoewno1cefua" path="res://addons/better-terrain/icons/SymmetryReflect.svg" id="8_8dhyg"]
|
||||
[ext_resource type="Texture2D" uid="uid://baxhjy28r1iqj" path="res://addons/better-terrain/icons/SymmetryRotateClockwise.svg" id="9_tq76a"]
|
||||
[ext_resource type="Texture2D" uid="uid://csbwdkr6bc2db" path="res://addons/better-terrain/icons/SymmetryRotateCounterClockwise.svg" id="10_o5h1f"]
|
||||
[ext_resource type="Texture2D" uid="uid://8mcycyl3e66r" path="res://addons/better-terrain/icons/SymmetryRotate180.svg" id="11_m6syp"]
|
||||
[ext_resource type="Texture2D" uid="uid://b7fx4mk18lmls" path="res://addons/better-terrain/icons/SymmetryRotateAll.svg" id="12_11vru"]
|
||||
[ext_resource type="Texture2D" uid="uid://cyjra4g05dwh" path="res://addons/better-terrain/icons/SymmetryAll.svg" id="13_lp5m2"]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_aon7c"]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_saph6"]
|
||||
device = -1
|
||||
keycode = 68
|
||||
unicode = 100
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_3k2al"]
|
||||
events = [SubResource("InputEventKey_saph6")]
|
||||
|
||||
[sub_resource type="SVGTexture" id="SVGTexture_nkf6h"]
|
||||
_source = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\"><path fill=\"#ff5d5d\" d=\"M2 1v8.586l1.293-1.293a1 1 0 0 1 1.414 0L7 10.587l2.293-2.293a1 1 0 0 1 1.414 0L13 10.586l1-1V6H9V1H2zm8 0v4h4zm-6 9.414-2 2V15h12v-2.586l-.293.293a1 1 0 0 1-1.414 0L10 10.414l-2.293 2.293a1 1 0 0 1-1.414 0L4 10.414z\"/></svg>
|
||||
"
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_q1v0d"]
|
||||
device = -1
|
||||
keycode = 76
|
||||
unicode = 108
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_wc6bu"]
|
||||
events = [SubResource("InputEventKey_q1v0d")]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_68n3h"]
|
||||
device = -1
|
||||
keycode = 82
|
||||
unicode = 114
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_qcu1e"]
|
||||
device = -1
|
||||
keycode = 67
|
||||
unicode = 99
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_tcjet"]
|
||||
events = [SubResource("InputEventKey_68n3h"), SubResource("InputEventKey_qcu1e")]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_grxy4"]
|
||||
device = -1
|
||||
keycode = 66
|
||||
unicode = 98
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_46fac"]
|
||||
events = [SubResource("InputEventKey_grxy4")]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_xd61m"]
|
||||
device = -1
|
||||
keycode = 80
|
||||
unicode = 112
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_uwwa1"]
|
||||
events = [SubResource("InputEventKey_xd61m")]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_3wrxn"]
|
||||
allow_unpress = true
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mpeb7"]
|
||||
bg_color = Color(0, 0, 0, 0.4)
|
||||
|
||||
[node name="Dock" type="Control" node_paths=PackedStringArray("shortcut_context")]
|
||||
custom_minimum_size = Vector2(0, 100)
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
focus_mode = 2
|
||||
shortcut_context = NodePath(".")
|
||||
script = ExtResource("1_raoha")
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Toolbar" type="HBoxContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Draw" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Draw terrain
|
||||
Shift: Draw line.
|
||||
Ctrl/Cmd+Shift: Draw rectangle."
|
||||
toggle_mode = true
|
||||
button_pressed = true
|
||||
button_group = SubResource("ButtonGroup_aon7c")
|
||||
shortcut = SubResource("Shortcut_3k2al")
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="Line" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Draw line"
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_aon7c")
|
||||
shortcut = SubResource("Shortcut_wc6bu")
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="Rectangle" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Fill a rectangle of terrain"
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_aon7c")
|
||||
shortcut = SubResource("Shortcut_tcjet")
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="Fill" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Bucket fill terrain"
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_aon7c")
|
||||
shortcut = SubResource("Shortcut_46fac")
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="Replace" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Toggle replace mode"
|
||||
toggle_mode = true
|
||||
shortcut = SubResource("Shortcut_uwwa1")
|
||||
icon = ExtResource("2_fvmt6")
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="SelectTiles" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Select"
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_3wrxn")
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="PaintType" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Paint terrain types"
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_3wrxn")
|
||||
icon = ExtResource("2_cpm2t")
|
||||
flat = true
|
||||
|
||||
[node name="PaintTerrain" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Paint terrain connecting types"
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_3wrxn")
|
||||
icon = ExtResource("3_pqb1p")
|
||||
flat = true
|
||||
|
||||
[node name="PaintSymmetry" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Paint tile symmetry"
|
||||
toggle_mode = true
|
||||
button_group = SubResource("ButtonGroup_3wrxn")
|
||||
icon = ExtResource("5_kfjwu")
|
||||
flat = true
|
||||
|
||||
[node name="SymmetryOptions" type="OptionButton" parent="VBox/Toolbar"]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
selected = 0
|
||||
item_count = 9
|
||||
popup/item_0/text = "No symmetry"
|
||||
popup/item_0/id = 8
|
||||
popup/item_1/text = "Mirror"
|
||||
popup/item_1/icon = ExtResource("6_mofuh")
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Flip"
|
||||
popup/item_2/icon = ExtResource("7_ojxs0")
|
||||
popup/item_2/id = 1
|
||||
popup/item_3/text = "Reflect"
|
||||
popup/item_3/icon = ExtResource("8_8dhyg")
|
||||
popup/item_3/id = 2
|
||||
popup/item_4/text = "Rotate clockwise"
|
||||
popup/item_4/icon = ExtResource("9_tq76a")
|
||||
popup/item_4/id = 3
|
||||
popup/item_5/text = "Rotate counter-clockwise"
|
||||
popup/item_5/icon = ExtResource("10_o5h1f")
|
||||
popup/item_5/id = 4
|
||||
popup/item_6/text = "Rotate 180"
|
||||
popup/item_6/icon = ExtResource("11_m6syp")
|
||||
popup/item_6/id = 5
|
||||
popup/item_7/text = "All rotations"
|
||||
popup/item_7/icon = ExtResource("12_11vru")
|
||||
popup/item_7/id = 6
|
||||
popup/item_8/text = "All reflections & rotations"
|
||||
popup/item_8/icon = ExtResource("13_lp5m2")
|
||||
popup/item_8/id = 7
|
||||
|
||||
[node name="VSeparator3" type="VSeparator" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ZoomContainer" type="VBoxContainer" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="Sources" type="MenuBar" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Sources" type="PopupMenu" parent="VBox/Toolbar/Sources"]
|
||||
auto_translate_mode = 2
|
||||
auto_translate = false
|
||||
hide_on_item_selection = false
|
||||
hide_on_checkable_item_selection = false
|
||||
|
||||
[node name="Spacer" type="Control" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ShuffleRandom" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Shuffle random tiles each update"
|
||||
toggle_mode = true
|
||||
icon = ExtResource("5_n3owo")
|
||||
flat = true
|
||||
|
||||
[node name="Clean" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
text = "Clean tile data"
|
||||
icon = ExtResource("4_6ahwe")
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="LayerUp" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Select previous layer"
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="LayerDown" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Select next layer"
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="LayerHighlight" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Highlight selected layer"
|
||||
toggle_mode = true
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="LayerGrid" type="Button" parent="VBox/Toolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Toggle grid visibility"
|
||||
toggle_mode = true
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="HSplit" type="HSplitContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
split_offset = 325
|
||||
|
||||
[node name="Terrains" type="VBoxContainer" parent="VBox/HSplit"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Panel" type="PanelContainer" parent="VBox/HSplit/Terrains"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_mpeb7")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="VBox/HSplit/Terrains/Panel"]
|
||||
layout_mode = 2
|
||||
horizontal_scroll_mode = 3
|
||||
|
||||
[node name="TerrainList" type="HFlowContainer" parent="VBox/HSplit/Terrains/Panel/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="LowerToolbar" type="HBoxContainer" parent="VBox/HSplit/Terrains"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridMode" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Toggle grid view"
|
||||
toggle_mode = true
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="QuickMode" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar"]
|
||||
auto_translate_mode = 1
|
||||
layout_mode = 2
|
||||
tooltip_text = "Toggle quick mode. Only shows paintable terrain types."
|
||||
toggle_mode = true
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="VBox/HSplit/Terrains/LowerToolbar"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="EditTools" type="HBoxContainer" parent="VBox/HSplit/Terrains/LowerToolbar"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
alignment = 2
|
||||
|
||||
[node name="AddTerrain" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Add terrain type"
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="EditTerrain" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Edit terrain type"
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="PickIcon" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Pick terrain icon from tileset"
|
||||
toggle_mode = true
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="MoveUp" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Move selected terrain up"
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="MoveDown" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Move selected terrain down"
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="RemoveTerrain" type="Button" parent="VBox/HSplit/Terrains/LowerToolbar/EditTools"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Remove selected terrain type(s)"
|
||||
icon = SubResource("SVGTexture_nkf6h")
|
||||
flat = true
|
||||
|
||||
[node name="Panel" type="Panel" parent="VBox/HSplit"]
|
||||
custom_minimum_size = Vector2(0, 80)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ScrollArea" type="ScrollContainer" parent="VBox/HSplit/Panel"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="TileView" type="Control" parent="VBox/HSplit/Panel/ScrollArea"]
|
||||
texture_filter = 1
|
||||
texture_repeat = 1
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 2
|
||||
script = ExtResource("4_nqppq")
|
||||
|
||||
[connection signal="item_selected" from="VBox/Toolbar/SymmetryOptions" to="." method="_on_symmetry_selected"]
|
||||
[connection signal="id_pressed" from="VBox/Toolbar/Sources/Sources" to="." method="_on_terrain_enable_id_pressed"]
|
||||
[connection signal="pressed" from="VBox/Toolbar/ShuffleRandom" to="." method="_on_shuffle_random_pressed"]
|
||||
[connection signal="pressed" from="VBox/Toolbar/Clean" to="." method="_on_clean_pressed"]
|
||||
[connection signal="pressed" from="VBox/Toolbar/LayerUp" to="." method="_on_layer_up_pressed"]
|
||||
[connection signal="pressed" from="VBox/Toolbar/LayerDown" to="." method="_on_layer_down_pressed"]
|
||||
[connection signal="toggled" from="VBox/Toolbar/LayerHighlight" to="." method="_on_layer_highlight_toggled"]
|
||||
[connection signal="toggled" from="VBox/Toolbar/LayerGrid" to="." method="_on_layer_grid_toggled"]
|
||||
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/GridMode" to="." method="_on_grid_mode_pressed"]
|
||||
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/QuickMode" to="." method="_on_quick_mode_pressed"]
|
||||
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/AddTerrain" to="." method="_on_add_terrain_pressed"]
|
||||
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/EditTerrain" to="." method="_on_edit_terrain_pressed"]
|
||||
[connection signal="focus_exited" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/PickIcon" to="." method="_on_pick_icon_focus_exited"]
|
||||
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/PickIcon" to="." method="_on_pick_icon_pressed"]
|
||||
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/MoveUp" to="." method="_on_move_pressed" binds= [false]]
|
||||
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/MoveDown" to="." method="_on_move_pressed" binds= [true]]
|
||||
[connection signal="pressed" from="VBox/HSplit/Terrains/LowerToolbar/EditTools/RemoveTerrain" to="." method="_on_remove_terrain_pressed"]
|
||||
[connection signal="mouse_exited" from="VBox/HSplit/Panel/ScrollArea/TileView" to="VBox/HSplit/Panel/ScrollArea/TileView" method="clear_highlighted_tile"]
|
185
addons/better-terrain/editor/TerrainEntry.gd
Executable file
185
addons/better-terrain/editor/TerrainEntry.gd
Executable file
@@ -0,0 +1,185 @@
|
||||
@tool
|
||||
extends PanelContainer
|
||||
|
||||
signal select(index)
|
||||
|
||||
@onready var color_panel := %Color
|
||||
@onready var terrain_icon_slot := %TerrainIcon
|
||||
@onready var type_icon_slot := %TypeIcon
|
||||
@onready var type_icon_panel := %TerrainIconPanel
|
||||
@onready var name_label := %Name
|
||||
@onready var layout_container := %Layout
|
||||
@onready var icon_layout_container := %IconLayout
|
||||
|
||||
var selected := false
|
||||
|
||||
var tileset:TileSet
|
||||
var terrain:Dictionary
|
||||
|
||||
var grid_mode := false
|
||||
var color_style_list:StyleBoxFlat
|
||||
var color_style_grid:StyleBoxFlat
|
||||
var color_style_decoration:StyleBoxFlat
|
||||
|
||||
var _terrain_texture:Texture2D
|
||||
var _terrain_texture_rect:Rect2i
|
||||
var _icon_draw_connected := false
|
||||
|
||||
|
||||
func _ready():
|
||||
update()
|
||||
|
||||
func update():
|
||||
if !terrain or !terrain.valid:
|
||||
return
|
||||
if !tileset:
|
||||
return
|
||||
|
||||
name_label.text = terrain.name
|
||||
tooltip_text = "%s (%d)" % [terrain.name, terrain.id]
|
||||
|
||||
color_style_list = color_panel.get_theme_stylebox("panel").duplicate()
|
||||
color_style_grid = color_panel.get_theme_stylebox("panel").duplicate()
|
||||
color_style_decoration = color_panel.get_theme_stylebox("panel").duplicate()
|
||||
|
||||
color_style_list.bg_color = terrain.color
|
||||
color_style_list.corner_radius_top_left = 8
|
||||
color_style_list.corner_radius_bottom_left = 8
|
||||
color_style_list.corner_radius_top_right = 0
|
||||
color_style_list.corner_radius_bottom_right = 0
|
||||
color_style_list.content_margin_left = -1
|
||||
color_style_list.content_margin_right = -1
|
||||
color_style_list.border_width_left = 0
|
||||
color_style_list.border_width_right = 0
|
||||
color_style_list.border_width_top = 0
|
||||
color_style_list.border_width_bottom = 0
|
||||
|
||||
color_style_grid.bg_color = terrain.color
|
||||
color_style_grid.corner_radius_top_left = 6
|
||||
color_style_grid.corner_radius_bottom_left = 6
|
||||
color_style_grid.corner_radius_top_right = 6
|
||||
color_style_grid.corner_radius_bottom_right = 6
|
||||
color_style_grid.content_margin_left = -1
|
||||
color_style_grid.content_margin_right = -1
|
||||
color_style_grid.border_width_left = 0
|
||||
color_style_grid.border_width_right = 0
|
||||
color_style_grid.border_width_top = 0
|
||||
color_style_grid.border_width_bottom = 0
|
||||
|
||||
color_style_decoration.bg_color = terrain.color
|
||||
color_style_decoration.corner_radius_top_left = 8
|
||||
color_style_decoration.corner_radius_bottom_left = 8
|
||||
color_style_decoration.corner_radius_top_right = 8
|
||||
color_style_decoration.corner_radius_bottom_right = 8
|
||||
color_style_decoration.content_margin_left = -1
|
||||
color_style_decoration.content_margin_right = -1
|
||||
color_style_decoration.border_width_left = 4
|
||||
color_style_decoration.border_width_right = 4
|
||||
color_style_decoration.border_width_top = 4
|
||||
color_style_decoration.border_width_bottom = 4
|
||||
|
||||
match terrain.type:
|
||||
BetterTerrain.TerrainType.MATCH_TILES:
|
||||
type_icon_slot.texture = load("res://addons/better-terrain/icons/MatchTiles.svg")
|
||||
BetterTerrain.TerrainType.MATCH_VERTICES:
|
||||
type_icon_slot.texture = load("res://addons/better-terrain/icons/MatchVertices.svg")
|
||||
BetterTerrain.TerrainType.CATEGORY:
|
||||
type_icon_slot.texture = load("res://addons/better-terrain/icons/NonModifying.svg")
|
||||
BetterTerrain.TerrainType.DECORATION:
|
||||
type_icon_slot.texture = load("res://addons/better-terrain/icons/Decoration.svg")
|
||||
|
||||
var has_icon = false
|
||||
if terrain.has("icon"):
|
||||
if terrain.icon.has("path") and not terrain.icon.path.is_empty():
|
||||
terrain_icon_slot.texture = load(terrain.icon.path)
|
||||
_terrain_texture = null
|
||||
terrain_icon_slot.queue_redraw()
|
||||
has_icon = true
|
||||
elif terrain.icon.has("source_id") and tileset.has_source(terrain.icon.source_id):
|
||||
var source := tileset.get_source(terrain.icon.source_id) as TileSetAtlasSource
|
||||
var coord := terrain.icon.coord as Vector2i
|
||||
var rect := source.get_tile_texture_region(coord, 0)
|
||||
_terrain_texture = source.texture
|
||||
_terrain_texture_rect = rect
|
||||
terrain_icon_slot.queue_redraw()
|
||||
has_icon = true
|
||||
|
||||
if not has_icon:
|
||||
var tiles = BetterTerrain.get_tile_sources_in_terrain(tileset, get_index())
|
||||
if tiles.size() > 0:
|
||||
var source := tiles[0].source as TileSetAtlasSource
|
||||
var coord := tiles[0].coord as Vector2i
|
||||
var rect := source.get_tile_texture_region(coord, 0)
|
||||
_terrain_texture = source.texture
|
||||
_terrain_texture_rect = rect
|
||||
terrain_icon_slot.queue_redraw()
|
||||
|
||||
if _terrain_texture:
|
||||
terrain_icon_slot.texture = null
|
||||
|
||||
if not _icon_draw_connected:
|
||||
terrain_icon_slot.connect("draw", func():
|
||||
if _terrain_texture:
|
||||
terrain_icon_slot.draw_texture_rect_region(_terrain_texture, Rect2i(0,0, 44, 44), _terrain_texture_rect)
|
||||
)
|
||||
_icon_draw_connected = true
|
||||
|
||||
update_style()
|
||||
|
||||
|
||||
func update_style():
|
||||
if terrain.type == BetterTerrain.TerrainType.DECORATION:
|
||||
type_icon_panel.visible = false
|
||||
color_panel.custom_minimum_size = Vector2i(52,52)
|
||||
else:
|
||||
type_icon_panel.visible = true
|
||||
color_panel.custom_minimum_size = Vector2i(24,24)
|
||||
|
||||
if grid_mode:
|
||||
if terrain.type == BetterTerrain.TerrainType.DECORATION:
|
||||
color_panel.add_theme_stylebox_override("panel", color_style_decoration)
|
||||
color_panel.size_flags_vertical = Control.SIZE_FILL
|
||||
icon_layout_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
else:
|
||||
color_panel.add_theme_stylebox_override("panel", color_style_grid)
|
||||
color_panel.size_flags_vertical = Control.SIZE_SHRINK_BEGIN
|
||||
icon_layout_container.size_flags_vertical = Control.SIZE_FILL
|
||||
custom_minimum_size = Vector2(0, 60)
|
||||
size_flags_horizontal = Control.SIZE_FILL
|
||||
layout_container.vertical = true
|
||||
name_label.visible = false
|
||||
icon_layout_container.add_theme_constant_override("separation", -24)
|
||||
else:
|
||||
if terrain.type == BetterTerrain.TerrainType.DECORATION:
|
||||
color_panel.add_theme_stylebox_override("panel", color_style_decoration)
|
||||
else:
|
||||
color_panel.add_theme_stylebox_override("panel", color_style_list)
|
||||
icon_layout_container.size_flags_vertical = Control.SIZE_FILL
|
||||
custom_minimum_size = Vector2(2000, 60)
|
||||
size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
layout_container.vertical = false
|
||||
name_label.visible = true
|
||||
color_panel.size_flags_vertical = Control.SIZE_FILL
|
||||
icon_layout_container.add_theme_constant_override("separation", 4)
|
||||
|
||||
|
||||
func set_selected(value:bool = true):
|
||||
selected = value
|
||||
if value:
|
||||
select.emit(get_index())
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _draw():
|
||||
if selected:
|
||||
draw_rect(Rect2(Vector2.ZERO, get_rect().size), Color(0.15, 0.70, 1, 0.3))
|
||||
|
||||
|
||||
func _on_focus_entered():
|
||||
queue_redraw()
|
||||
selected = true
|
||||
select.emit(get_index())
|
||||
|
||||
|
||||
func _on_focus_exited():
|
||||
queue_redraw()
|
1
addons/better-terrain/editor/TerrainEntry.gd.uid
Executable file
1
addons/better-terrain/editor/TerrainEntry.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://c2qfovpuj58b7
|
114
addons/better-terrain/editor/TerrainEntry.tscn
Executable file
114
addons/better-terrain/editor/TerrainEntry.tscn
Executable file
@@ -0,0 +1,114 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://u2y444hj182c"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c2qfovpuj58b7" path="res://addons/better-terrain/editor/TerrainEntry.gd" id="1_o2na3"]
|
||||
[ext_resource type="Texture2D" uid="uid://kmypxsqhynyv" path="res://addons/better-terrain/icons/Decoration.svg" id="2_ossyj"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3pdcc"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 4.0
|
||||
draw_center = false
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dqhir"]
|
||||
bg_color = Color(0.243, 0.816, 0.518, 1)
|
||||
border_color = Color(0, 0, 0, 0.439216)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rohyw"]
|
||||
content_margin_left = 2.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_right = 2.0
|
||||
content_margin_bottom = 2.0
|
||||
bg_color = Color(0, 0, 0, 0.439216)
|
||||
corner_radius_top_left = 4
|
||||
corner_radius_top_right = 4
|
||||
corner_radius_bottom_right = 4
|
||||
corner_radius_bottom_left = 4
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_xa0fl"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 4.0
|
||||
bg_color = Color(0, 0, 0, 0.439216)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_b4rkm"]
|
||||
content_margin_left = 3.0
|
||||
bg_color = Color(0, 0, 0, 0.439216)
|
||||
draw_center = false
|
||||
|
||||
[node name="TerrainEntry" type="PanelContainer"]
|
||||
custom_minimum_size = Vector2(60, 60)
|
||||
offset_right = 200.0
|
||||
offset_bottom = 60.0
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_3pdcc")
|
||||
script = ExtResource("1_o2na3")
|
||||
|
||||
[node name="Layout" type="BoxContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="IconLayout" type="HBoxContainer" parent="Layout"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
|
||||
[node name="Color" type="PanelContainer" parent="Layout/IconLayout"]
|
||||
unique_name_in_owner = true
|
||||
z_index = 1
|
||||
custom_minimum_size = Vector2(24, 24)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 0
|
||||
mouse_filter = 1
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_dqhir")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="Layout/IconLayout/Color"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
mouse_filter = 1
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_rohyw")
|
||||
|
||||
[node name="TypeIcon" type="TextureRect" parent="Layout/IconLayout/Color/PanelContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
texture = ExtResource("2_ossyj")
|
||||
|
||||
[node name="TerrainIconPanel" type="PanelContainer" parent="Layout/IconLayout"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(52, 52)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
mouse_filter = 1
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_xa0fl")
|
||||
|
||||
[node name="TerrainIcon" type="TextureRect" parent="Layout/IconLayout/TerrainIconPanel"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 1
|
||||
custom_minimum_size = Vector2(40, 40)
|
||||
layout_mode = 2
|
||||
expand_mode = 4
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="Name" type="Label" parent="Layout"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/outline_size = 0
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_b4rkm")
|
||||
text = "New Terrain"
|
||||
vertical_alignment = 1
|
||||
text_overrun_behavior = 3
|
||||
|
||||
[connection signal="focus_entered" from="." to="." method="_on_focus_entered"]
|
||||
[connection signal="focus_exited" from="." to="." method="_on_focus_exited"]
|
85
addons/better-terrain/editor/TerrainProperties.gd
Executable file
85
addons/better-terrain/editor/TerrainProperties.gd
Executable file
@@ -0,0 +1,85 @@
|
||||
@tool
|
||||
extends ConfirmationDialog
|
||||
|
||||
var category_icon := load("res://addons/better-terrain/icons/NonModifying.svg")
|
||||
|
||||
const CATEGORY_CHECK_ID = &"category_check_id"
|
||||
|
||||
var accepted := false
|
||||
|
||||
var terrain_name : String:
|
||||
set(value): %NameEdit.text = value
|
||||
get: return %NameEdit.text
|
||||
|
||||
var terrain_color : Color:
|
||||
set(value): %ColorPicker.color = value
|
||||
get: return %ColorPicker.color
|
||||
|
||||
var terrain_icon : String:
|
||||
set(value): %IconEdit.text = value
|
||||
get: return %IconEdit.text
|
||||
|
||||
var terrain_type : int:
|
||||
set(value):
|
||||
%TypeOption.selected = value
|
||||
_on_type_option_item_selected(value)
|
||||
get: return %TypeOption.selected
|
||||
|
||||
var terrain_categories : Array: set = set_categories, get = get_categories
|
||||
|
||||
|
||||
# category is name, color, id
|
||||
func set_category_data(options: Array) -> void:
|
||||
if !options.is_empty():
|
||||
%CategoryLabel.show()
|
||||
%CategoryContainer.show()
|
||||
|
||||
for o in options:
|
||||
var c = CheckBox.new()
|
||||
c.text = o.name
|
||||
c.icon = category_icon
|
||||
c.add_theme_color_override(&"icon_normal_color", o.color)
|
||||
c.add_theme_color_override(&"icon_disabled_color", Color(o.color, 0.4))
|
||||
c.add_theme_color_override(&"icon_focus_color", o.color)
|
||||
c.add_theme_color_override(&"icon_hover_color", o.color)
|
||||
c.add_theme_color_override(&"icon_hover_pressed_color", o.color)
|
||||
c.add_theme_color_override(&"icon_normal_color", o.color)
|
||||
c.add_theme_color_override(&"icon_pressed_color", o.color)
|
||||
|
||||
c.set_meta(CATEGORY_CHECK_ID, o.id)
|
||||
%CategoryLayout.add_child(c)
|
||||
|
||||
|
||||
func set_categories(ids : Array):
|
||||
for c in %CategoryLayout.get_children():
|
||||
c.button_pressed = c.get_meta(CATEGORY_CHECK_ID) in ids
|
||||
|
||||
|
||||
func get_categories() -> Array:
|
||||
var result := []
|
||||
if terrain_type == BetterTerrain.TerrainType.CATEGORY:
|
||||
return result
|
||||
for c in %CategoryLayout.get_children():
|
||||
if c.button_pressed:
|
||||
result.push_back(c.get_meta(CATEGORY_CHECK_ID))
|
||||
return result
|
||||
|
||||
|
||||
func _on_confirmed() -> void:
|
||||
# confirm valid name
|
||||
if terrain_name.is_empty():
|
||||
var dialog := AcceptDialog.new()
|
||||
dialog.dialog_text = "Name cannot be empty"
|
||||
EditorInterface.popup_dialog_centered(dialog)
|
||||
await dialog.visibility_changed
|
||||
dialog.queue_free()
|
||||
return
|
||||
|
||||
accepted = true
|
||||
hide()
|
||||
|
||||
|
||||
func _on_type_option_item_selected(index: int) -> void:
|
||||
var categories_available = (index != BetterTerrain.TerrainType.CATEGORY)
|
||||
for c in %CategoryLayout.get_children():
|
||||
c.disabled = !categories_available
|
1
addons/better-terrain/editor/TerrainProperties.gd.uid
Executable file
1
addons/better-terrain/editor/TerrainProperties.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://j81f0xo4p36y
|
96
addons/better-terrain/editor/TerrainProperties.tscn
Normal file
96
addons/better-terrain/editor/TerrainProperties.tscn
Normal file
@@ -0,0 +1,96 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://fdjybw6e7whr"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://j81f0xo4p36y" path="res://addons/better-terrain/editor/TerrainProperties.gd" id="1_52nx8"]
|
||||
[ext_resource type="Texture2D" uid="uid://d1h1p7pcwdnjk" path="res://addons/better-terrain/icons/MatchTiles.svg" id="2_ncc5p"]
|
||||
[ext_resource type="Texture2D" uid="uid://dfemy1g6okwlv" path="res://addons/better-terrain/icons/MatchVertices.svg" id="3_0nvmi"]
|
||||
[ext_resource type="Texture2D" uid="uid://1yr6yruwl63u" path="res://addons/better-terrain/icons/NonModifying.svg" id="5_awp83"]
|
||||
|
||||
[node name="TerrainProperties" type="ConfirmationDialog"]
|
||||
auto_translate_mode = 2
|
||||
oversampling_override = 1.0
|
||||
title = "Edit terrain properties"
|
||||
initial_position = 2
|
||||
size = Vector2i(317, 257)
|
||||
visible = true
|
||||
dialog_hide_on_ok = false
|
||||
script = ExtResource("1_52nx8")
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="."]
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 309.0
|
||||
offset_bottom = 212.0
|
||||
columns = 2
|
||||
|
||||
[node name="NameLabel" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Name"
|
||||
|
||||
[node name="NameEdit" type="LineEdit" parent="GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Terrain name"
|
||||
|
||||
[node name="ColorLabel" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Color"
|
||||
|
||||
[node name="ColorPicker" type="ColorPickerButton" parent="GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
color = Color(1, 0.262745, 0.498039, 1)
|
||||
edit_alpha = false
|
||||
|
||||
[node name="IconLabel" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Icon"
|
||||
|
||||
[node name="IconEdit" type="LineEdit" parent="GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Icon path (optional)"
|
||||
|
||||
[node name="TypeLabel" type="Label" parent="GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Mode"
|
||||
|
||||
[node name="TypeOption" type="OptionButton" parent="GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
item_count = 3
|
||||
popup/item_0/text = "Match tiles"
|
||||
popup/item_0/icon = ExtResource("2_ncc5p")
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "Match vertices"
|
||||
popup/item_1/icon = ExtResource("3_0nvmi")
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Category"
|
||||
popup/item_2/icon = ExtResource("5_awp83")
|
||||
popup/item_2/id = 2
|
||||
|
||||
[node name="CategoryLabel" type="Label" parent="GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 1
|
||||
text = "Categories"
|
||||
|
||||
[node name="CategoryContainer" type="ScrollContainer" parent="GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(0, 100)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="CategoryLayout" type="VBoxContainer" parent="GridContainer/CategoryContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 100)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[connection signal="confirmed" from="." to="." method="_on_confirmed"]
|
||||
[connection signal="item_selected" from="GridContainer/TypeOption" to="." method="_on_type_option_item_selected"]
|
190
addons/better-terrain/editor/TerrainUndo.gd
Executable file
190
addons/better-terrain/editor/TerrainUndo.gd
Executable file
@@ -0,0 +1,190 @@
|
||||
@tool
|
||||
extends Node
|
||||
|
||||
var action_index := 0
|
||||
var action_count := 0
|
||||
var _current_action_index := 0
|
||||
var _current_action_count := 0
|
||||
|
||||
func create_tile_restore_point(undo_manager: EditorUndoRedoManager, tm: TileMapLayer, cells: Array, and_surrounding_cells: bool = true) -> void:
|
||||
if and_surrounding_cells:
|
||||
cells = BetterTerrain._widen(tm, cells)
|
||||
|
||||
var restore := []
|
||||
for c in cells:
|
||||
restore.append([
|
||||
c,
|
||||
tm.get_cell_source_id(c),
|
||||
tm.get_cell_atlas_coords(c),
|
||||
tm.get_cell_alternative_tile(c)
|
||||
])
|
||||
|
||||
undo_manager.add_undo_method(self, &"restore_tiles", tm, restore)
|
||||
|
||||
|
||||
func create_tile_restore_point_area(undo_manager: EditorUndoRedoManager, tm: TileMapLayer, area: Rect2i, and_surrounding_cells: bool = true) -> void:
|
||||
area.end += Vector2i.ONE
|
||||
|
||||
var restore := []
|
||||
for y in range(area.position.y, area.end.y):
|
||||
for x in range(area.position.x, area.end.x):
|
||||
var c := Vector2i(x, y)
|
||||
restore.append([
|
||||
c,
|
||||
tm.get_cell_source_id(c),
|
||||
tm.get_cell_atlas_coords(c),
|
||||
tm.get_cell_alternative_tile(c)
|
||||
])
|
||||
|
||||
undo_manager.add_undo_method(self, &"restore_tiles", tm, restore)
|
||||
|
||||
if !and_surrounding_cells:
|
||||
return
|
||||
|
||||
var edges := []
|
||||
for x in range(area.position.x, area.end.x):
|
||||
edges.append(Vector2i(x, area.position.y))
|
||||
edges.append(Vector2i(x, area.end.y))
|
||||
for y in range(area.position.y + 1, area.end.y - 1):
|
||||
edges.append(Vector2i(area.position.x, y))
|
||||
edges.append(Vector2i(area.end.x, y))
|
||||
|
||||
edges = BetterTerrain._widen_with_exclusion(tm, edges, area)
|
||||
create_tile_restore_point(undo_manager, tm, edges, false)
|
||||
|
||||
|
||||
func restore_tiles(tm: TileMapLayer, restore: Array) -> void:
|
||||
for r in restore:
|
||||
tm.set_cell(r[0], r[1], r[2], r[3])
|
||||
|
||||
|
||||
func create_peering_restore_point(undo_manager: EditorUndoRedoManager, ts: TileSet) -> void:
|
||||
var restore := []
|
||||
|
||||
for s in ts.get_source_count():
|
||||
var source_id := ts.get_source_id(s)
|
||||
var source := ts.get_source(source_id) as TileSetAtlasSource
|
||||
if !source:
|
||||
continue
|
||||
|
||||
for t in source.get_tiles_count():
|
||||
var coord := source.get_tile_id(t)
|
||||
for a in source.get_alternative_tiles_count(coord):
|
||||
var alternate := source.get_alternative_tile_id(coord, a)
|
||||
|
||||
var td := source.get_tile_data(coord, alternate)
|
||||
var tile_type := BetterTerrain.get_tile_terrain_type(td)
|
||||
if tile_type == BetterTerrain.TileCategory.NON_TERRAIN:
|
||||
continue
|
||||
|
||||
var peering_dict := {}
|
||||
for c in BetterTerrain.tile_peering_keys(td):
|
||||
peering_dict[c] = BetterTerrain.tile_peering_types(td, c)
|
||||
var symmetry = BetterTerrain.get_tile_symmetry_type(td)
|
||||
restore.append([source_id, coord, alternate, tile_type, peering_dict, symmetry])
|
||||
|
||||
undo_manager.add_undo_method(self, &"restore_peering", ts, restore)
|
||||
|
||||
|
||||
func create_peering_restore_point_specific(undo_manager: EditorUndoRedoManager, ts: TileSet, protect: int) -> void:
|
||||
var restore := []
|
||||
|
||||
for s in ts.get_source_count():
|
||||
var source_id := ts.get_source_id(s)
|
||||
var source := ts.get_source(source_id) as TileSetAtlasSource
|
||||
if !source:
|
||||
continue
|
||||
|
||||
for t in source.get_tiles_count():
|
||||
var coord := source.get_tile_id(t)
|
||||
for a in source.get_alternative_tiles_count(coord):
|
||||
var alternate := source.get_alternative_tile_id(coord, a)
|
||||
|
||||
var td := source.get_tile_data(coord, alternate)
|
||||
var tile_type := BetterTerrain.get_tile_terrain_type(td)
|
||||
if tile_type == BetterTerrain.TileCategory.NON_TERRAIN:
|
||||
continue
|
||||
|
||||
var to_restore : bool = tile_type == protect
|
||||
|
||||
var terrain := BetterTerrain.get_terrain(ts, tile_type)
|
||||
var cells = BetterTerrain.data.get_terrain_peering_cells(ts, terrain.type)
|
||||
for c in cells:
|
||||
if protect in BetterTerrain.tile_peering_types(td, c):
|
||||
to_restore = true
|
||||
break
|
||||
|
||||
if !to_restore:
|
||||
continue
|
||||
|
||||
var peering_dict := {}
|
||||
for c in cells:
|
||||
peering_dict[c] = BetterTerrain.tile_peering_types(td, c)
|
||||
var symmetry = BetterTerrain.get_tile_symmetry_type(td)
|
||||
restore.append([source_id, coord, alternate, tile_type, peering_dict, symmetry])
|
||||
|
||||
undo_manager.add_undo_method(self, &"restore_peering", ts, restore)
|
||||
|
||||
|
||||
func create_peering_restore_point_tile(undo_manager: EditorUndoRedoManager, ts: TileSet, source_id: int, coord: Vector2i, alternate: int) -> void:
|
||||
var source := ts.get_source(source_id) as TileSetAtlasSource
|
||||
var td := source.get_tile_data(coord, alternate)
|
||||
var tile_type := BetterTerrain.get_tile_terrain_type(td)
|
||||
|
||||
var restore := []
|
||||
var peering_dict := {}
|
||||
for c in BetterTerrain.tile_peering_keys(td):
|
||||
peering_dict[c] = BetterTerrain.tile_peering_types(td, c)
|
||||
var symmetry = BetterTerrain.get_tile_symmetry_type(td)
|
||||
restore.append([source_id, coord, alternate, tile_type, peering_dict, symmetry])
|
||||
|
||||
undo_manager.add_undo_method(self, &"restore_peering", ts, restore)
|
||||
|
||||
|
||||
func restore_peering(ts: TileSet, restore: Array) -> void:
|
||||
for r in restore:
|
||||
var source := ts.get_source(r[0]) as TileSetAtlasSource
|
||||
var td := source.get_tile_data(r[1], r[2])
|
||||
BetterTerrain.set_tile_terrain_type(ts, td, r[3])
|
||||
var peering_types = r[4]
|
||||
for peering in peering_types:
|
||||
var types := BetterTerrain.tile_peering_types(td, peering)
|
||||
for t in types:
|
||||
BetterTerrain.remove_tile_peering_type(ts, td, peering, t)
|
||||
for t in peering_types[peering]:
|
||||
BetterTerrain.add_tile_peering_type(ts, td, peering, t)
|
||||
var symmetry = r[5]
|
||||
BetterTerrain.set_tile_symmetry_type(ts, td, symmetry)
|
||||
|
||||
|
||||
func create_terrain_type_restore_point(undo_manager: EditorUndoRedoManager, ts: TileSet) -> void:
|
||||
var count = BetterTerrain.terrain_count(ts)
|
||||
var restore = []
|
||||
for i in count:
|
||||
restore.push_back(BetterTerrain.get_terrain(ts, i))
|
||||
|
||||
undo_manager.add_undo_method(self, &"restore_terrain", ts, restore)
|
||||
|
||||
|
||||
func restore_terrain(ts: TileSet, restore: Array) -> void:
|
||||
for i in restore.size():
|
||||
var r = restore[i]
|
||||
BetterTerrain.set_terrain(ts, i, r.name, r.color, r.type, r.categories, r.icon)
|
||||
|
||||
|
||||
func add_do_method(undo_manager: EditorUndoRedoManager, object:Object, method:StringName, args:Array):
|
||||
if action_index > _current_action_index:
|
||||
_current_action_index = action_index
|
||||
_current_action_count = action_count
|
||||
if action_count > _current_action_count:
|
||||
_current_action_count = action_count
|
||||
undo_manager.add_do_method(self, "_do_method", object, method, args, action_count)
|
||||
|
||||
|
||||
func _do_method(object:Object, method:StringName, args:Array, this_action_count:int):
|
||||
if this_action_count >= _current_action_count:
|
||||
object.callv(method, args)
|
||||
|
||||
|
||||
func finish_action():
|
||||
_current_action_count = 0
|
1
addons/better-terrain/editor/TerrainUndo.gd.uid
Executable file
1
addons/better-terrain/editor/TerrainUndo.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://ds1kcnrnbywo6
|
896
addons/better-terrain/editor/TileView.gd
Executable file
896
addons/better-terrain/editor/TileView.gd
Executable file
@@ -0,0 +1,896 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
signal paste_occurred
|
||||
signal change_zoom_level(value)
|
||||
signal terrain_updated(index)
|
||||
|
||||
@onready var checkerboard := get_theme_icon("Checkerboard", "EditorIcons")
|
||||
|
||||
@onready var paint_symmetry_icons := [
|
||||
null,
|
||||
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryMirror.svg"),
|
||||
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryFlip.svg"),
|
||||
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryReflect.svg"),
|
||||
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryRotateClockwise.svg"),
|
||||
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryRotateCounterClockwise.svg"),
|
||||
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryRotate180.svg"),
|
||||
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryRotateAll.svg"),
|
||||
preload("res://addons/better-terrain/icons/paint-symmetry/SymmetryAll.svg"),
|
||||
]
|
||||
|
||||
# Draw checkerboard and tiles with specific materials in
|
||||
# individual canvas items via rendering server
|
||||
var _canvas_item_map = {}
|
||||
var _canvas_item_background : RID
|
||||
|
||||
var tileset: TileSet
|
||||
var disabled_sources: Array[int] = []: set = set_disabled_sources
|
||||
|
||||
var paint := BetterTerrain.TileCategory.NON_TERRAIN
|
||||
var paint_symmetry := BetterTerrain.SymmetryType.NONE
|
||||
var highlighted_tile_part := { valid = false }
|
||||
var zoom_level := 1.0
|
||||
|
||||
var tiles_size : Vector2
|
||||
var tile_size : Vector2i
|
||||
var tile_part_size : Vector2
|
||||
var alternate_size : Vector2
|
||||
var alternate_lookup := []
|
||||
var initial_click : Vector2i
|
||||
var prev_position : Vector2i
|
||||
var current_position : Vector2i
|
||||
|
||||
var selection_start : Vector2i
|
||||
var selection_end : Vector2i
|
||||
var selection_rect : Rect2i
|
||||
var selected_tile_states : Array[Dictionary] = []
|
||||
var copied_tile_states : Array[Dictionary] = []
|
||||
var staged_paste_tile_states : Array[Dictionary] = []
|
||||
|
||||
var pick_icon_terrain : int = -1
|
||||
var pick_icon_terrain_cancel := false
|
||||
|
||||
var undo_manager : EditorUndoRedoManager
|
||||
var terrain_undo
|
||||
|
||||
# Modes for painting
|
||||
enum PaintMode {
|
||||
NO_PAINT,
|
||||
PAINT_TYPE,
|
||||
PAINT_PEERING,
|
||||
PAINT_SYMMETRY,
|
||||
SELECT,
|
||||
PASTE
|
||||
}
|
||||
|
||||
var paint_mode := PaintMode.NO_PAINT
|
||||
|
||||
# Actual interactions for painting
|
||||
enum PaintAction {
|
||||
NO_ACTION,
|
||||
DRAW_TYPE,
|
||||
ERASE_TYPE,
|
||||
DRAW_PEERING,
|
||||
ERASE_PEERING,
|
||||
DRAW_SYMMETRY,
|
||||
ERASE_SYMMETRY,
|
||||
SELECT,
|
||||
PASTE
|
||||
}
|
||||
|
||||
var paint_action := PaintAction.NO_ACTION
|
||||
|
||||
const ALTERNATE_TILE_MARGIN := 18
|
||||
|
||||
func _enter_tree() -> void:
|
||||
_canvas_item_background = RenderingServer.canvas_item_create()
|
||||
RenderingServer.canvas_item_set_parent(_canvas_item_background, get_canvas_item())
|
||||
RenderingServer.canvas_item_set_draw_behind_parent(_canvas_item_background, true)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
RenderingServer.free_rid(_canvas_item_background)
|
||||
for p in _canvas_item_map:
|
||||
RenderingServer.free_rid(_canvas_item_map[p])
|
||||
_canvas_item_map.clear()
|
||||
|
||||
|
||||
func refresh_tileset(ts: TileSet) -> void:
|
||||
tileset = ts
|
||||
|
||||
tiles_size = Vector2.ZERO
|
||||
alternate_size = Vector2.ZERO
|
||||
alternate_lookup = []
|
||||
disabled_sources = []
|
||||
|
||||
if !tileset:
|
||||
return
|
||||
|
||||
for s in tileset.get_source_count():
|
||||
var source_id := tileset.get_source_id(s)
|
||||
var source := tileset.get_source(source_id) as TileSetAtlasSource
|
||||
if !source or !source.texture:
|
||||
continue
|
||||
|
||||
tiles_size.x = max(tiles_size.x, source.texture.get_width())
|
||||
tiles_size.y += source.texture.get_height()
|
||||
|
||||
tile_size = source.texture_region_size
|
||||
tile_part_size = Vector2(tile_size) / 3.0
|
||||
|
||||
for t in source.get_tiles_count():
|
||||
var coord := source.get_tile_id(t)
|
||||
var alt_count := source.get_alternative_tiles_count(coord)
|
||||
if alt_count <= 1:
|
||||
continue
|
||||
|
||||
var rect := source.get_tile_texture_region(coord, 0)
|
||||
alternate_lookup.append([rect.size, source_id, coord])
|
||||
alternate_size.x = max(alternate_size.x, rect.size.x * (alt_count - 1))
|
||||
alternate_size.y += rect.size.y
|
||||
|
||||
_on_zoom_value_changed(zoom_level)
|
||||
|
||||
|
||||
func is_tile_in_source(source: TileSetAtlasSource, coord: Vector2i) -> bool:
|
||||
var origin := source.get_tile_at_coords(coord)
|
||||
if origin == Vector2i(-1, -1):
|
||||
return false
|
||||
|
||||
# Animation frames are not needed
|
||||
var size := source.get_tile_size_in_atlas(origin)
|
||||
return coord.x < origin.x + size.x and coord.y < origin.y + size.y
|
||||
|
||||
|
||||
func _build_tile_part_from_position(result: Dictionary, position: Vector2i, rect: Rect2) -> void:
|
||||
result.rect = rect
|
||||
var type := BetterTerrain.get_tile_terrain_type(result.data)
|
||||
if type == BetterTerrain.TileCategory.NON_TERRAIN:
|
||||
return
|
||||
result.terrain_type = type
|
||||
|
||||
var normalize_position := (Vector2(position) - rect.position) / rect.size
|
||||
|
||||
var terrain := BetterTerrain.get_terrain(tileset, type)
|
||||
if !terrain.valid:
|
||||
return
|
||||
for p in BetterTerrain.data.get_terrain_peering_cells(tileset, terrain.type):
|
||||
var side_polygon = BetterTerrain.data.peering_polygon(tileset, terrain.type, p)
|
||||
if Geometry2D.is_point_in_polygon(normalize_position, side_polygon):
|
||||
result.peering = p
|
||||
result.polygon = side_polygon
|
||||
break
|
||||
|
||||
|
||||
func tile_part_from_position(position: Vector2i) -> Dictionary:
|
||||
if !tileset:
|
||||
return { valid = false }
|
||||
|
||||
var offset := Vector2.ZERO
|
||||
var alt_offset := Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
|
||||
if Rect2(alt_offset, zoom_level * alternate_size).has_point(position):
|
||||
for a in alternate_lookup:
|
||||
if a[1] in disabled_sources:
|
||||
continue
|
||||
var next_offset_y = alt_offset.y + zoom_level * a[0].y
|
||||
if position.y > next_offset_y:
|
||||
alt_offset.y = next_offset_y
|
||||
continue
|
||||
|
||||
var source := tileset.get_source(a[1]) as TileSetAtlasSource
|
||||
if !source:
|
||||
break
|
||||
|
||||
var count := source.get_alternative_tiles_count(a[2])
|
||||
var index := int((position.x - alt_offset.x) / (zoom_level * a[0].x)) + 1
|
||||
|
||||
if index < count:
|
||||
var alt_id := source.get_alternative_tile_id(a[2], index)
|
||||
var target_rect := Rect2(
|
||||
alt_offset + Vector2.RIGHT * (index - 1) * zoom_level * a[0].x,
|
||||
zoom_level * a[0]
|
||||
)
|
||||
|
||||
var result := {
|
||||
valid = true,
|
||||
source_id = a[1],
|
||||
coord = a[2],
|
||||
alternate = alt_id,
|
||||
data = source.get_tile_data(a[2], alt_id)
|
||||
}
|
||||
_build_tile_part_from_position(result, position, target_rect)
|
||||
return result
|
||||
|
||||
else:
|
||||
for s in tileset.get_source_count():
|
||||
var source_id := tileset.get_source_id(s)
|
||||
if source_id in disabled_sources:
|
||||
continue
|
||||
var source := tileset.get_source(source_id) as TileSetAtlasSource
|
||||
if !source || !source.texture:
|
||||
continue
|
||||
for t in source.get_tiles_count():
|
||||
var coord := source.get_tile_id(t)
|
||||
var rect := source.get_tile_texture_region(coord, 0)
|
||||
var target_rect := Rect2(offset + zoom_level * rect.position, zoom_level * rect.size)
|
||||
if !target_rect.has_point(position):
|
||||
continue
|
||||
|
||||
var result := {
|
||||
valid = true,
|
||||
source_id = source_id,
|
||||
coord = coord,
|
||||
alternate = 0,
|
||||
data = source.get_tile_data(coord, 0)
|
||||
}
|
||||
_build_tile_part_from_position(result, position, target_rect)
|
||||
return result
|
||||
|
||||
offset.y += zoom_level * source.texture.get_height()
|
||||
|
||||
return { valid = false }
|
||||
|
||||
|
||||
func tile_rect_from_position(position: Vector2i) -> Rect2:
|
||||
if !tileset:
|
||||
return Rect2(-1,-1,0,0)
|
||||
|
||||
var offset := Vector2.ZERO
|
||||
var alt_offset := Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
|
||||
if Rect2(alt_offset, zoom_level * alternate_size).has_point(position):
|
||||
for a in alternate_lookup:
|
||||
if a[1] in disabled_sources:
|
||||
continue
|
||||
var next_offset_y = alt_offset.y + zoom_level * a[0].y
|
||||
if position.y > next_offset_y:
|
||||
alt_offset.y = next_offset_y
|
||||
continue
|
||||
|
||||
var source := tileset.get_source(a[1]) as TileSetAtlasSource
|
||||
if !source:
|
||||
break
|
||||
|
||||
var count := source.get_alternative_tiles_count(a[2])
|
||||
var index := int((position.x - alt_offset.x) / (zoom_level * a[0].x)) + 1
|
||||
|
||||
if index < count:
|
||||
var target_rect := Rect2(
|
||||
alt_offset + Vector2.RIGHT * (index - 1) * zoom_level * a[0].x,
|
||||
zoom_level * a[0]
|
||||
)
|
||||
return target_rect
|
||||
|
||||
else:
|
||||
for s in tileset.get_source_count():
|
||||
var source_id := tileset.get_source_id(s)
|
||||
if source_id in disabled_sources:
|
||||
continue
|
||||
var source := tileset.get_source(source_id) as TileSetAtlasSource
|
||||
if !source:
|
||||
continue
|
||||
for t in source.get_tiles_count():
|
||||
var coord := source.get_tile_id(t)
|
||||
var rect := source.get_tile_texture_region(coord, 0)
|
||||
var target_rect := Rect2(offset + zoom_level * rect.position, zoom_level * rect.size)
|
||||
if target_rect.has_point(position):
|
||||
return target_rect
|
||||
|
||||
offset.y += zoom_level * source.texture.get_height()
|
||||
|
||||
return Rect2(-1,-1,0,0)
|
||||
|
||||
|
||||
func tile_parts_from_rect(rect:Rect2) -> Array[Dictionary]:
|
||||
if !tileset:
|
||||
return []
|
||||
|
||||
var tiles:Array[Dictionary] = []
|
||||
|
||||
var offset := Vector2.ZERO
|
||||
var alt_offset := Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
|
||||
for s in tileset.get_source_count():
|
||||
var source_id := tileset.get_source_id(s)
|
||||
if source_id in disabled_sources:
|
||||
continue
|
||||
var source := tileset.get_source(source_id) as TileSetAtlasSource
|
||||
if !source:
|
||||
continue
|
||||
for t in source.get_tiles_count():
|
||||
var coord := source.get_tile_id(t)
|
||||
var tile_rect := source.get_tile_texture_region(coord, 0)
|
||||
var target_rect := Rect2(offset + zoom_level * tile_rect.position, zoom_level * tile_rect.size)
|
||||
if target_rect.intersects(rect):
|
||||
var result := {
|
||||
valid = true,
|
||||
source_id = source_id,
|
||||
coord = coord,
|
||||
alternate = 0,
|
||||
data = source.get_tile_data(coord, 0)
|
||||
}
|
||||
var pos = target_rect.position + target_rect.size/2
|
||||
_build_tile_part_from_position(result, pos, target_rect)
|
||||
tiles.push_back(result)
|
||||
var alt_count := source.get_alternative_tiles_count(coord)
|
||||
for a in alt_count:
|
||||
var alt_id := 0
|
||||
if a == 0:
|
||||
continue
|
||||
|
||||
target_rect = Rect2(alt_offset + zoom_level * (a - 1) * tile_rect.size.x * Vector2.RIGHT, zoom_level * tile_rect.size)
|
||||
alt_id = source.get_alternative_tile_id(coord, a)
|
||||
if target_rect.intersects(rect):
|
||||
var td := source.get_tile_data(coord, alt_id)
|
||||
var result := {
|
||||
valid = true,
|
||||
source_id = source_id,
|
||||
coord = coord,
|
||||
alternate = alt_id,
|
||||
data = td
|
||||
}
|
||||
var pos = target_rect.position + target_rect.size/2
|
||||
_build_tile_part_from_position(result, pos, target_rect)
|
||||
tiles.push_back(result)
|
||||
if alt_count > 1:
|
||||
alt_offset.y += zoom_level * tile_rect.size.y
|
||||
|
||||
offset.y += zoom_level * source.texture.get_height()
|
||||
|
||||
return tiles
|
||||
|
||||
|
||||
func _get_canvas_item(td: TileData) -> RID:
|
||||
if !td.material:
|
||||
return self.get_canvas_item()
|
||||
if _canvas_item_map.has(td.material):
|
||||
return _canvas_item_map[td.material]
|
||||
|
||||
var rid = RenderingServer.canvas_item_create()
|
||||
RenderingServer.canvas_item_set_material(rid, td.material.get_rid())
|
||||
RenderingServer.canvas_item_set_parent(rid, get_canvas_item())
|
||||
RenderingServer.canvas_item_set_draw_behind_parent(rid, true)
|
||||
RenderingServer.canvas_item_set_default_texture_filter(rid, RenderingServer.CANVAS_ITEM_TEXTURE_FILTER_NEAREST)
|
||||
_canvas_item_map[td.material] = rid
|
||||
return rid
|
||||
|
||||
|
||||
func _draw_tile_data(texture: Texture2D, rect: Rect2, src_rect: Rect2, td: TileData, draw_sides: bool = true) -> void:
|
||||
var flipped_rect := rect
|
||||
if td.flip_h:
|
||||
flipped_rect.size.x = -rect.size.x
|
||||
if td.flip_v:
|
||||
flipped_rect.size.y = -rect.size.y
|
||||
|
||||
RenderingServer.canvas_item_add_texture_rect_region(
|
||||
_get_canvas_item(td),
|
||||
flipped_rect,
|
||||
texture.get_rid(),
|
||||
src_rect,
|
||||
td.modulate,
|
||||
td.transpose
|
||||
)
|
||||
|
||||
var type := BetterTerrain.get_tile_terrain_type(td)
|
||||
if type == BetterTerrain.TileCategory.NON_TERRAIN:
|
||||
draw_rect(rect, Color(0.1, 0.1, 0.1, 0.5), true)
|
||||
return
|
||||
|
||||
var terrain := BetterTerrain.get_terrain(tileset, type)
|
||||
if !terrain.valid:
|
||||
return
|
||||
|
||||
var transform := Transform2D(0.0, rect.size, 0.0, rect.position)
|
||||
var center_polygon = transform * BetterTerrain.data.peering_polygon(tileset, terrain.type, -1)
|
||||
draw_colored_polygon(center_polygon, Color(terrain.color, 0.6))
|
||||
if terrain.type == BetterTerrain.TerrainType.DECORATION:
|
||||
center_polygon.append(center_polygon[0])
|
||||
draw_polyline(center_polygon, Color.BLACK)
|
||||
|
||||
if paint < BetterTerrain.TileCategory.EMPTY or paint >= BetterTerrain.terrain_count(tileset):
|
||||
return
|
||||
|
||||
if not draw_sides:
|
||||
return
|
||||
|
||||
var paint_terrain := BetterTerrain.get_terrain(tileset, paint)
|
||||
for p in BetterTerrain.data.get_terrain_peering_cells(tileset, terrain.type):
|
||||
if paint in BetterTerrain.tile_peering_types(td, p):
|
||||
var side_polygon = transform * BetterTerrain.data.peering_polygon(tileset, terrain.type, p)
|
||||
draw_colored_polygon(side_polygon, Color(paint_terrain.color, 0.6))
|
||||
if paint_terrain.type == BetterTerrain.TerrainType.DECORATION:
|
||||
side_polygon.append(side_polygon[0])
|
||||
draw_polyline(side_polygon, Color.BLACK)
|
||||
|
||||
|
||||
func _draw_tile_symmetry(texture: Texture2D, rect: Rect2, src_rect: Rect2, td: TileData, draw_icon: bool = true) -> void:
|
||||
var flipped_rect := rect
|
||||
if td.flip_h:
|
||||
flipped_rect.size.x = -rect.size.x
|
||||
if td.flip_v:
|
||||
flipped_rect.size.y = -rect.size.y
|
||||
|
||||
RenderingServer.canvas_item_add_texture_rect_region(
|
||||
_get_canvas_item(td),
|
||||
flipped_rect,
|
||||
texture.get_rid(),
|
||||
src_rect,
|
||||
td.modulate,
|
||||
td.transpose
|
||||
)
|
||||
|
||||
if not draw_icon:
|
||||
return
|
||||
|
||||
var symmetry_type = BetterTerrain.get_tile_symmetry_type(td)
|
||||
if symmetry_type == 0:
|
||||
return
|
||||
var symmetry_icon = paint_symmetry_icons[symmetry_type]
|
||||
|
||||
RenderingServer.canvas_item_add_texture_rect_region(
|
||||
_get_canvas_item(td),
|
||||
rect,
|
||||
symmetry_icon.get_rid(),
|
||||
Rect2(Vector2.ZERO, symmetry_icon.get_size()),
|
||||
Color(1,1,1,0.5)
|
||||
)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if !tileset:
|
||||
return
|
||||
|
||||
# Clear material-based render targets
|
||||
RenderingServer.canvas_item_clear(_canvas_item_background)
|
||||
for p in _canvas_item_map:
|
||||
RenderingServer.canvas_item_clear(_canvas_item_map[p])
|
||||
|
||||
var offset := Vector2.ZERO
|
||||
var alt_offset := Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
|
||||
|
||||
RenderingServer.canvas_item_add_texture_rect(
|
||||
_canvas_item_background,
|
||||
Rect2(alt_offset, zoom_level * alternate_size),
|
||||
checkerboard.get_rid(),
|
||||
true
|
||||
)
|
||||
|
||||
for s in tileset.get_source_count():
|
||||
var source_id := tileset.get_source_id(s)
|
||||
if source_id in disabled_sources:
|
||||
continue
|
||||
var source := tileset.get_source(source_id) as TileSetAtlasSource
|
||||
if !source or !source.texture:
|
||||
continue
|
||||
|
||||
RenderingServer.canvas_item_add_texture_rect(
|
||||
_canvas_item_background,
|
||||
Rect2(offset, zoom_level * source.texture.get_size()),
|
||||
checkerboard.get_rid(),
|
||||
true
|
||||
)
|
||||
for t in source.get_tiles_count():
|
||||
var coord := source.get_tile_id(t)
|
||||
var rect := source.get_tile_texture_region(coord, 0)
|
||||
var alt_count := source.get_alternative_tiles_count(coord)
|
||||
var target_rect : Rect2
|
||||
for a in alt_count:
|
||||
var alt_id := 0
|
||||
if a == 0:
|
||||
target_rect = Rect2(offset + zoom_level * rect.position, zoom_level * rect.size)
|
||||
else:
|
||||
target_rect = Rect2(alt_offset + zoom_level * (a - 1) * rect.size.x * Vector2.RIGHT, zoom_level * rect.size)
|
||||
alt_id = source.get_alternative_tile_id(coord, a)
|
||||
|
||||
var td := source.get_tile_data(coord, alt_id)
|
||||
var drawing_current = BetterTerrain.get_tile_terrain_type(td) == paint
|
||||
if paint_mode == PaintMode.PAINT_SYMMETRY:
|
||||
_draw_tile_symmetry(source.texture, target_rect, rect, td, drawing_current)
|
||||
else:
|
||||
_draw_tile_data(source.texture, target_rect, rect, td)
|
||||
|
||||
if drawing_current:
|
||||
draw_rect(target_rect.grow(-1), Color(0,0,0, 0.75), false, 1)
|
||||
draw_rect(target_rect, Color(1,1,1, 0.75), false, 1)
|
||||
|
||||
if paint_mode == PaintMode.SELECT:
|
||||
if selected_tile_states.any(func(v):
|
||||
return v.part.data == td
|
||||
):
|
||||
draw_rect(target_rect.grow(-1), Color.DEEP_SKY_BLUE, false, 2)
|
||||
|
||||
if alt_count > 1:
|
||||
alt_offset.y += zoom_level * rect.size.y
|
||||
|
||||
# Blank out unused or uninteresting tiles
|
||||
var size := source.get_atlas_grid_size()
|
||||
for y in size.y:
|
||||
for x in size.x:
|
||||
var pos := Vector2i(x, y)
|
||||
if !is_tile_in_source(source, pos):
|
||||
var atlas_pos := source.margins + pos * (source.separation + source.texture_region_size)
|
||||
draw_rect(Rect2(offset + zoom_level * atlas_pos, zoom_level * source.texture_region_size), Color(0.0, 0.0, 0.0, 0.8), true)
|
||||
|
||||
offset.y += zoom_level * source.texture.get_height()
|
||||
|
||||
# Blank out unused alternate tile sections
|
||||
alt_offset = Vector2.RIGHT * (zoom_level * tiles_size.x + ALTERNATE_TILE_MARGIN)
|
||||
for a in alternate_lookup:
|
||||
if a[1] in disabled_sources:
|
||||
continue
|
||||
var source := tileset.get_source(a[1]) as TileSetAtlasSource
|
||||
if source:
|
||||
var count := source.get_alternative_tiles_count(a[2]) - 1
|
||||
var occupied_width = count * zoom_level * a[0].x
|
||||
var area := Rect2(
|
||||
alt_offset.x + occupied_width,
|
||||
alt_offset.y,
|
||||
zoom_level * alternate_size.x - occupied_width,
|
||||
zoom_level * a[0].y
|
||||
)
|
||||
draw_rect(area, Color(0.0, 0.0, 0.0, 0.8), true)
|
||||
alt_offset.y += zoom_level * a[0].y
|
||||
|
||||
if highlighted_tile_part.valid:
|
||||
if paint_mode == PaintMode.PAINT_PEERING and highlighted_tile_part.has("polygon"):
|
||||
var transform := Transform2D(0.0, highlighted_tile_part.rect.size - 2 * Vector2.ONE, 0.0, highlighted_tile_part.rect.position + Vector2.ONE)
|
||||
draw_colored_polygon(transform * highlighted_tile_part.polygon, Color(Color.WHITE, 0.2))
|
||||
if paint_mode != PaintMode.NO_PAINT:
|
||||
var inner_rect := Rect2(highlighted_tile_part.rect.position + Vector2.ONE, highlighted_tile_part.rect.size - 2 * Vector2.ONE)
|
||||
draw_rect(inner_rect, Color.WHITE, false)
|
||||
if paint_mode == PaintMode.PAINT_SYMMETRY:
|
||||
if paint_symmetry > 0:
|
||||
var symmetry_icon = paint_symmetry_icons[paint_symmetry]
|
||||
draw_texture_rect(symmetry_icon, highlighted_tile_part.rect, false, Color(0.5,0.75,1,0.5))
|
||||
|
||||
if paint_mode == PaintMode.SELECT:
|
||||
draw_rect(selection_rect, Color.WHITE, false)
|
||||
|
||||
if paint_mode == PaintMode.PASTE:
|
||||
if staged_paste_tile_states.size() > 0:
|
||||
var base_rect = staged_paste_tile_states[0].base_rect
|
||||
var paint_terrain := BetterTerrain.get_terrain(tileset, paint)
|
||||
var paint_terrain_type = paint_terrain.type
|
||||
if paint_terrain_type == BetterTerrain.TerrainType.CATEGORY:
|
||||
paint_terrain_type = 0
|
||||
for state in staged_paste_tile_states:
|
||||
var staged_rect:Rect2 = state.base_rect
|
||||
staged_rect.position -= base_rect.position + base_rect.size / 2
|
||||
|
||||
staged_rect.position *= zoom_level
|
||||
staged_rect.size *= zoom_level
|
||||
|
||||
staged_rect.position += Vector2(current_position)
|
||||
|
||||
var real_rect = tile_rect_from_position(staged_rect.get_center())
|
||||
if real_rect.position.x >= 0:
|
||||
draw_rect(real_rect, Color(0,0,0, 0.3), true)
|
||||
var transform := Transform2D(0.0, real_rect.size, 0.0, real_rect.position)
|
||||
var tile_sides = BetterTerrain.data.get_terrain_peering_cells(tileset, paint_terrain_type)
|
||||
for p in tile_sides:
|
||||
if state.paint in BetterTerrain.tile_peering_types(state.part.data, p):
|
||||
var side_polygon = BetterTerrain.data.peering_polygon(tileset, paint_terrain_type, p)
|
||||
var color = Color(paint_terrain.color, 0.6)
|
||||
draw_colored_polygon(transform * side_polygon, color)
|
||||
|
||||
draw_rect(staged_rect, Color.DEEP_PINK, false)
|
||||
|
||||
|
||||
|
||||
func delete_selection():
|
||||
undo_manager.create_action("Delete tile terrain peering types", UndoRedo.MERGE_DISABLE, tileset)
|
||||
for t in selected_tile_states:
|
||||
for side in range(16):
|
||||
var old_peering = BetterTerrain.tile_peering_types(t.part.data, side)
|
||||
if old_peering.has(paint):
|
||||
undo_manager.add_do_method(BetterTerrain, &"remove_tile_peering_type", tileset, t.part.data, side, paint)
|
||||
undo_manager.add_undo_method(BetterTerrain, &"add_tile_peering_type", tileset, t.part.data, side, paint)
|
||||
|
||||
undo_manager.add_do_method(self, &"queue_redraw")
|
||||
undo_manager.add_undo_method(self, &"queue_redraw")
|
||||
undo_manager.commit_action()
|
||||
|
||||
|
||||
func toggle_selection():
|
||||
undo_manager.create_action("Toggle tile terrain", UndoRedo.MERGE_DISABLE, tileset, true)
|
||||
for t in selected_tile_states:
|
||||
var type := BetterTerrain.get_tile_terrain_type(t.part.data)
|
||||
var goal := paint if paint != type else BetterTerrain.TileCategory.NON_TERRAIN
|
||||
|
||||
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_tile_terrain_type", [tileset, t.part.data, goal])
|
||||
if goal == BetterTerrain.TileCategory.NON_TERRAIN:
|
||||
terrain_undo.create_peering_restore_point_tile(
|
||||
undo_manager,
|
||||
tileset,
|
||||
t.part.source_id,
|
||||
t.part.coord,
|
||||
t.part.alternate
|
||||
)
|
||||
else:
|
||||
undo_manager.add_undo_method(BetterTerrain, &"set_tile_terrain_type", tileset, t.part.data, type)
|
||||
|
||||
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
|
||||
undo_manager.add_undo_method(self, &"queue_redraw")
|
||||
undo_manager.commit_action()
|
||||
terrain_undo.action_count += 1
|
||||
|
||||
|
||||
func copy_selection():
|
||||
copied_tile_states = selected_tile_states
|
||||
|
||||
|
||||
func paste_selection():
|
||||
staged_paste_tile_states = copied_tile_states
|
||||
selected_tile_states = []
|
||||
paint_mode = PaintMode.PASTE
|
||||
paint_action = PaintAction.PASTE
|
||||
paste_occurred.emit()
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func set_disabled_sources(list):
|
||||
disabled_sources = list
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func emit_terrain_updated(index):
|
||||
terrain_updated.emit(index)
|
||||
|
||||
|
||||
func _gui_input(event) -> void:
|
||||
if event is InputEventKey and event.is_pressed():
|
||||
if event.keycode == KEY_DELETE and not event.echo:
|
||||
accept_event()
|
||||
delete_selection()
|
||||
if event.keycode == KEY_ENTER and not event.echo:
|
||||
accept_event()
|
||||
toggle_selection()
|
||||
if event.keycode == KEY_ESCAPE and not event.echo:
|
||||
accept_event()
|
||||
if paint_action == PaintAction.PASTE:
|
||||
staged_paste_tile_states = []
|
||||
paint_mode = PaintMode.SELECT
|
||||
paint_action = PaintAction.NO_ACTION
|
||||
selection_start = Vector2i(-1,-1)
|
||||
if event.keycode == KEY_C and (event.ctrl_pressed or event.meta_pressed) and not event.echo:
|
||||
accept_event()
|
||||
copy_selection()
|
||||
if event.keycode == KEY_X and (event.ctrl_pressed or event.meta_pressed) and not event.echo:
|
||||
accept_event()
|
||||
copy_selection()
|
||||
delete_selection()
|
||||
if event.keycode == KEY_V and (event.ctrl_pressed or event.meta_pressed) and not event.echo:
|
||||
accept_event()
|
||||
paste_selection()
|
||||
if event is InputEventMouseButton:
|
||||
if event.button_index == MOUSE_BUTTON_WHEEL_UP and (event.ctrl_pressed or event.meta_pressed):
|
||||
accept_event()
|
||||
change_zoom_level.emit(zoom_level * 1.1)
|
||||
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN and (event.ctrl_pressed or event.meta_pressed):
|
||||
accept_event()
|
||||
change_zoom_level.emit(zoom_level / 1.1)
|
||||
|
||||
var released : bool = event is InputEventMouseButton and (not event.pressed and (event.button_index == MOUSE_BUTTON_LEFT or event.button_index == MOUSE_BUTTON_RIGHT))
|
||||
if released:
|
||||
paint_action = PaintAction.NO_ACTION
|
||||
|
||||
if event is InputEventMouseMotion:
|
||||
prev_position = current_position
|
||||
current_position = event.position
|
||||
var tile := tile_part_from_position(event.position)
|
||||
if tile.valid != highlighted_tile_part.valid or\
|
||||
(tile.valid and tile.data != highlighted_tile_part.data) or\
|
||||
(tile.valid and tile.get("peering") != highlighted_tile_part.get("peering")) or\
|
||||
event.button_mask & MOUSE_BUTTON_LEFT and paint_action == PaintAction.SELECT:
|
||||
queue_redraw()
|
||||
highlighted_tile_part = tile
|
||||
|
||||
var clicked : bool = event is InputEventMouseButton and (event.pressed and (event.button_index == MOUSE_BUTTON_LEFT or event.button_index == MOUSE_BUTTON_RIGHT))
|
||||
if clicked:
|
||||
initial_click = current_position
|
||||
selection_start = Vector2i(-1,-1)
|
||||
terrain_undo.action_index += 1
|
||||
terrain_undo.action_count = 0
|
||||
if released:
|
||||
terrain_undo.finish_action()
|
||||
selection_rect = Rect2i(0,0,0,0)
|
||||
queue_redraw()
|
||||
|
||||
if paint_action == PaintAction.PASTE:
|
||||
if event is InputEventMouseMotion:
|
||||
queue_redraw()
|
||||
|
||||
if clicked:
|
||||
if event.button_index == MOUSE_BUTTON_LEFT and staged_paste_tile_states.size() > 0:
|
||||
undo_manager.create_action("Paste tile terrain peering types", UndoRedo.MERGE_DISABLE, tileset)
|
||||
var base_rect = staged_paste_tile_states[0].base_rect
|
||||
for p in staged_paste_tile_states:
|
||||
var staged_rect:Rect2 = p.base_rect
|
||||
staged_rect.position -= base_rect.position + base_rect.size / 2
|
||||
|
||||
staged_rect.position *= zoom_level
|
||||
staged_rect.size *= zoom_level
|
||||
|
||||
staged_rect.position += Vector2(current_position)
|
||||
|
||||
var old_tile_part = tile_part_from_position(staged_rect.get_center())
|
||||
var new_tile_state = p
|
||||
if (not old_tile_part.valid) or (not new_tile_state.part.valid):
|
||||
continue
|
||||
|
||||
for side in range(16):
|
||||
var old_peering = BetterTerrain.tile_peering_types(old_tile_part.data, side)
|
||||
var new_sides = new_tile_state.sides
|
||||
if new_sides.has(side) and not old_peering.has(paint):
|
||||
undo_manager.add_do_method(BetterTerrain, &"add_tile_peering_type", tileset, old_tile_part.data, side, paint)
|
||||
undo_manager.add_undo_method(BetterTerrain, &"remove_tile_peering_type", tileset, old_tile_part.data, side, paint)
|
||||
elif old_peering.has(paint) and not new_sides.has(side):
|
||||
undo_manager.add_do_method(BetterTerrain, &"remove_tile_peering_type", tileset, old_tile_part.data, side, paint)
|
||||
undo_manager.add_undo_method(BetterTerrain, &"add_tile_peering_type", tileset, old_tile_part.data, side, paint)
|
||||
|
||||
var old_symmetry = BetterTerrain.get_tile_symmetry_type(old_tile_part.data)
|
||||
var new_symmetry = new_tile_state.symmetry
|
||||
if new_symmetry != old_symmetry:
|
||||
undo_manager.add_do_method(BetterTerrain, &"set_tile_symmetry_type", tileset, old_tile_part.data, new_symmetry)
|
||||
undo_manager.add_undo_method(BetterTerrain, &"set_tile_symmetry_type", tileset, old_tile_part.data, old_symmetry)
|
||||
|
||||
undo_manager.add_do_method(self, &"queue_redraw")
|
||||
undo_manager.add_undo_method(self, &"queue_redraw")
|
||||
undo_manager.commit_action()
|
||||
|
||||
staged_paste_tile_states = []
|
||||
paint_mode = PaintMode.SELECT
|
||||
paint_action = PaintAction.SELECT
|
||||
return
|
||||
|
||||
if clicked and pick_icon_terrain >= 0:
|
||||
highlighted_tile_part = tile_part_from_position(current_position)
|
||||
if !highlighted_tile_part.valid:
|
||||
return
|
||||
|
||||
var t = BetterTerrain.get_terrain(tileset, paint)
|
||||
var prev_icon = t.icon.duplicate()
|
||||
var icon = {
|
||||
source_id = highlighted_tile_part.source_id,
|
||||
coord = highlighted_tile_part.coord
|
||||
}
|
||||
undo_manager.create_action("Edit terrain details", UndoRedo.MERGE_DISABLE, tileset)
|
||||
undo_manager.add_do_method(BetterTerrain, &"set_terrain", tileset, paint, t.name, t.color, t.type, t.categories, icon)
|
||||
undo_manager.add_do_method(self, &"emit_terrain_updated", paint)
|
||||
undo_manager.add_undo_method(BetterTerrain, &"set_terrain", tileset, paint, t.name, t.color, t.type, t.categories, prev_icon)
|
||||
undo_manager.add_undo_method(self, &"emit_terrain_updated", paint)
|
||||
undo_manager.commit_action()
|
||||
pick_icon_terrain = -1
|
||||
return
|
||||
|
||||
if pick_icon_terrain_cancel:
|
||||
pick_icon_terrain = -1
|
||||
pick_icon_terrain_cancel = false
|
||||
|
||||
if paint != BetterTerrain.TileCategory.NON_TERRAIN and clicked:
|
||||
paint_action = PaintAction.NO_ACTION
|
||||
if highlighted_tile_part.valid:
|
||||
match [paint_mode, event.button_index]:
|
||||
[PaintMode.PAINT_TYPE, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.DRAW_TYPE
|
||||
[PaintMode.PAINT_TYPE, MOUSE_BUTTON_RIGHT]: paint_action = PaintAction.ERASE_TYPE
|
||||
[PaintMode.PAINT_PEERING, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.DRAW_PEERING
|
||||
[PaintMode.PAINT_PEERING, MOUSE_BUTTON_RIGHT]: paint_action = PaintAction.ERASE_PEERING
|
||||
[PaintMode.PAINT_SYMMETRY, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.DRAW_SYMMETRY
|
||||
[PaintMode.PAINT_SYMMETRY, MOUSE_BUTTON_RIGHT]: paint_action = PaintAction.ERASE_SYMMETRY
|
||||
[PaintMode.SELECT, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.SELECT
|
||||
else:
|
||||
match [paint_mode, event.button_index]:
|
||||
[PaintMode.SELECT, MOUSE_BUTTON_LEFT]: paint_action = PaintAction.SELECT
|
||||
|
||||
if (clicked or event is InputEventMouseMotion) and paint_action != PaintAction.NO_ACTION:
|
||||
|
||||
if paint_action == PaintAction.SELECT:
|
||||
if clicked:
|
||||
selection_start = Vector2i(-1,-1)
|
||||
queue_redraw()
|
||||
if selection_start.x < 0:
|
||||
selection_start = current_position
|
||||
selection_end = current_position
|
||||
|
||||
selection_rect = Rect2i(selection_start, selection_end - selection_start).abs()
|
||||
var selected_tile_parts = tile_parts_from_rect(selection_rect)
|
||||
selected_tile_states = []
|
||||
for t in selected_tile_parts:
|
||||
var state := {
|
||||
part = t,
|
||||
base_rect = Rect2(t.rect.position / zoom_level, t.rect.size / zoom_level),
|
||||
paint = paint,
|
||||
sides = BetterTerrain.tile_peering_for_type(t.data, paint),
|
||||
symmetry = BetterTerrain.get_tile_symmetry_type(t.data)
|
||||
}
|
||||
selected_tile_states.push_back(state)
|
||||
else:
|
||||
if !highlighted_tile_part.valid:
|
||||
return
|
||||
#slightly crude and non-optimal but way simpler than the "correct" solution
|
||||
var current_position_vec2 = Vector2(current_position)
|
||||
var prev_position_vec2 = Vector2(prev_position)
|
||||
var mouse_dist = current_position_vec2.distance_to(prev_position_vec2)
|
||||
var step_size = (tile_part_size.x * zoom_level)
|
||||
var steps = ceil(mouse_dist / step_size) + 1
|
||||
for i in range(steps):
|
||||
var t = float(i) / steps
|
||||
var check_position = prev_position_vec2.lerp(current_position_vec2, t)
|
||||
highlighted_tile_part = tile_part_from_position(check_position)
|
||||
|
||||
if !highlighted_tile_part.valid:
|
||||
continue
|
||||
|
||||
if paint_action == PaintAction.DRAW_TYPE or paint_action == PaintAction.ERASE_TYPE:
|
||||
var type := BetterTerrain.get_tile_terrain_type(highlighted_tile_part.data)
|
||||
var goal := paint if paint_action == PaintAction.DRAW_TYPE else BetterTerrain.TileCategory.NON_TERRAIN
|
||||
if type != goal:
|
||||
undo_manager.create_action("Set tile terrain type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
|
||||
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_tile_terrain_type", [tileset, highlighted_tile_part.data, goal])
|
||||
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
|
||||
if goal == BetterTerrain.TileCategory.NON_TERRAIN:
|
||||
terrain_undo.create_peering_restore_point_tile(
|
||||
undo_manager,
|
||||
tileset,
|
||||
highlighted_tile_part.source_id,
|
||||
highlighted_tile_part.coord,
|
||||
highlighted_tile_part.alternate
|
||||
)
|
||||
else:
|
||||
undo_manager.add_undo_method(BetterTerrain, &"set_tile_terrain_type", tileset, highlighted_tile_part.data, type)
|
||||
undo_manager.add_undo_method(self, &"queue_redraw")
|
||||
undo_manager.commit_action()
|
||||
terrain_undo.action_count += 1
|
||||
elif paint_action == PaintAction.DRAW_PEERING:
|
||||
if highlighted_tile_part.has("peering"):
|
||||
if !(paint in BetterTerrain.tile_peering_types(highlighted_tile_part.data, highlighted_tile_part.peering)):
|
||||
undo_manager.create_action("Set tile terrain peering type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
|
||||
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"add_tile_peering_type", [tileset, highlighted_tile_part.data, highlighted_tile_part.peering, paint])
|
||||
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
|
||||
undo_manager.add_undo_method(BetterTerrain, &"remove_tile_peering_type", tileset, highlighted_tile_part.data, highlighted_tile_part.peering, paint)
|
||||
undo_manager.add_undo_method(self, &"queue_redraw")
|
||||
undo_manager.commit_action()
|
||||
terrain_undo.action_count += 1
|
||||
elif paint_action == PaintAction.ERASE_PEERING:
|
||||
if highlighted_tile_part.has("peering"):
|
||||
if paint in BetterTerrain.tile_peering_types(highlighted_tile_part.data, highlighted_tile_part.peering):
|
||||
undo_manager.create_action("Remove tile terrain peering type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
|
||||
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"remove_tile_peering_type", [tileset, highlighted_tile_part.data, highlighted_tile_part.peering, paint])
|
||||
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
|
||||
undo_manager.add_undo_method(BetterTerrain, &"add_tile_peering_type", tileset, highlighted_tile_part.data, highlighted_tile_part.peering, paint)
|
||||
undo_manager.add_undo_method(self, &"queue_redraw")
|
||||
undo_manager.commit_action()
|
||||
terrain_undo.action_count += 1
|
||||
elif paint_action == PaintAction.DRAW_SYMMETRY:
|
||||
if paint == BetterTerrain.get_tile_terrain_type(highlighted_tile_part.data):
|
||||
undo_manager.create_action("Set tile symmetry type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
|
||||
var old_symmetry = BetterTerrain.get_tile_symmetry_type(highlighted_tile_part.data)
|
||||
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_tile_symmetry_type", [tileset, highlighted_tile_part.data, paint_symmetry])
|
||||
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
|
||||
undo_manager.add_undo_method(BetterTerrain, &"set_tile_symmetry_type", tileset, highlighted_tile_part.data, old_symmetry)
|
||||
undo_manager.add_undo_method(self, &"queue_redraw")
|
||||
undo_manager.commit_action()
|
||||
terrain_undo.action_count += 1
|
||||
elif paint_action == PaintAction.ERASE_SYMMETRY:
|
||||
if paint == BetterTerrain.get_tile_terrain_type(highlighted_tile_part.data):
|
||||
undo_manager.create_action("Remove tile symmetry type " + str(terrain_undo.action_index), UndoRedo.MERGE_ALL, tileset, true)
|
||||
var old_symmetry = BetterTerrain.get_tile_symmetry_type(highlighted_tile_part.data)
|
||||
terrain_undo.add_do_method(undo_manager, BetterTerrain, &"set_tile_symmetry_type", [tileset, highlighted_tile_part.data, BetterTerrain.SymmetryType.NONE])
|
||||
terrain_undo.add_do_method(undo_manager, self, &"queue_redraw", [])
|
||||
undo_manager.add_undo_method(BetterTerrain, &"set_tile_symmetry_type", tileset, highlighted_tile_part.data, old_symmetry)
|
||||
undo_manager.add_undo_method(self, &"queue_redraw")
|
||||
undo_manager.commit_action()
|
||||
terrain_undo.action_count += 1
|
||||
|
||||
|
||||
func _on_zoom_value_changed(value) -> void:
|
||||
zoom_level = value
|
||||
custom_minimum_size.x = zoom_level * tiles_size.x
|
||||
if alternate_size.x > 0:
|
||||
custom_minimum_size.x += ALTERNATE_TILE_MARGIN + zoom_level * alternate_size.x
|
||||
custom_minimum_size.y = zoom_level * max(tiles_size.y, alternate_size.y)
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func clear_highlighted_tile() -> void:
|
||||
highlighted_tile_part = { valid = false }
|
||||
queue_redraw()
|
1
addons/better-terrain/editor/TileView.gd.uid
Executable file
1
addons/better-terrain/editor/TileView.gd.uid
Executable file
@@ -0,0 +1 @@
|
||||
uid://cpm7dq6r0n0sn
|
Reference in New Issue
Block a user