Adding custom tile functionality written by Cursor
This commit is contained in:
parent
cf2a9aaecf
commit
ef53ed543f
5 changed files with 246 additions and 3 deletions
1
hexmapper/.cursorrules
Normal file
1
hexmapper/.cursorrules
Normal file
|
|
@ -0,0 +1 @@
|
|||
Always use Godot 4.x GDScript syntax. Prefer static typing (e.g., var x: int = 5) and use the @onready annotation for nodes.
|
||||
3
hexmapper/.vscode/settings.json
vendored
Normal file
3
hexmapper/.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"godotTools.editorPath.godot4": "/opt/godot/Godot_v4.5.1-stable_linux.x86_64"
|
||||
}
|
||||
|
|
@ -255,11 +255,13 @@ unique_name_in_owner = true
|
|||
custom_minimum_size = Vector2(125, 120)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
item_count = 4
|
||||
item_count = 6
|
||||
item_0/text = "Neu"
|
||||
item_1/text = "Laden"
|
||||
item_2/text = "Speichern"
|
||||
item_3/text = "Speichern als"
|
||||
item_4/text = "---"
|
||||
item_5/text = "Tile hinzufügen"
|
||||
|
||||
[node name="PaletteButton" type="Button" parent="UI/MenuTop"]
|
||||
unique_name_in_owner = true
|
||||
|
|
|
|||
|
|
@ -90,10 +90,11 @@ func get_tile_texcoord(str_palette:String, tile_name:String) -> Vector2i:
|
|||
return ret_v2
|
||||
|
||||
|
||||
func _on_map_item_selected(index: int) -> void: # 1 laden, 2 speichern, 3 speichern als
|
||||
func _on_map_item_selected(index: int) -> void: # 0 neu, 1 laden, 2 speichern, 3 speichern als, 4 separator, 5 tile hinzufügen
|
||||
%MapItemList.deselect_all()
|
||||
match index:
|
||||
0: print("Neue Karte erstellen")
|
||||
0:
|
||||
print("Neue Karte erstellen")
|
||||
1:
|
||||
print("Karte laden")
|
||||
load_map_from()
|
||||
|
|
@ -103,6 +104,12 @@ func _on_map_item_selected(index: int) -> void: # 1 laden, 2 speichern, 3 speich
|
|||
3:
|
||||
print("Karte speichern als...")
|
||||
save_map_as()
|
||||
4:
|
||||
# Separator - do nothing
|
||||
pass
|
||||
5:
|
||||
print("Custom Tile hinzufügen...")
|
||||
add_custom_tile_from_file()
|
||||
|
||||
map_menu_container.hide()
|
||||
|
||||
|
|
@ -194,3 +201,75 @@ func scroll_left():
|
|||
func scroll_right():
|
||||
var tile_width = ground_map.tile_set.tile_size.x
|
||||
camera_2d.position.x += tile_width
|
||||
|
||||
## Opens a file dialog for the user to select and add a custom tile at runtime
|
||||
func add_custom_tile_from_file():
|
||||
%FileDialog.mode_overrides_title = false
|
||||
%FileDialog.title = "Custom Tile hinzufügen"
|
||||
%FileDialog.access = FileDialog.ACCESS_FILESYSTEM
|
||||
%FileDialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
|
||||
%FileDialog.set_filters(PackedStringArray(["*.png", "*.jpg", "*.jpeg"]))
|
||||
|
||||
# Set initial directory to user's home or documents
|
||||
var initial_dir: String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
|
||||
if DirAccess.dir_exists_absolute(initial_dir):
|
||||
%FileDialog.set_current_dir(initial_dir)
|
||||
|
||||
%FileDialog.popup_centered()
|
||||
%FileDialog.file_selected.connect(_on_custom_tile_file_selected, CONNECT_ONE_SHOT)
|
||||
|
||||
## Called when user selects a tile image file
|
||||
func _on_custom_tile_file_selected(file_path: String):
|
||||
var source_id: int = TileSetHelper.add_custom_single_tile(
|
||||
ground_map.tile_set,
|
||||
file_path,
|
||||
Vector2i(72, 72) # Default tile size - could be made configurable
|
||||
)
|
||||
|
||||
if source_id >= 0:
|
||||
print("Custom tile added successfully! Source ID: %d" % source_id)
|
||||
# Optionally add to palette or notify user
|
||||
# You could emit a signal here to update the UI
|
||||
else:
|
||||
print("Failed to add custom tile from: %s" % file_path)
|
||||
|
||||
## Opens a directory dialog for the user to add multiple tiles at once
|
||||
func add_custom_tiles_from_directory():
|
||||
%FileDialog.mode_overrides_title = false
|
||||
%FileDialog.title = "Custom Tiles aus Ordner hinzufügen"
|
||||
%FileDialog.access = FileDialog.ACCESS_FILESYSTEM
|
||||
%FileDialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
|
||||
|
||||
var initial_dir: String = OS.get_system_dir(OS.SYSTEM_DIR_DOCUMENTS)
|
||||
if DirAccess.dir_exists_absolute(initial_dir):
|
||||
%FileDialog.set_current_dir(initial_dir)
|
||||
|
||||
%FileDialog.popup_centered()
|
||||
%FileDialog.dir_selected.connect(_on_custom_tiles_directory_selected, CONNECT_ONE_SHOT)
|
||||
|
||||
## Called when user selects a directory containing tile images
|
||||
func _on_custom_tiles_directory_selected(dir_path: String):
|
||||
var custom_tiles: Dictionary = TileSetHelper.add_custom_tiles_from_directory(
|
||||
ground_map.tile_set,
|
||||
dir_path,
|
||||
Vector2i(72, 72)
|
||||
)
|
||||
|
||||
if custom_tiles.size() > 0:
|
||||
print("Added %d custom tiles from directory: %s" % [custom_tiles.size(), dir_path])
|
||||
for file_path in custom_tiles.keys():
|
||||
print(" - %s (source_id: %d)" % [file_path, custom_tiles[file_path]])
|
||||
else:
|
||||
print("No valid tile images found in directory: %s" % dir_path)
|
||||
|
||||
## Lists all tile sources (useful for debugging)
|
||||
func list_all_tile_sources():
|
||||
var all_sources: Array = TileSetHelper.list_all_sources(ground_map.tile_set)
|
||||
print("=== All Tile Sources ===")
|
||||
for source_info in all_sources:
|
||||
print("Source ID: %d, Texture: %s, Tiles: %d" % [
|
||||
source_info.get("source_id", -1),
|
||||
source_info.get("texture_path", ""),
|
||||
source_info.get("tile_count", 0)
|
||||
])
|
||||
print("========================")
|
||||
|
|
|
|||
158
hexmapper/scenes/MainMap/map/tile_set_helper.gd
Normal file
158
hexmapper/scenes/MainMap/map/tile_set_helper.gd
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
extends RefCounted
|
||||
class_name TileSetHelper
|
||||
|
||||
## Helper class for managing custom single tiles in TileSet
|
||||
## This allows adding individual tile images as separate sources alongside atlas-based tiles
|
||||
##
|
||||
## EASIEST WAY TO ADD CUSTOM SINGLE TILES:
|
||||
##
|
||||
## METHOD 1 - Manual (Editor):
|
||||
## 1. Select GroundMapLayer in the scene
|
||||
## 2. In Inspector, click on the TileSet property to open TileSet editor
|
||||
## 3. Click "Add Texture(s) to TileSet" button
|
||||
## 4. Select your custom tile image(s)
|
||||
## 5. Each image will be added as a new source with a unique source_id
|
||||
## 6. The tile will be at atlas_coords (0, 0) in that source
|
||||
##
|
||||
## METHOD 2 - Programmatic (Runtime):
|
||||
## Use the helper functions below to add tiles from code:
|
||||
## - add_custom_single_tile() - Add one tile from a file path
|
||||
## - add_custom_tiles_from_directory() - Add all PNG files from a directory
|
||||
##
|
||||
## USAGE NOTES:
|
||||
## - Your existing save/load system already supports multiple sources via source_id
|
||||
## - Custom tiles use source_id > 1 (atlas tiles use source_id = 1)
|
||||
## - Custom tiles always use atlas_coords (0, 0) since they're single-tile sources
|
||||
## - Example: ground_map.set_cell(cell_pos, custom_source_id, Vector2i(0, 0))
|
||||
|
||||
## Adds a custom single tile image to a TileSet from a filesystem path (runtime)
|
||||
## Supports both res:// paths and user filesystem paths
|
||||
## Returns the source_id of the newly added tile, or -1 on failure
|
||||
static func add_custom_single_tile(tile_set: TileSet, tile_texture_path: String, tile_size: Vector2i = Vector2i(72, 72)) -> int:
|
||||
if tile_set == null:
|
||||
push_error("TileSetHelper: tile_set is null")
|
||||
return -1
|
||||
|
||||
# Load the texture - supports both res:// and filesystem paths
|
||||
var texture: Texture2D = _load_texture_from_path(tile_texture_path)
|
||||
if texture == null:
|
||||
push_error("TileSetHelper: Failed to load texture from path: %s" % tile_texture_path)
|
||||
return -1
|
||||
|
||||
# Create a new TileSetAtlasSource
|
||||
var atlas_source: TileSetAtlasSource = TileSetAtlasSource.new()
|
||||
atlas_source.texture = texture
|
||||
atlas_source.texture_region_size = tile_size
|
||||
|
||||
# Create a single tile at position (0, 0)
|
||||
atlas_source.create_tile(Vector2i(0, 0))
|
||||
|
||||
# Find the next available source_id
|
||||
var next_source_id: int = _get_next_available_source_id(tile_set)
|
||||
|
||||
# Add the source to the TileSet
|
||||
tile_set.add_source(atlas_source, next_source_id)
|
||||
|
||||
print("TileSetHelper: Added custom tile from '%s' with source_id %d" % [tile_texture_path, next_source_id])
|
||||
return next_source_id
|
||||
|
||||
## Loads a texture from either res:// or filesystem path
|
||||
static func _load_texture_from_path(path: String) -> Texture2D:
|
||||
# Try res:// path first (for project resources)
|
||||
if path.begins_with("res://"):
|
||||
return load(path) as Texture2D
|
||||
|
||||
# For filesystem paths, load the image and create a texture
|
||||
var image: Image = Image.new()
|
||||
var error: Error = image.load_from_file(path)
|
||||
if error != OK:
|
||||
push_error("TileSetHelper: Failed to load image from file: %s (Error: %d)" % [path, error])
|
||||
return null
|
||||
|
||||
var texture: ImageTexture = ImageTexture.create_from_image(image)
|
||||
return texture
|
||||
|
||||
## Adds multiple custom single tiles from a directory
|
||||
## Supports both res:// and filesystem paths
|
||||
## Returns a dictionary mapping file paths to their source_ids
|
||||
static func add_custom_tiles_from_directory(tile_set: TileSet, directory_path: String, tile_size: Vector2i = Vector2i(72, 72)) -> Dictionary:
|
||||
var result: Dictionary = {}
|
||||
|
||||
if tile_set == null:
|
||||
push_error("TileSetHelper: tile_set is null")
|
||||
return result
|
||||
|
||||
var dir: DirAccess
|
||||
if directory_path.begins_with("res://"):
|
||||
dir = DirAccess.open(directory_path)
|
||||
else:
|
||||
dir = DirAccess.open(directory_path)
|
||||
|
||||
if dir == null:
|
||||
push_error("TileSetHelper: Failed to open directory: %s" % directory_path)
|
||||
return result
|
||||
|
||||
dir.list_dir_begin()
|
||||
var file_name: String = dir.get_next()
|
||||
|
||||
while file_name != "":
|
||||
if not dir.current_is_dir():
|
||||
# Check for common image extensions
|
||||
var lower_name: String = file_name.to_lower()
|
||||
if lower_name.ends_with(".png") or lower_name.ends_with(".jpg") or lower_name.ends_with(".jpeg"):
|
||||
var full_path: String
|
||||
if directory_path.begins_with("res://"):
|
||||
full_path = directory_path.path_join(file_name)
|
||||
else:
|
||||
full_path = directory_path.path_join(file_name)
|
||||
|
||||
var source_id: int = add_custom_single_tile(tile_set, full_path, tile_size)
|
||||
if source_id >= 0:
|
||||
result[full_path] = source_id
|
||||
file_name = dir.get_next()
|
||||
|
||||
return result
|
||||
|
||||
## Gets the next available source_id in the TileSet
|
||||
static func _get_next_available_source_id(tile_set: TileSet) -> int:
|
||||
var max_id: int = 0
|
||||
for i in range(tile_set.get_source_count()):
|
||||
var source_id: int = tile_set.get_source_id(i)
|
||||
max_id = max(max_id, source_id)
|
||||
return max_id + 1
|
||||
|
||||
## Gets information about a tile source
|
||||
## Returns a dictionary with source_id, texture_path, and tile_count
|
||||
static func get_source_info(tile_set: TileSet, source_id: int) -> Dictionary:
|
||||
if tile_set == null:
|
||||
return {}
|
||||
|
||||
var source: TileSetSource = tile_set.get_source(source_id)
|
||||
if source == null:
|
||||
return {}
|
||||
|
||||
var info: Dictionary = {
|
||||
"source_id": source_id,
|
||||
"tile_count": 0
|
||||
}
|
||||
|
||||
if source is TileSetAtlasSource:
|
||||
var atlas_source: TileSetAtlasSource = source as TileSetAtlasSource
|
||||
info["texture_path"] = atlas_source.texture.resource_path if atlas_source.texture else ""
|
||||
info["tile_count"] = atlas_source.get_tiles_count()
|
||||
|
||||
return info
|
||||
|
||||
## Lists all sources in the TileSet
|
||||
static func list_all_sources(tile_set: TileSet) -> Array:
|
||||
var sources: Array = []
|
||||
|
||||
if tile_set == null:
|
||||
return sources
|
||||
|
||||
for i in range(tile_set.get_source_count()):
|
||||
var source_id: int = tile_set.get_source_id(i)
|
||||
var info: Dictionary = get_source_info(tile_set, source_id)
|
||||
sources.append(info)
|
||||
|
||||
return sources
|
||||
Loading…
Add table
Add a link
Reference in a new issue