I cannot reproduce your bug with GDScript.
code
class_name ShipPart extends Area2D
var draggable := false
var is_inside_droppable := false
var slot: Node2D
var offset: Vector2
var initial_position: Vector2
var starting_position: Vector2
var previous_parent: Node
@onready var rollover_sound: AudioStreamPlayer = $RolloverSound
@onready var pick_up_sound: AudioStreamPlayer = $PickUpSound
@onready var slot_sound: AudioStreamPlayer = $SlotSound
@onready var go_back_sound: AudioStreamPlayer = $GoBackSound
@onready var sprite_2d: Sprite2D = $Sprite2D
func _ready() -> void:
area_entered.connect(_on_slot_entered)
area_exited.connect(_on_slot_exited)
mouse_entered.connect(_on_mouse_entered)
mouse_exited.connect(_on_mouse_exited)
starting_position = global_position
func _process(_delta: float) -> void:
if draggable:
if Input.is_action_just_pressed("click"):
pick_up_sound.play()
initial_position = global_position
offset = get_global_mouse_position() - global_position
Global.is_dragging = true
if Input.is_action_pressed("click"):
global_position = get_global_mouse_position()
elif Input.is_action_just_released("click"):
Global.is_dragging = false
var tween = create_tween()
if is_inside_droppable:
slot_sound.play()
tween.tween_property(self, "global_position", slot.global_position, 0.2).set_ease(Tween.EASE_OUT)
previous_parent = get_parent()
call_deferred("reparent", slot)
else:
go_back_sound.play()
tween.tween_property(self, "global_position", initial_position, 0.2).set_ease(Tween.EASE_OUT)
if Input.is_action_just_pressed("rotate_left"):
rotation -= deg_to_rad(90)
if rad_to_deg(rotation) <= -360:
rotation = 0
elif Input.is_action_just_pressed("rotate_right"):
rotation += deg_to_rad(90)
if rad_to_deg(rotation) >= 360:
rotation = 0
if Input.is_action_just_pressed("flip"):
sprite_2d.flip_h = !sprite_2d.flip_h
func _on_mouse_entered() -> void:
print("MOUSE ENTER: %s" % name)
if not Global.is_dragging:
rollover_sound.play()
draggable = true
scale = Vector2.ONE * 1.05
func _on_mouse_exited() -> void:
print("MOUSE EXIT: %s" % name)
if not Global.is_dragging:
draggable = false
scale = Vector2.ONE
func _on_slot_entered(area: Area2D) -> void:
is_inside_droppable = true
rollover_sound.play()
slot = area
func _on_slot_exited(_area: Area2D) -> void:
is_inside_droppable = false
slot = null
func reset() -> void:
global_position = starting_position
call_deferred("reparent", previous_parent)
So then I reproduced your code in GDScript.
@icon("uid://86fn7y83u6mk")
class_name Card extends Area2D
var is_hovered: bool = false
var is_dragging: bool = false
func _ready() -> void:
mouse_entered.connect(_on_mouse_entered)
mouse_exited.connect(_on_mouse_exited)
func _process(_delta: float) -> void:
if is_hovered:
print("Process.Hover: %s" % name)
if Input.is_action_just_pressed("click"):
is_dragging = true
if is_dragging and Input.is_action_just_released("click"):
is_dragging = false
if is_dragging:
global_position = get_global_mouse_position()
func _on_mouse_entered() -> void:
print("MOUSE_ENTER: %s" % name)
is_hovered = true
func _on_mouse_exited() -> void:
print("MOUSE_EXIT: %s" % name)
is_hovered = false
And I was able to reproduce your bug if I moved the mouse really fast across both cards.
But if I go at a normal speed (and comment out the hover print statement every frame so I can read the output), I get this every time.

I went back and played with my ship part code then, to try and reproduce the problem, and still could not. I think it because the ship part collision shapes are so small that they do not detect fast mouse movements, so I am forced to slow down to get enter and exit signals.
Conclusion
We go back to, I believe you are too deep in the weeds. Specifically, your post appears to be an example of the XY Problem. I went back to read what your problem is, and it is with the current solution which is this:
- You create all the cards in a deck and stack them on top of one another.
- When you click on the deck, you cannot be sure you will get the top card.
- You have dug deep into the engine to solve this problem.
- You have posted asking how the engine works.
You need to back up before step 1 and look at that problem. Which is you want the player to be able to take the top card from the deck.
Here’s my suggestion: Create a deck object that displays the top card. When you click it and move the top card, the deck object shows the next card. Once you place the first card, the second card is spawned in.