Files
trenchlock/scripts/tetromino.gd

164 lines
4.8 KiB
GDScript

## 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)
signal unhovered(tetromino)
signal selected(tetromino, grid_pos)
signal deselected(tetromino)
# Data
@export var resource: TetrominoDefinition
# Visual
@export 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, rotation: Quaternion) -> PackedVector3Array:
var result = PackedVector3Array()
# Apply rotation to relative grid cells
for cell in _grid_cells:
var rotated = rotation * 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:
print("Tetromino selected")
selected.emit(self, grid_position)
elif event.pressed and event.button_index == MOUSE_BUTTON_RIGHT:
print("Tetromino released")
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)