Skip to content

Commit bd9c1fa

Browse files
committed
feat: add dnd
1 parent 5d46c87 commit bd9c1fa

File tree

3 files changed

+300
-2
lines changed

3 files changed

+300
-2
lines changed

src/main_scene/main_scene.cpp

Lines changed: 282 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ void MainScene::_notification(int p_what) {
9494
}
9595

9696
void 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, &current_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

122310
void 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

679868
void 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
}

src/main_scene/main_scene.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
#include <godot_cpp/classes/node3d.hpp>
44
#include <godot_cpp/classes/camera3d.hpp>
5+
#include <godot_cpp/classes/world3d.hpp>
56
#include <godot_cpp/classes/input_event_mouse_button.hpp>
7+
#include <godot_cpp/classes/input_event_mouse_motion.hpp>
8+
#include <godot_cpp/classes/physics_direct_space_state3d.hpp>
9+
#include <godot_cpp/classes/physics_ray_query_parameters3d.hpp>
610
#include <godot_cpp/classes/button.hpp>
711
#include <godot_cpp/classes/option_button.hpp>
812
#include <godot_cpp/classes/label.hpp>
@@ -28,6 +32,7 @@
2832
#include <fstream>
2933
#include <vector>
3034
#include <map>
35+
#include <cmath>
3136

3237
#include "../utils/utils.hpp"
3338

@@ -80,6 +85,14 @@ class MainScene : public Node3D {
8085
VBoxContainer* notification_container = nullptr;
8186
Label* notification_label_template = nullptr;
8287

88+
Node3D* dragged_car_node = nullptr;
89+
Piece* dragged_piece_data = nullptr;
90+
Vector3 drag_start_mouse_world_pos;
91+
Vector3 drag_start_car_3d_pos;
92+
Coordinates drag_start_piece_coords;
93+
bool is_dragging_piece = false;
94+
int current_manual_moves = 0;
95+
8396
void _animate_next_move();
8497
void _on_move_animation_finished();
8598
Vector3 _get_3d_position_for_piece_coords(const Coordinates& piece_top_left_coords, int piece_size, bool is_vertical_piece);
@@ -100,6 +113,9 @@ class MainScene : public Node3D {
100113

101114
void add_notification(const String& p_message);
102115

116+
Coordinates _get_grid_coords_from_3d_position(const Vector3& pos_3d, int piece_size, bool is_vertical_piece);
117+
bool _is_move_valid(const Coordinates& from_coords, const Coordinates& to_coords, char piece_id_moving, bool is_vertical);
118+
103119
protected:
104120
static void _bind_methods();
105121
void _notification(int p_what);

tukang-parkir-simulator/scenes/main.tscn

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,5 +281,6 @@ alignment = 2
281281
layout_mode = 2
282282
size_flags_vertical = 1
283283
theme_override_styles/normal = SubResource("StyleBoxFlat_jyhfs")
284-
text = "ini contoh notifikasi"
284+
text = "Congratulations! You reached the exit in \" + String::num_int64(current_manual_moves) + \" moves!"
285285
vertical_alignment = 1
286+
autowrap_mode = 1

0 commit comments

Comments
 (0)