@@ -94,6 +94,7 @@ void MainScene::_notification(int p_what) {
9494}
9595
9696void MainScene::_input (const Ref<InputEvent>& event) {
97+ // --- Mouse Wheel Zoom ---
9798 if (!camera_node) {
9899 return ;
99100 }
@@ -117,6 +118,193 @@ void MainScene::_input(const Ref<InputEvent>& event) {
117118 }
118119 }
119120 }
121+
122+ if (is_animating_solution || is_searching || is_solved) {
123+ return ;
124+ }
125+
126+ Ref<InputEventMouseMotion> mm_event = event;
127+
128+ // --- Start Drag Piece ---
129+ if (mb_event.is_valid () && mb_event->get_button_index () == MouseButton::MOUSE_BUTTON_LEFT && mb_event->is_pressed ()) {
130+ if (camera_node && floor) {
131+ Vector2 mouse_pos_viewport = get_viewport ()->get_mouse_position ();
132+ PhysicsDirectSpaceState3D* space_state = get_world_3d ()->get_direct_space_state ();
133+
134+ if (space_state) {
135+ Vector3 ray_origin = camera_node->project_ray_origin (mouse_pos_viewport);
136+ Vector3 ray_direction = camera_node->project_ray_normal (mouse_pos_viewport);
137+ Vector3 ray_end = ray_origin + ray_direction * 1000.0 ;
138+
139+ Ref<PhysicsRayQueryParameters3D> query_params;
140+ query_params.instantiate ();
141+ query_params->set_from (ray_origin);
142+ query_params->set_to (ray_end);
143+ query_params->set_collide_with_bodies (true );
144+
145+
146+ Dictionary result = space_state->intersect_ray (query_params);
147+
148+ if (!result.is_empty ()) {
149+ Object* collider_obj = result[" collider" ];
150+ Node3D* hit_node = Object::cast_to<Node3D>(collider_obj);
151+
152+ if (hit_node) {
153+ bool piece_found_for_drag = false ;
154+ for (auto & p_data_iter : this ->pieces ) {
155+ auto map_entry = coord_to_car_node_map.find (p_data_iter.coordinates );
156+ if (map_entry != coord_to_car_node_map.end () && map_entry->second == hit_node) {
157+ if (p_data_iter.id == ' K' ) continue ;
158+
159+ dragged_piece_data = &p_data_iter;
160+ dragged_car_node = hit_node;
161+ piece_found_for_drag = true ;
162+ break ;
163+ }
164+ }
165+
166+ if (piece_found_for_drag) {
167+ is_dragging_piece = true ;
168+ drag_start_car_3d_pos = dragged_car_node->get_position ();
169+ drag_start_piece_coords = dragged_piece_data->coordinates ;
170+
171+ Plane floor_plane (Vector3 (0 , 1 , 0 ), dragged_car_node->get_global_transform ().origin .y );
172+ Vector3 intersection_point_vector3;
173+
174+ if (floor_plane.intersects_ray (ray_origin, ray_direction, &intersection_point_vector3)) {
175+ drag_start_mouse_world_pos = intersection_point_vector3;
176+ } else {
177+ is_dragging_piece = false ;
178+ dragged_car_node = nullptr ;
179+ dragged_piece_data = nullptr ;
180+ UtilityFunctions::printerr (" Drag Error: Could not project mouse to drag plane (intersects_ray returned false)." );
181+ return ;
182+ }
183+
184+ UtilityFunctions::print (" Start dragging piece: " , String::chr (dragged_piece_data->id ));
185+ if (solve_button) solve_button->set_disabled (true );
186+ if (reset_button) reset_button->set_disabled (true );
187+ if (load_button) load_button->set_disabled (true );
188+ if (algo_button) algo_button->set_disabled (true );
189+ }
190+ }
191+ }
192+ }
193+ }
194+ }
195+ // --- Mouse Motion ---
196+ else if (mm_event.is_valid () && is_dragging_piece && dragged_car_node && dragged_piece_data) {
197+ Vector2 mouse_pos_viewport = get_viewport ()->get_mouse_position ();
198+ Vector3 ray_origin = camera_node->project_ray_origin (mouse_pos_viewport);
199+ Vector3 ray_direction = camera_node->project_ray_normal (mouse_pos_viewport);
200+
201+ Plane floor_plane (Vector3 (0 , 1 , 0 ), drag_start_car_3d_pos.y );
202+ Vector3 current_mouse_world_pos_vector3;
203+
204+ if (floor_plane.intersects_ray (ray_origin, ray_direction, ¤t_mouse_world_pos_vector3)) {
205+ Vector3 world_delta = current_mouse_world_pos_vector3 - drag_start_mouse_world_pos;
206+ Vector3 new_potential_car_pos = drag_start_car_3d_pos;
207+
208+ if (dragged_piece_data->is_vertical ) {
209+ new_potential_car_pos.z += world_delta.z ;
210+ } else {
211+ new_potential_car_pos.x += world_delta.x ;
212+ }
213+
214+ Coordinates potential_grid_coords = _get_grid_coords_from_3d_position (new_potential_car_pos, dragged_piece_data->size , dragged_piece_data->is_vertical );
215+ Coordinates target_move_coords = drag_start_piece_coords;
216+ int steps_to_check = 0 ;
217+ int direction_step = 0 ;
218+
219+ if (dragged_piece_data->is_vertical ) {
220+ steps_to_check = potential_grid_coords.y - drag_start_piece_coords.y ;
221+ direction_step = (steps_to_check == 0 ) ? 0 : (steps_to_check > 0 ? 1 : -1 );
222+ for (int s = 1 ; s <= abs (steps_to_check); ++s) {
223+ Coordinates check_coord = {drag_start_piece_coords.x , drag_start_piece_coords.y + s * direction_step};
224+ if (_is_move_valid (drag_start_piece_coords, check_coord, dragged_piece_data->id , true )) {
225+ target_move_coords.y = check_coord.y ;
226+ } else {
227+ break ;
228+ }
229+ }
230+ } else {
231+ steps_to_check = potential_grid_coords.x - drag_start_piece_coords.x ;
232+ direction_step = (steps_to_check == 0 ) ? 0 : (steps_to_check > 0 ? 1 : -1 );
233+ for (int s = 1 ; s <= abs (steps_to_check); ++s) {
234+ Coordinates check_coord = {drag_start_piece_coords.x + s * direction_step, drag_start_piece_coords.y };
235+ if (_is_move_valid (drag_start_piece_coords, check_coord, dragged_piece_data->id , false )) {
236+ target_move_coords.x = check_coord.x ;
237+ } else {
238+ break ;
239+ }
240+ }
241+ }
242+ Vector3 snapped_3d_pos = _get_3d_position_for_piece_coords (target_move_coords, dragged_piece_data->size , dragged_piece_data->is_vertical );
243+ dragged_car_node->set_position (snapped_3d_pos);
244+ }
245+ }
246+ // --- Drop ---
247+ else if (mb_event.is_valid () && mb_event->get_button_index () == MouseButton::MOUSE_BUTTON_LEFT && !mb_event->is_pressed ()) {
248+ if (is_dragging_piece && dragged_car_node && dragged_piece_data) {
249+ Vector3 final_car_3d_pos = dragged_car_node->get_position ();
250+ Coordinates final_grid_coords = _get_grid_coords_from_3d_position (final_car_3d_pos, dragged_piece_data->size , dragged_piece_data->is_vertical );
251+
252+ bool move_was_valid_and_occurred = false ;
253+ if (final_grid_coords != drag_start_piece_coords) {
254+ if (_is_move_valid (drag_start_piece_coords, final_grid_coords, dragged_piece_data->id , dragged_piece_data->is_vertical )){
255+ move_was_valid_and_occurred = true ;
256+ } else {
257+ move_was_valid_and_occurred = true ;
258+ }
259+ }
260+
261+
262+ if (move_was_valid_and_occurred) {
263+ UtilityFunctions::print (" Piece " , String::chr (dragged_piece_data->id ), " moved from (" ,
264+ drag_start_piece_coords.x - board.piece_padding , " ," , drag_start_piece_coords.y - board.piece_padding ,
265+ " ) to (" ,
266+ final_grid_coords.x - board.piece_padding , " ," , final_grid_coords.y - board.piece_padding , " )" );
267+
268+ // Update
269+ auto map_iter = coord_to_car_node_map.find (drag_start_piece_coords);
270+ if (map_iter != coord_to_car_node_map.end ()) coord_to_car_node_map.erase (map_iter);
271+ dragged_piece_data->coordinates = final_grid_coords;
272+ coord_to_car_node_map[final_grid_coords] = dragged_car_node;
273+
274+ Vector3 confirmed_snapped_3d_pos = _get_3d_position_for_piece_coords (final_grid_coords, dragged_piece_data->size , dragged_piece_data->is_vertical );
275+ dragged_car_node->set_position (confirmed_snapped_3d_pos);
276+
277+ current_manual_moves++;
278+ move_label->set_text (" Moves: " + String::num_int64 (current_manual_moves));
279+
280+ if (is_solved) {
281+ is_solved = false ;
282+ time_label->set_text (" Time: 0.0 ms" );
283+ node_label->set_text (" Nodes: 0" );
284+ current_solution.moves .clear ();
285+ }
286+ if (Utils::is_exit (board, pieces)) {
287+ add_notification (" Congratulations! You reached the exit in " + String::num_int64 (current_manual_moves) + " moves!" );
288+ is_solved = true ;
289+ if (solve_button) solve_button->set_disabled (true );
290+ }
291+
292+
293+ } else {
294+ UtilityFunctions::print (" Move for piece " , String::chr (dragged_piece_data->id ), " was invalid or no change. Reverting." );
295+ dragged_car_node->set_position (drag_start_car_3d_pos);
296+ }
297+
298+ // Reset drag
299+ is_dragging_piece = false ;
300+ dragged_car_node = nullptr ;
301+ dragged_piece_data = nullptr ;
302+ if (solve_button && !Utils::is_exit (board, pieces)) solve_button->set_disabled (false );
303+ if (reset_button) reset_button->set_disabled (false );
304+ if (load_button) load_button->set_disabled (false );
305+ if (algo_button) algo_button->set_disabled (false );
306+ }
307+ }
120308}
121309
122310void MainScene::_process (double delta) {
@@ -407,7 +595,6 @@ void MainScene::_on_load_file_selected(const String& path) {
407595 reset_button->set_disabled (false );
408596
409597 _clear_all_cars ();
410-
411598
412599 Node3D* gate_node = Object::cast_to<Node3D>(gate_template->instantiate ());
413600 if (gate_node) {
@@ -674,6 +861,8 @@ void MainScene::_clear_all_cars() {
674861 node_label->set_text (" Nodes: 0" );
675862 time_label->set_text (" Time: 0.0 ms" );
676863 move_label->set_text (" Moves: 0" );
864+ current_manual_moves = 0 ;
865+ is_solved = false ;
677866}
678867
679868void MainScene::_spawn_piece_as_car (const Piece& piece_data) {
@@ -765,4 +954,96 @@ void MainScene::add_notification(const String& p_message) {
765954 if (!notification_container) UtilityFunctions::printerr (" Notification Error: Notification container not found." );
766955 if (!notification_label_template) UtilityFunctions::printerr (" Notification Error: Notification panel template not found." );
767956 }
957+ }
958+
959+ Coordinates MainScene::_get_grid_coords_from_3d_position (const Vector3& car_3d_pos, int piece_size, bool is_vertical_piece) {
960+ float actual_col_float, actual_row_float;
961+
962+ if (is_vertical_piece) {
963+ actual_col_float = car_3d_pos.x + static_cast <float >(board.cols ) / 2 .0f - 0 .5f ;
964+ if (piece_size >= 2 ) {
965+ actual_row_float = car_3d_pos.z + (static_cast <float >(board.rows ) - (static_cast <float >(piece_size - 2 .0f ))) / 2 .0f - 1 .0f ;
966+ } else {
967+ actual_row_float = car_3d_pos.z + static_cast <float >(board.rows ) / 2 .0f - 0 .5f ;
968+ }
969+ } else { // Horizontal
970+ actual_row_float = car_3d_pos.z + static_cast <float >(board.rows ) / 2 .0f - 0 .5f ;
971+ if (piece_size >= 2 ) {
972+ actual_col_float = car_3d_pos.x + (static_cast <float >(board.cols ) - (static_cast <float >(piece_size - 2 .0f ))) / 2 .0f - 1 .0f ;
973+ } else {
974+ actual_col_float = car_3d_pos.x + static_cast <float >(board.cols ) / 2 .0f - 0 .5f ;
975+ }
976+ }
977+
978+ int actual_col = static_cast <int >(std::round (actual_col_float));
979+ int actual_row = static_cast <int >(std::round (actual_row_float));
980+
981+ Coordinates grid_coords_with_padding;
982+ grid_coords_with_padding.x = actual_col + board.piece_padding ;
983+ grid_coords_with_padding.y = actual_row + board.piece_padding ;
984+
985+ return grid_coords_with_padding;
986+ }
987+
988+ bool MainScene::_is_move_valid (const Coordinates& from_coords, const Coordinates& to_coords, char piece_id_moving, bool is_vertical_piece_moving) {
989+ if (from_coords.x == to_coords.x && from_coords.y == to_coords.y ) {
990+ return true ;
991+ }
992+
993+ const Piece* p_moving_data = nullptr ;
994+ for (const auto & p_iter : this ->pieces ){
995+ if (p_iter.id == piece_id_moving) {
996+ p_moving_data = &p_iter;
997+ break ;
998+ }
999+ }
1000+ if (!p_moving_data) {
1001+ UtilityFunctions::printerr (" _is_move_valid Error: Piece data not found for ID: " , String::chr (piece_id_moving));
1002+ return false ;
1003+ }
1004+
1005+ if (is_vertical_piece_moving) {
1006+ if (from_coords.x != to_coords.x ) return false ;
1007+
1008+ int dir_y = (to_coords.y > from_coords.y ) ? 1 : -1 ;
1009+ int steps = abs (to_coords.y - from_coords.y );
1010+
1011+ for (int i = 1 ; i <= steps; ++i) {
1012+ int next_cell_y_of_top_left = from_coords.y + i * dir_y;
1013+
1014+ int check_cell_r;
1015+ if (dir_y > 0 ) {
1016+ check_cell_r = from_coords.y + p_moving_data->size -1 + i * dir_y;
1017+ } else {
1018+ check_cell_r = from_coords.y + i * dir_y;
1019+ }
1020+ int check_cell_c = from_coords.x ;
1021+
1022+ if (!Utils::is_cell_clear (board, check_cell_r, check_cell_c, this ->pieces , piece_id_moving)) {
1023+ return false ;
1024+ }
1025+ }
1026+ } else {
1027+ if (from_coords.y != to_coords.y ) return false ;
1028+
1029+ int dir_x = (to_coords.x > from_coords.x ) ? 1 : -1 ;
1030+ int steps = abs (to_coords.x - from_coords.x );
1031+
1032+ for (int i = 1 ; i <= steps; ++i) {
1033+ int next_cell_x_of_top_left = from_coords.x + i * dir_x;
1034+
1035+ int check_cell_c;
1036+ if (dir_x > 0 ) {
1037+ check_cell_c = from_coords.x + p_moving_data->size -1 + i * dir_x;
1038+ } else {
1039+ check_cell_c = from_coords.x + i * dir_x;
1040+ }
1041+ int check_cell_r = from_coords.y ;
1042+
1043+ if (!Utils::is_cell_clear (board, check_cell_r, check_cell_c, this ->pieces , piece_id_moving)) {
1044+ return false ;
1045+ }
1046+ }
1047+ }
1048+ return true ;
7681049}
0 commit comments