## Represents a 3D tetromino (tower block) with shape, position, and rotation. ## ## Manages 3D grid cell data, mesh instances, position/rotation state, ## synergy detection, and material effects. class_name Tetromino extends Node3D signal hovered(tetromino: Tetromino) signal unhovered(tetromino: Tetromino) signal selected(tetromino: Tetromino) signal deselected(tetromino: Tetromino) # Data @export var resource: TetrominoDefinition # Visual var _mesh_color: Color = Color.WHITE var _material: StandardMaterial3D var _is_ghost: bool = false var _original_color: Color = Color.WHITE # Runtime @onready var base_damage: int = resource.base_damage @onready var fire_rate: float = resource.fire_rate @onready var synergy_radius: float = resource.synergy_radius @onready var synergy_tags: PackedStringArray = resource.synergy_tags @onready var _mesh_instance: MeshInstance3D = get_node("Visuals/MeshInstance3D") # Grid state var grid_position: Vector3i = Vector3i.ZERO var rotation_quat: Quaternion = Quaternion.IDENTITY var _grid_cells: PackedVector3Array = [] # Relative cell coordinates func _ready() -> void: _initialize_material() _set_mesh_representation() pass ## Initializes the material for visual representation. func _initialize_material() -> void: _material = StandardMaterial3D.new() _material.albedo_color = Color.RED _material.metallic = 0.3 _material.roughness = 0.7 ## Creates a simple mesh representation based on shape type. func _set_mesh_representation() -> void: _mesh_instance.mesh = resource.mesh _mesh_instance.set_surface_override_material(0, _material) $SelectionArea/CollisionShape3D.shape = _mesh_instance.mesh.create_trimesh_shape() _grid_cells = resource.grid_cells ## Gets the grid cells occupied by this tetromino at its current position. func get_grid_cells(at_position: Vector3i = grid_position) -> PackedVector3Array: var result = PackedVector3Array() for cell in _grid_cells: result.append(at_position + Vector3i(cell.x, cell.y, cell.z)) return result ## Gets grid cells with a specific rotation applied. func get_grid_cells_with_rotation(at_position: Vector3i, rot: Quaternion) -> PackedVector3Array: var result = PackedVector3Array() # Apply rotation to relative grid cells for cell in _grid_cells: var rotated = rot * Vector3(cell) var rotated_int = Vector3i(rotated.round()) result.append(at_position + rotated_int) return result ## Gets the bounds (AABB) of this tetromino. func get_bounds() -> AABB: var min_cell = Vector3i.ZERO var max_cell = Vector3i.ZERO for cell in _grid_cells: min_cell = min_cell.min(cell) max_cell = max_cell.max(cell) var size = Vector3(max_cell - min_cell) + Vector3.ONE var pos = grid_position + min_cell return AABB(Vector3(pos), size) ## Sets the color/material of the tetromino. func set_color(color: Color) -> void: _mesh_color = color _original_color = color if _material: _material.albedo_color = color ## Enables/disables ghost mode (semi-transparent, movable state). func set_ghost_mode(enabled: bool) -> void: _is_ghost = enabled if enabled: # Create material if needed if not _material: _initialize_material() # Make semi-transparent var ghost_color = _original_color ghost_color.a = 0.5 _material.albedo_color = ghost_color _mesh_instance.set_surface_override_material(0, _material) else: # Restore original appearance if _material: _material.albedo_color = _original_color _material.albedo_color.a = 1.0 _mesh_instance.set_surface_override_material(0, _material) ## Enables/disables highlight for visual feedback. func set_highlighted(enabled: bool) -> void: if _material: _material.emission_enabled = enabled if enabled: _material.emission = _mesh_color _material.emission_energy_multiplier = 2.0 else: _material.emission_energy_multiplier = 0.0 ## Returns string representation for debugging. func _to_string() -> String: return "Tetromino3D[type=%s, pos=%v, cells=%d]" % [ resource.shape_type, grid_position, _grid_cells.size() ] ## Callback used to handle tetromino selection and deselection func _on_selection_area_input_event(camera: Node, event: InputEvent, event_position: Vector3, normal: Vector3, shape_idx: int) -> void: if event is InputEventMouseButton: if event.pressed and event.button_index == MOUSE_BUTTON_LEFT: selected.emit(self) elif event.pressed and event.button_index == MOUSE_BUTTON_RIGHT: deselected.emit(self) ## Callback used to handle tetromino hovering func _on_selection_area_mouse_entered() -> void: print("Tetromino hovered") set_highlighted(true) hovered.emit(self) ## Callback used to handle tetromino unhovering func _on_selection_area_mouse_exited() -> void: print("Tetromino unhovered") set_highlighted(false) unhovered.emit(self)