Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 64 additions & 51 deletions addons/TileMapLayer3D/TileMapLayer3D_plugin.gd
Original file line number Diff line number Diff line change
Expand Up @@ -450,40 +450,38 @@ func _handle_mesh_rotations(event: InputEventKey, camera: Camera3D) -> int:
_on_vertex_delete_requested()
return AFTER_GUI_INPUT_STOP
return AFTER_GUI_INPUT_PASS

if _is_key_matching_action(event,"tilemaplayer3d_rotate_left"):
placement_manager.current_mesh_rotation = (placement_manager.current_mesh_rotation - 1) % GlobalConstants.MAX_SPIN_ROTATION_STEPS
if placement_manager.current_mesh_rotation < 0:
placement_manager.current_mesh_rotation += GlobalConstants.MAX_SPIN_ROTATION_STEPS
needs_update = true

if _is_key_matching_action(event,"tilemaplayer3d_rotate_right"):
placement_manager.current_mesh_rotation = (placement_manager.current_mesh_rotation + 1) % GlobalConstants.MAX_SPIN_ROTATION_STEPS
needs_update = true

if _is_key_matching_action(event,"tilemaplayer3d_flip"):
placement_manager.is_current_face_flipped = not placement_manager.is_current_face_flipped
needs_update = true

if _is_key_matching_action(event,"tilemaplayer3d_tilt"):
if event.shift_pressed:
GlobalPlaneDetector.cycle_tilt_backward()
else:
GlobalPlaneDetector.cycle_tilt_forward()
needs_update = true

match event.physical_keycode:
KEY_Q:
placement_manager.current_mesh_rotation = (placement_manager.current_mesh_rotation - 1) % GlobalConstants.MAX_SPIN_ROTATION_STEPS
if placement_manager.current_mesh_rotation < 0:
placement_manager.current_mesh_rotation += GlobalConstants.MAX_SPIN_ROTATION_STEPS
needs_update = true

KEY_E:
placement_manager.current_mesh_rotation = (placement_manager.current_mesh_rotation + 1) % GlobalConstants.MAX_SPIN_ROTATION_STEPS
needs_update = true

KEY_F:
placement_manager.is_current_face_flipped = not placement_manager.is_current_face_flipped
needs_update = true

KEY_R:
if event.shift_pressed:
GlobalPlaneDetector.cycle_tilt_backward()
else:
GlobalPlaneDetector.cycle_tilt_forward()
needs_update = true

var should_be_flipped: bool = GlobalPlaneDetector.determine_rotation_flip_for_plane(GlobalPlaneDetector.current_plane_6d)

placement_manager.is_current_face_flipped = should_be_flipped

var should_be_flipped: bool = GlobalPlaneDetector.determine_rotation_flip_for_plane(GlobalPlaneDetector.current_plane_6d)

KEY_T:
GlobalPlaneDetector.reset_to_flat()
placement_manager.current_mesh_rotation = 0
needs_update = true
var default_flip: bool = GlobalPlaneDetector.determine_auto_flip_for_plane(GlobalPlaneDetector.current_plane_6d)
placement_manager.is_current_face_flipped = default_flip
placement_manager.is_current_face_flipped = should_be_flipped

if _is_key_matching_action(event,"tilemaplayer3d_reset"):
GlobalPlaneDetector.reset_to_flat()
placement_manager.current_mesh_rotation = 0
needs_update = true
var default_flip: bool = GlobalPlaneDetector.determine_auto_flip_for_plane(GlobalPlaneDetector.current_plane_6d)
placement_manager.is_current_face_flipped = default_flip

if needs_update:
if current_tile_map3d and current_tile_map3d.settings:
Expand Down Expand Up @@ -513,26 +511,28 @@ func _handle_cursor3d_movement(event: InputEventKey, camera: Camera3D) -> int:
var handled: bool = false
var move_vector: Vector3 = Vector3.ZERO
var basis: Basis = camera.global_transform.basis

match event.physical_keycode:
KEY_W:
if shift_pressed:
move_vector = GlobalUtil._get_snapped_cardinal_vector(basis.y)
else:
move_vector = GlobalUtil._get_snapped_cardinal_vector(-basis.z)
handled = true
KEY_S:
if shift_pressed:



if _is_key_matching_action(event,"tilemaplayer3d_up"):
if shift_pressed:
move_vector = GlobalUtil._get_snapped_cardinal_vector(basis.y)
else:
move_vector = GlobalUtil._get_snapped_cardinal_vector(-basis.z)
handled = true

if _is_key_matching_action(event,"tilemaplayer3d_down"):
if shift_pressed:
move_vector = GlobalUtil._get_snapped_cardinal_vector(-basis.y)
else:
move_vector = GlobalUtil._get_snapped_cardinal_vector(basis.z)
handled = true
KEY_A:
move_vector = GlobalUtil._get_snapped_cardinal_vector(-basis.x)
handled = true
KEY_D:
move_vector = GlobalUtil._get_snapped_cardinal_vector(basis.x)
handled = true
else:
move_vector = GlobalUtil._get_snapped_cardinal_vector(basis.z)
handled = true
if _is_key_matching_action(event,"tilemaplayer3d_left"):
move_vector = GlobalUtil._get_snapped_cardinal_vector(-basis.x)
handled = true
if _is_key_matching_action(event,"tilemaplayer3d_right"):
move_vector = GlobalUtil._get_snapped_cardinal_vector(basis.x)
handled = true

if handled:
if move_vector.length_squared() > 0.0:
Expand Down Expand Up @@ -2538,3 +2538,16 @@ func _delete_selected_tiles() -> void:
current_tile_map3d.smart_selected_tiles.clear()
current_tile_map3d.clear_highlights()
current_tile_map3d.update_gizmos()

static func _is_key_matching_action(event: InputEventKey, action_name: String) -> bool:
var action_events := InputMap.action_get_events(action_name)

for action_event in action_events:
if action_event is InputEventKey:
if event.physical_keycode != KEY_NONE and action_event.physical_keycode != KEY_NONE:
if event.physical_keycode == action_event.physical_keycode:
return true
elif event.keycode == action_event.keycode:
return true

return false
12 changes: 12 additions & 0 deletions addons/TileMapLayer3D/core/global/global_const.gd
Original file line number Diff line number Diff line change
Expand Up @@ -683,3 +683,15 @@ const VERTEX_HANDLE_SIZE: float = 0.05
const VERTEX_WIREFRAME_COLOR: Color = Color(1.0, 0.4, 0.4, 0.8)

const VERTEX_TILE_WARNING_THRESHOLD: int = 100

const DEFAULT_KEY_BINDINGS = {
"tilemaplayer3d_up": KEY_W,
"tilemaplayer3d_down": KEY_S,
"tilemaplayer3d_left": KEY_A,
"tilemaplayer3d_right": KEY_D,
"tilemaplayer3d_rotate_right": KEY_E,
"tilemaplayer3d_rotate_left": KEY_Q,
"tilemaplayer3d_flip":KEY_F,
"tilemaplayer3d_tilt":KEY_R,
"tilemaplayer3d_reset":KEY_T
}
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ extends Resource
arch_radius_ratio = clampf(value, GlobalConstants.ARCH_MIN_RADIUS_RATIO, GlobalConstants.ARCH_MAX_RADIUS_RATIO)
emit_changed()

@export var keybindings: Dictionary = {}

static func create_default() -> TileMapLayerSettings:
var settings: TileMapLayerSettings = TileMapLayerSettings.new()
settings._settings_format_version = 1
Expand Down
157 changes: 155 additions & 2 deletions addons/TileMapLayer3D/ui/tileset_panel.gd
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ extends PanelContainer
@onready var remove_terrain_button: Button = %RemoveTerrainButton
@onready var terrain_name_input: LineEdit = %TerrainNameInput

@onready var up_key_button: Button = %UpKeyButton
@onready var down_key_button: Button = %DownKeyButton
@onready var left_key_button: Button = %LeftKeyButton
@onready var right_key_button: Button = %RightKeyButton

signal tile_selected(uv_rect: Rect2)
signal multi_tile_selected(uv_rects: Array[Rect2], anchor_index: int)
Expand Down Expand Up @@ -100,13 +104,28 @@ var _current_zoom: float = GlobalConstants.TILESET_DEFAULT_ZOOM
var _is_updating_zoom: bool = false
var _previous_texture: Texture2D = null

@onready var _keybindings_map: Dictionary = {
"tilemaplayer3d_up": %UpKeyButton,
"tilemaplayer3d_down": %DownKeyButton,
"tilemaplayer3d_left": %LeftKeyButton,
"tilemaplayer3d_right": %RightKeyButton,
"tilemaplayer3d_rotate_right": %RotateRightKeyButton,
"tilemaplayer3d_rotate_left": %RotateLeftKeyButton,
"tilemaplayer3d_flip":%FlipFaceKeyButton,
"tilemaplayer3d_tilt":%TiltFaceKeyButton,
"tilemaplayer3d_reset":%ResetFaceKeyButton
}
var _is_rebinding: bool = false
var _active_action: String = ""
var _rebinding_button: Button = null


var _current_tiling_mode: GlobalConstants.MainAppMode = GlobalConstants.MainAppMode.MANUAL

var _selected_tiles: Array[Rect2] = []

func _ready() -> void:

_ensure_keybinding_actions_exist()
_connect_signals()
manual_tiling_tab.show()
set_tiling_mode_from_external(GlobalConstants.MainAppMode.MANUAL)
Expand Down Expand Up @@ -241,7 +260,14 @@ func _connect_signals() -> void:

if load_tile_set_button and not load_tile_set_button.pressed.is_connected(_on_load_tileset_file_pressed):
load_tile_set_button.pressed.connect(_on_load_tileset_file_pressed)


for action_name in _keybindings_map:
var button : Button = _keybindings_map[action_name]

if button and not button.pressed.is_connected(_on_keybinding_button_pressed):
button.pressed.connect(_on_keybinding_button_pressed.bind(action_name))
_update_keybinding_button_text(action_name)


## Returns current TileSet tile size (used by AutotileTab for TileSet creation).
## The picker grid uses `_tile_size` directly and is intentionally separate.
Expand Down Expand Up @@ -444,6 +470,22 @@ func _load_settings_to_ui(settings: TileMapLayerSettings) -> void:
grid_size_changed.emit(settings.grid_size)

enabled_arched_tiles_checkbox.button_pressed = settings.enable_arched_tiles if _on_enabled_arched_tiles_toggled else false

if settings and settings.keybindings:
for action_name in _keybindings_map:
var event: InputEventKey
if settings.keybindings.has(action_name):
event = settings.keybindings[action_name]
else:
event = _get_key_event(GlobalConstants.DEFAULT_KEY_BINDINGS[action_name])

if event:
if not InputMap.has_action(action_name):
InputMap.add_action(action_name)

InputMap.action_erase_events(action_name)
InputMap.action_add_event(action_name, event)
_update_keybinding_button_text(action_name)

_is_loading_from_node = false

Expand Down Expand Up @@ -511,6 +553,13 @@ func _save_ui_to_settings() -> void:

if _on_enabled_arched_tiles_toggled:
current_tilemap3d_node.settings.enable_arched_tiles = enabled_arched_tiles_checkbox.button_pressed

for action_name in _keybindings_map:
if InputMap.has_action(action_name):
var events := InputMap.action_get_events(action_name)
if events.size() > 0:
current_tilemap3d_node.settings.keybindings[action_name] = events[0]



func _clear_ui() -> void:
Expand Down Expand Up @@ -1119,3 +1168,107 @@ func _set_scroll_position(scroll_pos: Vector2) -> void:

scroll_container.scroll_horizontal = int(scroll_pos.x)
scroll_container.scroll_vertical = int(scroll_pos.y)

func _unhandled_input(event: InputEvent) -> void:
if not _is_rebinding:
return

if event is InputEventKey and event.pressed:
if event.keycode == KEY_ESCAPE:
_cancel_rebinding()

if event is InputEventKey and event.pressed and not event.is_echo():
InputMap.action_erase_events(_active_action)
InputMap.action_add_event(_active_action, event)

_update_keybinding_button_text(_active_action)
_is_rebinding = false
_active_action = ""
get_viewport().set_input_as_handled()

_save_ui_to_settings()
else:
_is_rebinding=false
_update_keybinding_button_text(_active_action)
_active_action = ""
get_viewport().set_input_as_handled()

func _input(event: InputEvent) -> void:
# catch key presses to cancel the rebinding
if not _is_rebinding:
return

if event is InputEventKey and event.is_pressed() and not event.is_echo():
_complete_rebinding(event)
get_viewport().set_input_as_handled()
return

if event is InputEventMouseButton and event.is_pressed():
if _rebinding_button:
var click_pos: Vector2 = event.position
var button_rect: Rect2 = _rebinding_button.get_global_rect()

if not button_rect.has_point(click_pos):
_cancel_rebinding()

static func _get_key_event(keycode):
var ev:InputEventKey=InputEventKey.new()
ev.keycode=keycode
ev.physical_keycode=keycode
return ev

func _ensure_keybinding_actions_exist() -> void:
for action_name in _keybindings_map.keys():
if not InputMap.has_action(action_name):
InputMap.add_action(action_name)
var default_event : InputEventKey= _get_key_event(GlobalConstants.DEFAULT_KEY_BINDINGS[action_name])
InputMap.action_add_event(action_name, default_event)


func _on_keybinding_button_pressed(action_name: String) -> void:
if _is_rebinding:
_cancel_rebinding()
return

_is_rebinding = true
_active_action = action_name
_rebinding_button = _keybindings_map[action_name]
_rebinding_button.text = "New key..."


func _update_keybinding_button_text(action_name) -> void:

var btn: Button = _keybindings_map.get(action_name)
if not btn:
return

if InputMap.has_action(action_name):
var events := InputMap.action_get_events(action_name)
if events.size() > 0:
btn.text = events[0].as_text().to_upper()
return
btn.text = "NONE"


func _complete_rebinding(new_event: InputEventKey) -> void:
if not InputMap.has_action(_active_action):
InputMap.add_action(_active_action)

InputMap.action_erase_events(_active_action)
InputMap.action_add_event(_active_action, new_event)

_update_keybinding_button_text(_active_action)
_save_ui_to_settings()
_reset_rebind_state()


func _cancel_rebinding() -> void:
if _active_action != "":
_update_keybinding_button_text(_active_action)
_reset_rebind_state()


func _reset_rebind_state() -> void:
_is_rebinding = false
_active_action = ""
_rebinding_button = null
Loading