254 lines
9.0 KiB
GDScript
254 lines
9.0 KiB
GDScript
## Interactive drop zone system with sensor partitioning and visual debugging.
|
|
##
|
|
## DropZone provides sophisticated drag-and-drop target detection with configurable
|
|
## sensor areas, partitioning systems, and visual debugging capabilities. It integrates
|
|
## with CardContainer to enable precise card placement and reordering operations.
|
|
##
|
|
## Key Features:
|
|
## - Flexible sensor sizing and positioning with dynamic adjustment
|
|
## - Vertical/horizontal partitioning for precise drop targeting
|
|
## - Visual debugging with colored outlines and partition indicators
|
|
## - Mouse detection with global coordinate transformation
|
|
## - Accept type filtering for specific draggable object types
|
|
##
|
|
## Partitioning System:
|
|
## - Vertical partitions: Divide sensor into left-right sections for card ordering
|
|
## - Horizontal partitions: Divide sensor into up-down sections for layered placement
|
|
## - Dynamic outline generation for visual feedback during development
|
|
##
|
|
## Usage:
|
|
## [codeblock]
|
|
## var drop_zone = DropZone.new()
|
|
## drop_zone.init(container, ["card"])
|
|
## drop_zone.set_sensor(Vector2(200, 300), Vector2.ZERO, null, false)
|
|
## drop_zone.set_vertical_partitions([100, 200, 300])
|
|
## [/codeblock]
|
|
class_name DropZone
|
|
extends Control
|
|
|
|
|
|
|
|
# Dynamic sensor properties with automatic UI synchronization
|
|
## Size of the drop sensor area
|
|
var sensor_size: Vector2:
|
|
set(value):
|
|
sensor.size = value
|
|
sensor_outline.size = value
|
|
|
|
## Position offset of the drop sensor relative to DropZone
|
|
var sensor_position: Vector2:
|
|
set(value):
|
|
sensor.position = value
|
|
sensor_outline.position = value
|
|
|
|
## @deprecated: Since it was designed to debug the sensor, please use sensor_outline_visible instead.
|
|
var sensor_texture : Texture:
|
|
set(value):
|
|
sensor.texture = value
|
|
|
|
## @deprecated: Since it was designed to debug the sensor, please use sensor_outline_visible instead.
|
|
var sensor_visible := true:
|
|
set(value):
|
|
sensor.visible = value
|
|
|
|
## Controls visibility of debugging outlines for sensor and partitions
|
|
var sensor_outline_visible := false:
|
|
set(value):
|
|
sensor_outline.visible = value
|
|
for outline in sensor_partition_outlines:
|
|
outline.visible = value
|
|
|
|
# Core drop zone configuration and state
|
|
## Array of accepted draggable object types (e.g., ["card", "token"])
|
|
var accept_types: Array = []
|
|
## Original sensor size for restoration after dynamic changes
|
|
var stored_sensor_size: Vector2
|
|
## Original sensor position for restoration after dynamic changes
|
|
var stored_sensor_position: Vector2
|
|
## Parent container that owns this drop zone
|
|
var parent: Node
|
|
|
|
# UI components
|
|
## Main sensor control for hit detection (invisible)
|
|
var sensor: Control
|
|
## Debug outline for visual sensor boundary indication
|
|
var sensor_outline: ReferenceRect
|
|
## Array of partition outline controls for debugging
|
|
var sensor_partition_outlines: Array = []
|
|
|
|
# Partitioning system for precise drop targeting
|
|
## Global vertical lines to divide sensing partitions (left to right direction)
|
|
var vertical_partition: Array
|
|
## Global horizontal lines to divide sensing partitions (up to down direction)
|
|
var horizontal_partition: Array
|
|
|
|
|
|
## Initializes the drop zone with parent reference and accepted drag types.
|
|
## Creates sensor and debugging UI components.
|
|
## @param _parent: Container that owns this drop zone
|
|
## @param accept_types: Array of draggable object types this zone accepts
|
|
func init(_parent: Node, accept_types: Array =[]):
|
|
parent = _parent
|
|
self.accept_types = accept_types
|
|
|
|
# Create invisible sensor for hit detection
|
|
if sensor == null:
|
|
sensor = TextureRect.new()
|
|
sensor.name = "Sensor"
|
|
sensor.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
sensor.z_index = CardFrameworkSettings.VISUAL_SENSOR_Z_INDEX # Behind everything else
|
|
add_child(sensor)
|
|
|
|
# Create debugging outline (initially hidden)
|
|
if sensor_outline == null:
|
|
sensor_outline = ReferenceRect.new()
|
|
sensor_outline.editor_only = false
|
|
sensor_outline.name = "SensorOutline"
|
|
sensor_outline.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
sensor_outline.border_color = CardFrameworkSettings.DEBUG_OUTLINE_COLOR
|
|
sensor_outline.z_index = CardFrameworkSettings.VISUAL_OUTLINE_Z_INDEX
|
|
add_child(sensor_outline)
|
|
|
|
# Initialize default values
|
|
stored_sensor_size = Vector2(0, 0)
|
|
stored_sensor_position = Vector2(0, 0)
|
|
vertical_partition = []
|
|
horizontal_partition = []
|
|
|
|
|
|
## Checks if the mouse cursor is currently within the drop zone sensor area.
|
|
## @returns: True if mouse is inside the sensor bounds
|
|
func check_mouse_is_in_drop_zone() -> bool:
|
|
var mouse_position = get_global_mouse_position()
|
|
var result = sensor.get_global_rect().has_point(mouse_position)
|
|
return result
|
|
|
|
|
|
## Configures the sensor with size, position, texture, and visibility settings.
|
|
## Stores original values for later restoration.
|
|
## @param _size: Size of the sensor area
|
|
## @param _position: Position offset from DropZone origin
|
|
## @param _texture: Optional texture for sensor visualization
|
|
## @param _visible: Whether sensor texture is visible (deprecated)
|
|
func set_sensor(_size: Vector2, _position: Vector2, _texture: Texture, _visible: bool):
|
|
sensor_size = _size
|
|
sensor_position = _position
|
|
stored_sensor_size = _size
|
|
stored_sensor_position = _position
|
|
sensor_texture = _texture
|
|
sensor_visible = _visible
|
|
|
|
|
|
## Dynamically adjusts sensor size and position without affecting stored values.
|
|
## Used for temporary sensor modifications that can be restored later.
|
|
## @param _size: New temporary sensor size
|
|
## @param _position: New temporary sensor position
|
|
func set_sensor_size_flexibly(_size: Vector2, _position: Vector2):
|
|
sensor_size = _size
|
|
sensor_position = _position
|
|
|
|
|
|
## Restores sensor to its original size and position from stored values.
|
|
## Used to undo temporary modifications made by set_sensor_size_flexibly.
|
|
func return_sensor_size():
|
|
sensor_size = stored_sensor_size
|
|
sensor_position = stored_sensor_position
|
|
|
|
|
|
## Adjusts sensor position by adding an offset to the stored position.
|
|
## @param offset: Vector2 offset to add to the original stored position
|
|
func change_sensor_position_with_offset(offset: Vector2):
|
|
sensor_position = stored_sensor_position + offset
|
|
|
|
|
|
## Sets vertical partition lines for drop targeting and creates debug outlines.
|
|
## Vertical partitions divide the sensor into left-right sections for card ordering.
|
|
## @param positions: Array of global X coordinates for partition lines
|
|
func set_vertical_partitions(positions: Array):
|
|
vertical_partition = positions
|
|
|
|
# Clear existing partition outlines
|
|
for outline in sensor_partition_outlines:
|
|
outline.queue_free()
|
|
sensor_partition_outlines.clear()
|
|
|
|
# Create debug outline for each partition
|
|
for i in range(vertical_partition.size()):
|
|
var outline = ReferenceRect.new()
|
|
outline.editor_only = false
|
|
outline.name = "VerticalPartition" + str(i)
|
|
outline.z_index = CardFrameworkSettings.VISUAL_OUTLINE_Z_INDEX
|
|
outline.border_color = CardFrameworkSettings.DEBUG_OUTLINE_COLOR
|
|
outline.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
outline.size = Vector2(1, sensor.size.y) # Vertical line full height
|
|
|
|
# Convert global partition position to local coordinates
|
|
var local_x = vertical_partition[i] - global_position.x
|
|
outline.position = Vector2(local_x, sensor.position.y)
|
|
outline.visible = sensor_outline.visible
|
|
add_child(outline)
|
|
sensor_partition_outlines.append(outline)
|
|
|
|
|
|
func set_horizontal_partitions(positions: Array):
|
|
horizontal_partition = positions
|
|
# clear existing outlines
|
|
for outline in sensor_partition_outlines:
|
|
outline.queue_free()
|
|
sensor_partition_outlines.clear()
|
|
for i in range(horizontal_partition.size()):
|
|
var outline = ReferenceRect.new()
|
|
outline.editor_only = false
|
|
outline.name = "HorizontalPartition" + str(i)
|
|
outline.z_index = CardFrameworkSettings.VISUAL_OUTLINE_Z_INDEX
|
|
outline.border_color = CardFrameworkSettings.DEBUG_OUTLINE_COLOR
|
|
outline.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
outline.size = Vector2(sensor.size.x, 1)
|
|
var local_y = horizontal_partition[i] - global_position.y
|
|
outline.position = Vector2(sensor.position.x, local_y)
|
|
outline.visible = sensor_outline.visible
|
|
add_child(outline)
|
|
sensor_partition_outlines.append(outline)
|
|
|
|
|
|
## Determines which vertical partition the mouse is currently in.
|
|
## Returns the partition index for precise drop targeting.
|
|
## @returns: Partition index (0-based) or -1 if outside sensor or no partitions
|
|
func get_vertical_layers() -> int:
|
|
if not check_mouse_is_in_drop_zone():
|
|
return -1
|
|
|
|
if vertical_partition == null or vertical_partition.is_empty():
|
|
return -1
|
|
|
|
var mouse_position = get_global_mouse_position()
|
|
|
|
# Count how many partition lines the mouse has crossed
|
|
var current_index := 0
|
|
|
|
for i in range(vertical_partition.size()):
|
|
if mouse_position.x >= vertical_partition[i]:
|
|
current_index += 1
|
|
else:
|
|
break
|
|
return current_index
|
|
|
|
|
|
func get_horizontal_layers() -> int:
|
|
if not check_mouse_is_in_drop_zone():
|
|
return -1
|
|
|
|
if horizontal_partition == null or horizontal_partition.is_empty():
|
|
return -1
|
|
|
|
var mouse_position = get_global_mouse_position()
|
|
|
|
var current_index := 0
|
|
|
|
for i in range(horizontal_partition.size()):
|
|
if mouse_position.y >= horizontal_partition[i]:
|
|
current_index += 1
|
|
else:
|
|
break
|
|
return current_index
|