diff --git a/scenes/board/tetromino.tscn b/scenes/board/tetromino.tscn index 7a45ce3..c768366 100644 --- a/scenes/board/tetromino.tscn +++ b/scenes/board/tetromino.tscn @@ -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="."] diff --git a/scripts/board_manager.gd b/scripts/board_manager.gd index 54aee4e..c3adc05 100644 --- a/scripts/board_manager.gd +++ b/scripts/board_manager.gd @@ -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 diff --git a/scripts/encounter_manager.gd b/scripts/encounter_manager.gd index 081eba7..f46939a 100644 --- a/scripts/encounter_manager.gd +++ b/scripts/encounter_manager.gd @@ -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 diff --git a/scripts/tetromino.gd b/scripts/tetromino.gd index a881692..1c73aa6 100644 --- a/scripts/tetromino.gd +++ b/scripts/tetromino.gd @@ -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)