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)
|
custom_minimum_size = Vector2(125, 120)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
item_count = 4
|
item_count = 6
|
||||||
item_0/text = "Neu"
|
item_0/text = "Neu"
|
||||||
item_1/text = "Laden"
|
item_1/text = "Laden"
|
||||||
item_2/text = "Speichern"
|
item_2/text = "Speichern"
|
||||||
item_3/text = "Speichern als"
|
item_3/text = "Speichern als"
|
||||||
|
item_4/text = "---"
|
||||||
|
item_5/text = "Tile hinzufügen"
|
||||||
|
|
||||||
[node name="PaletteButton" type="Button" parent="UI/MenuTop"]
|
[node name="PaletteButton" type="Button" parent="UI/MenuTop"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
|
||||||
|
|
@ -90,10 +90,11 @@ func get_tile_texcoord(str_palette:String, tile_name:String) -> Vector2i:
|
||||||
return ret_v2
|
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()
|
%MapItemList.deselect_all()
|
||||||
match index:
|
match index:
|
||||||
0: print("Neue Karte erstellen")
|
0:
|
||||||
|
print("Neue Karte erstellen")
|
||||||
1:
|
1:
|
||||||
print("Karte laden")
|
print("Karte laden")
|
||||||
load_map_from()
|
load_map_from()
|
||||||
|
|
@ -103,6 +104,12 @@ func _on_map_item_selected(index: int) -> void: # 1 laden, 2 speichern, 3 speich
|
||||||
3:
|
3:
|
||||||
print("Karte speichern als...")
|
print("Karte speichern als...")
|
||||||
save_map_as()
|
save_map_as()
|
||||||
|
4:
|
||||||
|
# Separator - do nothing
|
||||||
|
pass
|
||||||
|
5:
|
||||||
|
print("Custom Tile hinzufügen...")
|
||||||
|
add_custom_tile_from_file()
|
||||||
|
|
||||||
map_menu_container.hide()
|
map_menu_container.hide()
|
||||||
|
|
||||||
|
|
@ -194,3 +201,75 @@ func scroll_left():
|
||||||
func scroll_right():
|
func scroll_right():
|
||||||
var tile_width = ground_map.tile_set.tile_size.x
|
var tile_width = ground_map.tile_set.tile_size.x
|
||||||
camera_2d.position.x += tile_width
|
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