diff --git a/src/logic-scenes/board/card-board.gd b/src/logic-scenes/board/card-board.gd index fdb37db..6478ab6 100644 --- a/src/logic-scenes/board/card-board.gd +++ b/src/logic-scenes/board/card-board.gd @@ -1,11 +1,309 @@ extends PanelContainer +var area_dict = {} +enum ui_context {DROPZONE, POST_IT_LIST, ASSIGN_POST_IT} + +@onready var dropzone = $HBoxContainer/dropzone +@onready var postit_container = $HBoxContainer/ScrollContainer/VBoxContainer +@onready var board_of_devs = $"board of devs" +@onready var active_context = ui_context.DROPZONE # 0 = dropzone, 1 = post it list + +var currently_selected_node: Area2D = null +var currently_selected_card_for_assigning: Area2D = null +var is_area_dragged: bool = false +var currently_dragged_area: Area2D + +var selected_dropzone_element: int = -1 +var selected_postit_list_element: int = 0 +var selected_card_for_assignment # Called when the node enters the scene tree for the first time. func _ready(): - pass - - + + var test_arr = ["c_Joy","p_effort","c_backlash","c_body","c_hit","p_slut","p_worried_mother","p_cross_friend"] + + populate_board(test_arr) + reorder_areas("dropzone_content") + active_context = ui_context.DROPZONE + + # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta): - pass + # Reset information about Areas being dragged, if the mouse is not longer pressed. + # Needed because otherwise it can happen that the areas don't register it if you stop clicking on them. + if !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) and is_area_dragged: + currently_dragged_area.is_dragged = false + is_area_dragged = false + currently_dragged_area = null + + + +# Will be used later to spawn Cards and Post-Its and remember them in the dictionary +func populate_board(card_names: Array): + var all_cards = Array() + var all_postits = Array() + + # define entries in dictionary + area_dict["dropzone_content"] = Array() + area_dict["post_its_in_list"] = Array() + + # to remember panel positions + area_dict["post_it_panels"] = get_child(0).get_child(1).get_child(0).get_children() + + # get all the cards and post-its from the board of devs + for child in board_of_devs.get_children(): + for child_2 in child.get_children(): # put all cards in all_cards array + all_cards.push_back(child_2) + for child_3 in child_2.get_children(): # put all post-its in all_postits array + if "p_" in child_3.name: # post-its are currently children of cards. + all_postits.push_back(child_3) # If this changes, this logic needs to be adjusted. + + # spawning the cards and adding them to the dictionary + for card_name in card_names: + if "c_" in card_name: # spawning a card + var new_card = _find_area_by_string(all_cards, card_name).duplicate() + for child in new_card.get_children(): # We need to clear all the post-its from the cards on the dev-board + if "p_" in child.name: + new_card.remove_child(child) # dev-board has post-its attached to the cards, which need to be removed + dropzone.add_child(new_card) + new_card.set_owner(self) + area_dict["dropzone_content"].push_back(new_card) + new_card.global_position = Vector2(300, 200) # using hard-coded position because I'm lazy + new_card.is_dragable = true + if "p_" in card_name: # spawning a post-it + var new_postit = _find_area_by_string(all_postits, card_name).duplicate() + for panel in area_dict["post_it_panels"]: # still mad I can't use the return_postit-func for this... + if panel.get_child_count() == 1: + panel.add_child(new_postit) + new_postit.set_owner(self) + area_dict["post_its_in_list"].push_back(new_postit) + new_postit.position = panel.get_child(0).position + new_postit.is_dragable = true + break + + currently_selected_node = area_dict["dropzone_content"][0] # set first Card as currently selected node by default + +# Handy function to filter an array of areas by the name of a card +func _find_area_by_string(area_arr: Array, name: String) -> Area2D: + for area in area_arr: + if area.name == name: + return area + return null + +# Checks if a Node is currently inside the dropzone +func is_in_dropzone(to_check: Node) -> bool: + if (dropzone.size.x < to_check.global_position.x or dropzone.size.y < to_check.global_position.y): + return false + elif (to_check.global_position.x < 0 or to_check.global_position.y < 0): + return false + else: + return true + +# called if a mouse button is pressed +func handle_mouse_button(to_handle: Area2D, input: InputEvent): + # No two areas can be dragged at the same time. + # Make sure that only the same area is dragged. + # Otherwise overlapping areas are dragged at the same time. + if currently_dragged_area != null and to_handle != currently_dragged_area: + return + + currently_selected_node = to_handle # update currently selected + currently_dragged_area = to_handle + to_handle.is_dragged = input.pressed + is_area_dragged = input.pressed + + # TODO: We need a better way to recognize whether "to_handle" is a Card or a Post-It. + # (Tried checking for a script, didn't work, because script has no name attached. + # Alternative might be to check for specific values within the script ("is_card" f.e)) + match to_handle.get_meta("type"): + "card": # 1 = Card + active_context = ui_context.DROPZONE + if input.is_pressed(): + reorder_areas("dropzone_content") + else: + currently_dragged_area = null + "post-it": # 2 = PostIt + if input.is_pressed(): + to_handle.reparent(dropzone) + to_handle.set_owner(self) # needs to be here otherwise the owner disappears + area_dict["post_its_in_list"].erase(to_handle) + area_dict["dropzone_content"].push_back(to_handle) + # TODO (if needed): Add function to rearrange the array based on positions in the dropzone + else: + if is_in_dropzone(to_handle): + if to_handle.has_overlapping_areas(): + for area in to_handle.get_overlapping_areas(): + if area.get_meta("type") == "card": + if !area.has_postit_attached(): + attach_postit_to_card(to_handle, area) + else: + to_handle.rotation = to_handle.base_rotation + to_handle.scale = to_handle.base_scale + else: + active_context = ui_context.POST_IT_LIST + _return_postit_to_panels(to_handle) + currently_dragged_area = null + + +# Logic for attaching a postit to a card. Also reset postit positions if the card cannot be attached +func attach_postit_to_card(postit: Area2D, card: Area2D, update_dict = false): + postit.reparent(card) + postit.set_owner(self) + postit.position = card.get_child(3).position + + if update_dict: + area_dict["post_its_in_list"].erase(postit) + area_dict["dropzone_content"].push_back(postit) + + reorder_areas("dropzone_content") + reorder_areas("post_its_in_list") + + +# Mark area that was hovered over as currently selected +func handle_hover(to_handle: Area2D): + if to_handle != currently_selected_node: + currently_selected_node.highlighted = false + currently_selected_node = to_handle + + if is_in_dropzone(to_handle): + selected_dropzone_element = area_dict["dropzone_content"].find(to_handle) + active_context = ui_context.DROPZONE + else: + selected_postit_list_element = area_dict["post_its_in_list"].find(to_handle) + active_context = ui_context.POST_IT_LIST + + +# Reorders the areas in any of the dictionaries entries +# Pass the entry's key in order to reorder it +func reorder_areas(reorder: String): + var old_order = area_dict[reorder] + var new_order = Array() + + for obj in old_order: + var i = 0 + if !new_order.is_empty(): + for obj_2 in new_order: + if obj_2.position.y < obj.position.y: + i += 1 + new_order.insert(i, obj) + + +# Takes the inputs for control inputs +func _input(event): + # Return, if the input is a mouse event (mouse events are handled separately) + if event is InputEventMouse: return + + if event.is_action_pressed("ui_up"): # up to select an element above + match active_context: + ui_context.DROPZONE: + selected_dropzone_element -= 1 + ui_context.POST_IT_LIST: + selected_postit_list_element -= 1 + ui_context.ASSIGN_POST_IT: + selected_card_for_assignment -= 1 + + elif event.is_action_pressed("ui_down"): # down to select an element beneath + match active_context: + ui_context.DROPZONE: + selected_dropzone_element += 1 + ui_context.POST_IT_LIST: + selected_postit_list_element += 1 + ui_context.ASSIGN_POST_IT: + selected_card_for_assignment += 1 + + elif event.is_action_pressed("ui_left"): # left to switch context to the left + active_context -= 1 + if active_context < 0: + active_context = ui_context.POST_IT_LIST + + elif event.is_action_pressed("ui_right"): # right to switch context to the right + active_context += 1 + if active_context > 2: + active_context = ui_context.DROPZONE + + elif event.is_action_pressed("ui_select"): # select the selected post it + if active_context == ui_context.ASSIGN_POST_IT: # to assign it to a card + attach_postit_to_card(currently_selected_node, + currently_selected_card_for_assigning, true) + _leave_assignment_context() + else: + _enter_assignment_context() + + # TODO: I forgor the HECKING RIGHT-CLICK!!!!111 AAAAAAAAAAAAAAAAAAAA + + # do some adjustments to loop elements (after last element, select first one etc.) + if selected_dropzone_element < 0: + selected_dropzone_element = area_dict["dropzone_content"].size()-1 + elif selected_dropzone_element > area_dict["dropzone_content"].size()-1: + selected_dropzone_element = 0 + + if selected_postit_list_element < 0: + selected_postit_list_element = area_dict["post_its_in_list"].size()-1 + elif selected_postit_list_element > area_dict["post_its_in_list"].size()-1: + selected_postit_list_element = 0 + + if active_context == ui_context.ASSIGN_POST_IT: # skip this if we're not in assign post it context + if selected_card_for_assignment < 0: + selected_card_for_assignment = area_dict["dropzone_content"].size()-1 + elif selected_card_for_assignment > area_dict["dropzone_content"].size()-1: + selected_card_for_assignment = 0 + + # highlight the selected element + match active_context: + ui_context.DROPZONE: + currently_selected_node.highlighted = false + currently_selected_node = area_dict["dropzone_content"][selected_dropzone_element] + currently_selected_node.highlighted = true + ui_context.POST_IT_LIST: + currently_selected_node.highlighted = false + currently_selected_node = area_dict["post_its_in_list"][selected_postit_list_element] + currently_selected_node.highlighted = true + ui_context.ASSIGN_POST_IT: + currently_selected_card_for_assigning.highlighted = false + currently_selected_card_for_assigning = area_dict["dropzone_content"][selected_card_for_assignment] + currently_selected_card_for_assigning.highlighted = true + + # update dictiornary orders + reorder_areas("dropzone_content") + reorder_areas("post_its_in_list") + + +# Sets everything up to enter the context where postits can be assigned via button controls +func _enter_assignment_context(): + # cards are currently not moved, only post its. Exit function if a card should be moved. + if currently_selected_node == null or currently_selected_node.get_meta("type") != "post-it" : return + + # if the postit is already attached, remove it and return it to the post it panels + if currently_selected_node.is_postit_attached(): + _return_postit_to_panels(currently_selected_node) + active_context = ui_context.POST_IT_LIST + return + + # adjust everything for the post it to select its attach-target + active_context = ui_context.ASSIGN_POST_IT + selected_card_for_assignment = 0 + currently_selected_card_for_assigning = area_dict["dropzone_content"][0] + currently_selected_card_for_assigning.highlighted = true + +# leaves the context for assigning postit via button controls +func _leave_assignment_context(): + currently_selected_node.highlighted = false + active_context = ui_context.DROPZONE + currently_selected_node = currently_selected_card_for_assigning + +# handles everything to return a post it to the panels +func _return_postit_to_panels(postit: Area2D): + for panel in area_dict["post_it_panels"]: + if panel.get_child_count() == 1: + postit.reparent(panel) + postit.set_owner(self) + area_dict["dropzone_content"].erase(postit) + area_dict["post_its_in_list"].push_back(postit) + postit.position = panel.get_child(0).position + postit.rotation = postit.base_rotation + postit.scale = postit.base_scale + reorder_areas("dropzone_content") + reorder_areas("post_its_in_list") + break + + diff --git a/src/logic-scenes/board/card.gd b/src/logic-scenes/board/card.gd index 369fbce..caf015d 100644 --- a/src/logic-scenes/board/card.gd +++ b/src/logic-scenes/board/card.gd @@ -56,6 +56,9 @@ var is_dragged: bool = false func _ready(): + self.set_meta("type", "card") # set type information to find out if this node is a card + + _handle_wiggle(0) if not Engine.is_editor_hint() and is_inside_tree(): for postit in self.get_children(): if postit is PostIt: self.own_postits.append(postit as PostIt) @@ -80,6 +83,8 @@ func _process(delta: float) -> void: if highlighted: _handle_wiggle(delta) + + _move_card() func _handle_wiggle(delta): wiggle_pos += delta * wiggle_speed * wiggle_intensity @@ -108,13 +113,22 @@ func _on_mouse_exited(): highlighted = false func _on_input_event(viewport, event, shape_idx): - if event is InputEventMouseMotion and is_dragged: - position += event.relative + + if event is InputEventMouseMotion: + _move_card() if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_LEFT: - if is_dragable: - is_dragged = event.pressed - if "handle_mouse_button" in owner: + if is_dragable and "handle_mouse_button" in owner: owner.handle_mouse_button(self, event) +func _move_card(): + if is_dragged: + position += get_viewport().get_mouse_position() - position + +func has_postit_attached() -> bool: + var all_children = get_children() + for child in all_children: + if child.get_meta("type") == "post-it": + return true + return false diff --git a/src/logic-scenes/board/card.tscn b/src/logic-scenes/board/card.tscn index 7125c0a..29f1a82 100644 --- a/src/logic-scenes/board/card.tscn +++ b/src/logic-scenes/board/card.tscn @@ -194,13 +194,8 @@ theme = ExtResource("3_rktsa") theme_type_variation = &"card_text" autowrap_mode = 3 -[node name="postit anchor" type="Control" parent="."] -layout_mode = 3 -anchors_preset = 0 -offset_left = 100.0 -offset_top = 148.0 -offset_right = 140.0 -offset_bottom = 188.0 +[node name="postit anchor" type="Node2D" parent="."] +position = Vector2(-65.6478, 60.3852) [connection signal="input_event" from="." to="." method="_on_input_event"] [connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] diff --git a/src/logic-scenes/board/physics-board.tscn b/src/logic-scenes/board/physics-board.tscn index 4b1b0c5..d419f00 100644 --- a/src/logic-scenes/board/physics-board.tscn +++ b/src/logic-scenes/board/physics-board.tscn @@ -40,13 +40,13 @@ mouse_filter = 1 [node name="card3" parent="HBoxContainer/dropzone" instance=ExtResource("3_mg053")] z_as_relative = false position = Vector2(371, 290) -rotation = 0.00712087 +rotation = 0.00381354 collision_layer = 2147483649 +is_dragable = true [node name="card" parent="HBoxContainer/dropzone" instance=ExtResource("3_mg053")] position = Vector2(640, 659) -rotation = 0.0714344 -scale = Vector2(1, 1) +rotation = -0.0287091 is_dragable = true [node name="ScrollContainer" type="ScrollContainer" parent="HBoxContainer"] @@ -61,6 +61,9 @@ custom_minimum_size = Vector2(400, 120) layout_mode = 2 mouse_filter = 1 +[node name="post-it_anchor1" type="Node2D" parent="HBoxContainer/ScrollContainer/VBoxContainer/Panel"] +position = Vector2(105, 57) + [node name="post-it" parent="HBoxContainer/ScrollContainer/VBoxContainer/Panel" instance=ExtResource("5_048k0")] z_index = 1 position = Vector2(105, 57) @@ -71,6 +74,9 @@ custom_minimum_size = Vector2(400, 120) layout_mode = 2 mouse_filter = 1 +[node name="post-it_anchor2" type="Node2D" parent="HBoxContainer/ScrollContainer/VBoxContainer/Panel2"] +position = Vector2(105, 57) + [node name="post-it" parent="HBoxContainer/ScrollContainer/VBoxContainer/Panel2" instance=ExtResource("5_048k0")] position = Vector2(105, 57) is_dragable = true @@ -80,6 +86,9 @@ custom_minimum_size = Vector2(400, 120) layout_mode = 2 mouse_filter = 1 +[node name="post-it_anchor3" type="Node2D" parent="HBoxContainer/ScrollContainer/VBoxContainer/Panel3"] +position = Vector2(105, 57) + [node name="post-it" parent="HBoxContainer/ScrollContainer/VBoxContainer/Panel3" instance=ExtResource("5_048k0")] position = Vector2(105, 57) is_dragable = true @@ -89,6 +98,9 @@ custom_minimum_size = Vector2(400, 120) layout_mode = 2 mouse_filter = 1 +[node name="post-it_anchor4" type="Node2D" parent="HBoxContainer/ScrollContainer/VBoxContainer/Panel4"] +position = Vector2(105, 57) + [node name="post-it" parent="HBoxContainer/ScrollContainer/VBoxContainer/Panel4" instance=ExtResource("5_048k0")] position = Vector2(105, 57) is_dragable = true @@ -111,7 +123,7 @@ shape = SubResource("CircleShape2D_x8rl2") position = Vector2(-151, -107) [node name="card2" parent="Node2D/RigidBody2D/ancor" instance=ExtResource("3_mg053")] -rotation = -0.0180459 +rotation = -0.0498721 collision_layer = 2 [node name="RigidBody2D2" type="Area2D" parent="Node2D"] @@ -125,7 +137,7 @@ shape = SubResource("CircleShape2D_x8rl2") position = Vector2(-151, -107) [node name="card2" parent="Node2D/RigidBody2D2/ancor" instance=ExtResource("3_mg053")] -rotation = 0.0612526 +rotation = -0.0210958 collision_layer = 2 [node name="RigidBody2D3" type="Area2D" parent="Node2D"] @@ -139,5 +151,5 @@ shape = SubResource("CircleShape2D_x8rl2") position = Vector2(-151, -107) [node name="card2" parent="Node2D/RigidBody2D3/ancor" instance=ExtResource("3_mg053")] -rotation = 0.0528008 +rotation = -0.00481569 collision_layer = 2 diff --git a/src/logic-scenes/board/post-it.gd b/src/logic-scenes/board/post-it.gd index 19ba4d0..5471ffb 100644 --- a/src/logic-scenes/board/post-it.gd +++ b/src/logic-scenes/board/post-it.gd @@ -42,9 +42,14 @@ var modulate_tween @export var voice_line: AudioStream = null @export var is_dragable: bool = false +@onready var base_rotation = rotation +@onready var base_scale = scale var is_dragged = false func _ready() -> void: + + self.set_meta("type", "post-it") # set type information to find out if this node is a post-it + $Content/Label.text = self.text $Content/BackgroundSprite.frame = text.hash() % $Content/BackgroundSprite.sprite_frames.get_frame_count($Content/BackgroundSprite.animation) @@ -55,7 +60,8 @@ func replace_with(postit: PostIt): self.sibling = postit.sibling func _process(delta: float) -> void: - pass + _move_post_it() + func _on_focus_entered(): print(self, "is focused") @@ -73,12 +79,20 @@ func _on_mouse_exited(): highlighted = false func _on_input_event(viewport, event, shape_idx): - if event is InputEventMouseMotion and is_dragged: - position += event.relative + if event is InputEventMouseMotion: + _move_post_it() if event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_LEFT: - if is_dragable: - is_dragged = event.pressed - if "handle_mouse_button" in owner: + if is_dragable and "handle_mouse_button" in owner: owner.handle_mouse_button(self, event) + +func _move_post_it(): + if is_dragged: + position += get_viewport().get_mouse_position() - position + +func is_postit_attached() -> bool: + if self.get_parent().get_meta("type") == "card": + return true + return false + diff --git a/src/logic-scenes/board/post-it.tscn b/src/logic-scenes/board/post-it.tscn index 15f49b0..b31e579 100644 --- a/src/logic-scenes/board/post-it.tscn +++ b/src/logic-scenes/board/post-it.tscn @@ -103,7 +103,6 @@ animations = [{ [node name="post-it" type="Area2D"] script = ExtResource("1_yvh5n") highlight_color = Color(1.2, 1.2, 1.2, 1) -is_dragable = null [node name="CollisionShape2D" type="CollisionShape2D" parent="."] position = Vector2(99.5, 0)