Add tetromino movement
This commit is contained in:
@@ -2,14 +2,13 @@
|
||||
|
||||
[ext_resource type="Script" uid="uid://c8041v2usigk4" path="res://scripts/tetromino.gd" id="1_hprdj"]
|
||||
[ext_resource type="Resource" uid="uid://curn7voye0ewx" path="res://resources/data/tetrominos/i_piece.tres" id="2_f3wyc"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://5jb2cluw5746" path="res://assets/models/MSH_T_Corta.res" id="3_f3wyc"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://cp721yqfdga1n" path="res://assets/models/MSH_T_Corta.res" id="3_f3wyc"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_f3wyc"]
|
||||
|
||||
[node name="Tetromino" type="Node3D"]
|
||||
script = ExtResource("1_hprdj")
|
||||
resource = ExtResource("2_f3wyc")
|
||||
mesh_color = Color(1, 0, 0, 1)
|
||||
|
||||
[node name="Visuals" type="Node3D" parent="."]
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ signal placement_invalid(reason: String)
|
||||
var _grid: Dictionary = {} # Key: Vector3i, Value: GridCell3D
|
||||
var _tetrominoes: Array[Tetromino] = []
|
||||
var _occupied_cells: Dictionary = {} # Key: Vector3i, Value: Tetromino3D reference
|
||||
var selected_tetromino: Tetromino
|
||||
|
||||
# Ray casting for placement validation
|
||||
var _space_state: PhysicsDirectSpaceState3D
|
||||
@@ -28,6 +29,20 @@ var _space_state: PhysicsDirectSpaceState3D
|
||||
func _ready() -> void:
|
||||
_space_state = get_world_3d().direct_space_state
|
||||
_initialize_grid()
|
||||
|
||||
# Connect to every tetrominos already present on board
|
||||
var curr_tetrominos = $TetrominoContainer.get_children();
|
||||
for t in curr_tetrominos:
|
||||
t.selected.connect(_on_tetromino_selected)
|
||||
t.deselected.connect(_on_tetromino_deselected)
|
||||
|
||||
func _on_tetromino_selected(tetromino: Tetromino):
|
||||
print("Tetromino selected")
|
||||
selected_tetromino = tetromino
|
||||
|
||||
func _on_tetromino_deselected(tetromino: Tetromino):
|
||||
print("Tetromino deselected")
|
||||
selected_tetromino = null
|
||||
|
||||
|
||||
## Initializes the 3D grid structure with empty cells.
|
||||
@@ -72,16 +87,16 @@ func is_cell_empty(grid_pos: Vector3i) -> bool:
|
||||
|
||||
|
||||
## Attempts to place a tetromino at the given grid position with optional rotation.
|
||||
func place_tetromino(tetromino: Tetromino, grid_position: Vector3i, rotation: Quaternion = Quaternion.IDENTITY) -> bool:
|
||||
func place_tetromino(tetromino: Tetromino, grid_position: Vector3i, rot: Quaternion = Quaternion.IDENTITY) -> bool:
|
||||
# Validate placement
|
||||
if not _can_place_tetromino(tetromino, grid_position, rotation):
|
||||
if not _can_place_tetromino(tetromino, grid_position, rot):
|
||||
placement_invalid.emit("Invalid placement: overlapping or out of bounds")
|
||||
return false
|
||||
|
||||
# Update tetromino state
|
||||
tetromino.grid_position = grid_position
|
||||
tetromino.rotation_quat = rotation
|
||||
tetromino.world_position = grid_to_world(grid_position)
|
||||
tetromino.rotation_quat = rot
|
||||
tetromino.global_position = grid_to_world(grid_position)
|
||||
|
||||
# Mark cells as occupied
|
||||
var cells = tetromino.get_grid_cells(grid_position)
|
||||
@@ -97,8 +112,8 @@ func place_tetromino(tetromino: Tetromino, grid_position: Vector3i, rotation: Qu
|
||||
|
||||
## Moves a tetromino to a new position (used during combat).
|
||||
func move_tetromino(tetromino: Tetromino, new_grid_position: Vector3i) -> bool:
|
||||
if tetromino not in _tetrominoes:
|
||||
return false
|
||||
#if tetromino not in _tetrominoes:
|
||||
#return false
|
||||
|
||||
# Clear old occupation
|
||||
var old_cells = tetromino.get_grid_cells(tetromino.grid_position)
|
||||
@@ -107,20 +122,20 @@ func move_tetromino(tetromino: Tetromino, new_grid_position: Vector3i) -> bool:
|
||||
if cell_pos in _grid:
|
||||
_grid[cell_pos].owner = null
|
||||
|
||||
# Validate new position
|
||||
if not _can_place_tetromino(tetromino, new_grid_position, tetromino.rotation_quat):
|
||||
# Restore old occupation
|
||||
for cell_pos in old_cells:
|
||||
_occupied_cells[cell_pos] = tetromino
|
||||
if cell_pos in _grid:
|
||||
_grid[cell_pos].owner = tetromino
|
||||
placement_invalid.emit("Cannot move to target position")
|
||||
return false
|
||||
## Validate new position
|
||||
#if not _can_place_tetromino(tetromino, new_grid_position, tetromino.rotation_quat):
|
||||
## Restore old occupation
|
||||
#for cell_pos in old_cells:
|
||||
#_occupied_cells[cell_pos] = tetromino
|
||||
#if cell_pos in _grid:
|
||||
#_grid[cell_pos].owner = tetromino
|
||||
#placement_invalid.emit("Cannot move to target position")
|
||||
#return false
|
||||
|
||||
# Update position
|
||||
var old_position = tetromino.grid_position
|
||||
tetromino.grid_position = new_grid_position
|
||||
tetromino.world_position = grid_to_world(new_grid_position)
|
||||
tetromino.global_position = grid_to_world(new_grid_position)
|
||||
|
||||
# Mark cells as occupied
|
||||
var new_cells = tetromino.get_grid_cells(new_grid_position)
|
||||
@@ -129,7 +144,7 @@ func move_tetromino(tetromino: Tetromino, new_grid_position: Vector3i) -> bool:
|
||||
if cell_pos in _grid:
|
||||
_grid[cell_pos].owner = tetromino
|
||||
|
||||
tetromino_moved.emit(tetromino, grid_to_world(old_position), tetromino.world_position)
|
||||
tetromino_moved.emit(tetromino, grid_to_world(old_position), tetromino.global_position)
|
||||
return true
|
||||
|
||||
|
||||
@@ -194,13 +209,13 @@ func get_tetromino_at(grid_pos: Vector3i) -> Tetromino:
|
||||
## Gets all adjacent tetrominoes within synergy radius.
|
||||
func get_adjacent_tetrominoes(tetromino: Tetromino, synergy_radius: float = 2.5) -> Array[Tetromino]:
|
||||
var adjacent: Array[Tetromino] = []
|
||||
var world_pos = tetromino.world_position
|
||||
var world_pos = tetromino.global_position
|
||||
|
||||
for other in _tetrominoes:
|
||||
if other == tetromino:
|
||||
continue
|
||||
|
||||
var distance = world_pos.distance_to(other.world_position)
|
||||
var distance = world_pos.distance_to(other.global_position)
|
||||
if distance <= synergy_radius:
|
||||
adjacent.append(other)
|
||||
|
||||
@@ -208,9 +223,9 @@ func get_adjacent_tetrominoes(tetromino: Tetromino, synergy_radius: float = 2.5)
|
||||
|
||||
|
||||
## Validates whether a tetromino can be placed at the given position with rotation.
|
||||
func _can_place_tetromino(tetromino: Tetromino, grid_position: Vector3i, rotation: Quaternion) -> bool:
|
||||
func _can_place_tetromino(tetromino: Tetromino, grid_position: Vector3i, rot: Quaternion) -> bool:
|
||||
# Get the cells this tetromino would occupy
|
||||
var cells = tetromino.get_grid_cells_with_rotation(grid_position, rotation)
|
||||
var cells = tetromino.get_grid_cells_with_rotation(grid_position, rot)
|
||||
|
||||
# Check all cells are within bounds and empty
|
||||
for cell_pos in cells:
|
||||
@@ -222,7 +237,7 @@ func _can_place_tetromino(tetromino: Tetromino, grid_position: Vector3i, rotatio
|
||||
return false
|
||||
|
||||
# Raycast check for physical obstacles
|
||||
if not _validate_placement_raycast(tetromino, grid_position, rotation):
|
||||
if not _validate_placement_raycast(tetromino, grid_position):
|
||||
return false
|
||||
|
||||
return true
|
||||
@@ -230,7 +245,7 @@ func _can_place_tetromino(tetromino: Tetromino, grid_position: Vector3i, rotatio
|
||||
|
||||
## Performs raycast validation for tetromino placement.
|
||||
## Checks for physical obstacles using raycasting.
|
||||
func _validate_placement_raycast(tetromino: Tetromino, grid_position: Vector3i, rotation: Quaternion) -> bool:
|
||||
func _validate_placement_raycast(tetromino: Tetromino, grid_position: Vector3i) -> bool:
|
||||
if not _space_state:
|
||||
return true
|
||||
|
||||
|
||||
@@ -32,6 +32,9 @@ signal health_changed(amount: int)
|
||||
@export var starting_health: int = 100
|
||||
@export var starting_gold: int = 500
|
||||
|
||||
#
|
||||
@onready var camera: Camera3D = $Camera3D
|
||||
|
||||
# State management
|
||||
var _current_state: State = State.DRAFT
|
||||
var _previous_state: State
|
||||
@@ -71,11 +74,11 @@ func _ready() -> void:
|
||||
_event_bus = get_node("/root/EventBus") if "/root/EventBus" in get_tree().root else null
|
||||
|
||||
# Find board manager in scene
|
||||
_board_manager = get_parent().get_node_or_null("Board")
|
||||
_combat_system = get_parent().get_node_or_null("CombatSystem")
|
||||
_synergy_system = get_parent().get_node_or_null("SynergySystem")
|
||||
_enemy_spawner = get_parent().get_node_or_null("EnemySpawner")
|
||||
_ui_manager = get_parent().get_node_or_null("HUD")
|
||||
_board_manager = get_node_or_null("Board")
|
||||
#_combat_system = get_node_or_null("CombatSystem")
|
||||
#_synergy_system = get_node_or_null("SynergySystem")
|
||||
#_enemy_spawner = get_node_or_null("EnemySpawner")
|
||||
#_ui_manager = get_node_or_null("HUD")
|
||||
|
||||
# Connect to signal events if event bus exists
|
||||
if _event_bus:
|
||||
@@ -88,6 +91,28 @@ func _ready() -> void:
|
||||
_current_wave = 0
|
||||
_transition_to_state(State.DRAFT)
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if _board_manager.selected_tetromino and event is InputEventMouseMotion:
|
||||
var mouse_pos = get_viewport().get_mouse_position()
|
||||
var ray_origin = camera.project_ray_origin(mouse_pos)
|
||||
var ray_direction = camera.project_ray_normal(mouse_pos)
|
||||
|
||||
# Cast ray to find ground position (Y = 0 plane)
|
||||
var t = -ray_origin.y / ray_direction.y if ray_direction.y != 0 else 0.0
|
||||
var world_hit = ray_origin + ray_direction * t
|
||||
|
||||
# Convert to grid position
|
||||
var new_grid_pos = _board_manager.world_to_grid(world_hit)
|
||||
_board_manager.move_tetromino(_board_manager.selected_tetromino, new_grid_pos)
|
||||
|
||||
func _on_tetromino_selected(tetromino: Tetromino):
|
||||
print("Tetromino selected")
|
||||
_board_manager.selected_tetromino = tetromino
|
||||
|
||||
func _on_tetromino_deselected(tetromino: Tetromino):
|
||||
print("Tetromino deselected")
|
||||
_board_manager.selected_tetromino = null
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
_state_timer += delta
|
||||
|
||||
@@ -5,16 +5,17 @@
|
||||
class_name Tetromino
|
||||
extends Node3D
|
||||
|
||||
signal hovered(tetromino)
|
||||
signal unhovered(tetromino)
|
||||
signal selected(tetromino, grid_pos)
|
||||
signal deselected(tetromino)
|
||||
signal hovered(tetromino: Tetromino)
|
||||
signal unhovered(tetromino: Tetromino)
|
||||
signal selected(tetromino: Tetromino)
|
||||
signal deselected(tetromino: Tetromino)
|
||||
|
||||
|
||||
# Data
|
||||
@export var resource: TetrominoDefinition
|
||||
|
||||
# Visual
|
||||
@export var mesh_color: Color = Color.WHITE
|
||||
var _mesh_color: Color = Color.WHITE
|
||||
var _material: StandardMaterial3D
|
||||
var _is_ghost: bool = false
|
||||
var _original_color: Color = Color.WHITE
|
||||
@@ -63,12 +64,12 @@ func get_grid_cells(at_position: Vector3i = grid_position) -> PackedVector3Array
|
||||
|
||||
|
||||
## Gets grid cells with a specific rotation applied.
|
||||
func get_grid_cells_with_rotation(at_position: Vector3i, rotation: Quaternion) -> PackedVector3Array:
|
||||
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 = rotation * Vector3(cell)
|
||||
var rotated = rot * Vector3(cell)
|
||||
var rotated_int = Vector3i(rotated.round())
|
||||
result.append(at_position + rotated_int)
|
||||
|
||||
@@ -92,7 +93,7 @@ func get_bounds() -> AABB:
|
||||
|
||||
## Sets the color/material of the tetromino.
|
||||
func set_color(color: Color) -> void:
|
||||
mesh_color = color
|
||||
_mesh_color = color
|
||||
_original_color = color
|
||||
if _material:
|
||||
_material.albedo_color = color
|
||||
@@ -125,7 +126,7 @@ func set_highlighted(enabled: bool) -> void:
|
||||
if _material:
|
||||
_material.emission_enabled = enabled
|
||||
if enabled:
|
||||
_material.emission = mesh_color
|
||||
_material.emission = _mesh_color
|
||||
_material.emission_energy_multiplier = 2.0
|
||||
else:
|
||||
_material.emission_energy_multiplier = 0.0
|
||||
@@ -143,10 +144,8 @@ func _to_string() -> String:
|
||||
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)
|
||||
selected.emit(self)
|
||||
elif event.pressed and event.button_index == MOUSE_BUTTON_RIGHT:
|
||||
print("Tetromino released")
|
||||
deselected.emit(self)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user