OpponentCardSlot hover overlay issues

Godot Version

4.3

Hello everyone. I’m trying to have a hover overlay effect for the Opponent Cards but I can’t seem to implement it.

I’ve already changed the collision masks to 2, the same as the player card slots but to no avail. The mouse is not really detecting the enemy card or card slots when I hover over it at all. Below is my CardManager.gd code that is responsible for this.

extends Node2D

const COLLISION_MASK_CARD = 1
const COLLISION_MASK_CARD_SLOT = 2 # Use the same layer as opponent card slots
const DEFAULT_CARD_MOVE_SPEED = 0.1
const DEFAULT_CARD_SCALE = 0.4
const CARD_BIGGER_SCALE = 0.45
const CARD_SLOT_BIGGER_SCALE = 0.35
const CARD_SMALLER_SCALE = 0.28
const CARD_MOVE_SPEED = 0.2

var screen_size
var card_being_dragged
var is_hovering_on_card
var player_hand_reference
var played_creature_card_this_turn = false
var opponent_hand_reference  # Reference to the opponent's hand
var opponent_creature_slots  # Reference to the opponent's creature slots
var selected_creature
var last_click_time = 0.0  # Time of the last click
var hover_delay: float = 0.1  # Time in seconds
var last_hover_time: float = 0.0
var overlay_card: Sprite2D = null

# Variable to track the last hovered card
var last_hovered_card : Node2D = null  # Keep track of the last hovered card


func connect_signals_for_cards(cards):
	for card in cards:
		#if card.has_method("is_card"):  # Replace "is_card" with a method or property unique to Card
			#card.connect("clicked", self, "card_clicked")
		#else:
			#print("Invalid card object found:", card.name)
		# Check if the card is valid and has the required property
		if card and "card_slot_card_is_in" in card:  # Replace with a property unique to Card
			card.connect("clicked", self, "card_clicked")
		else:
			print("Invalid card object found:", card.name)

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	screen_size = get_viewport_rect().size
	player_hand_reference = $"../PlayerHand"
	opponent_hand_reference = $"../OpponentHand"  # Initialize opponent hand reference
	
	# Use get_node_or_null to check if the node exists
	var opponent_creature_slots = get_node_or_null("../OpponentCreatureSlots")
	opponent_creature_slots = $"../OpponentCreatureSlots"  # Initialize opponent creature slots reference
	$"../InputManager".connect("left_mouse_button_released", on_left_click_released)


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	if card_being_dragged:
		var mouse_pos = get_global_mouse_position()
		card_being_dragged.position = Vector2(clamp(mouse_pos.x, 0, screen_size.x), 
			clamp(mouse_pos.y, 0, screen_size.y))  


func can_place_card(card, slot) -> bool:
	# Ensure the slot is valid and belongs to the player
	if not slot:
		return false  # No slot found
	
	# Check if the slot belongs to the opponent
	if slot.is_in_group("OpponentCardSlot"):
		return false  # Prevent placement in opponent slots
		
	# Ensure the card type matches the slot type
	if card.card_type != slot.card_slot_type:
		return false
	
	# Additional rules for specific card types (e.g., creatures)
	if card.card_type == "Creature" and played_creature_card_this_turn:
		return false
		
	return true  # All checks passed


func place_card(card, slot):
	if not can_place_card(card, slot):  # Use can_place_card to validate
		print("Cannot place card in opponent slot!")
		return  # Exit early if placement is invalid
	
	# Call the appropriate function to handle card placement logic
	card.set_parent(slot)


#func card_clicked(card):
	## Card if card on battlefield or in hand
	#if card.card_slot_card_is_in:
		#if $"../BattleManager".is_opponents_turn:
			#return
		#
		#if card in $"../BattleManager".player_cards_that_attacked_this_turn:
			#return
		#
		## Check if the card was recently summoned and cannot attack yet
		#if card in $"../BattleManager".recently_summoned_creatures:
			#print("This creature was just summoned and cannot attack this turn.")
			#return
		#
		#if card.card_type != "Creature":
			#return
		#
		#if $"../BattleManager".opponent_cards_on_battlefield.size() == 0:
			#$"../BattleManager".direct_attack(card, "Player")
		#else:
			#select_card_for_battle(card)
	#else:
		## Card in hand
		#start_drag(card)
func card_clicked(card):
	print("Card clicked:", card.name)
	print("Card type:", card.get_class())
	
	# Check if the card is valid and has the required property
	#if not card or not card.has_method("get_property_list"):  # Check if the object is valid
		#print("Invalid card object passed to card_clicked()")
		#return
	
	if not "card_slot_card_is_in" in card:  # Check if the card has the required property
		print("Invalid card object: missing 'card_slot_card_is_in' property")
		return
		
	# Card if card on battlefield or in hand
	if card.card_slot_card_is_in:
		if $"../BattleManager".is_opponents_turn:
			return
		
		if card in $"../BattleManager".player_cards_that_attacked_this_turn:
			return
		
		# Check if the card was recently summoned and cannot attack yet
		if card in $"../BattleManager".recently_summoned_creatures:
			print("This creature was just summoned and cannot attack this turn.")
			return
		
		if card.card_type != "Creature":
			return
		
		if $"../BattleManager".opponent_cards_on_battlefield.size() == 0:
			$"../BattleManager".direct_attack(card, "Player")
		else:
			select_card_for_battle(card)
	else:
		# Card in hand
		start_drag(card)


func select_card_for_battle(card):
	# Toggle selected card
	if selected_creature:
		# If card already selected
		if selected_creature == card:
			card.position.y += 20
			selected_creature = null
		else:
			selected_creature.position.y +=20
			selected_creature = card
			card.position.y -= 20
	else:
		selected_creature = card
		card.position.y -= 20


func start_drag(card):
	card_being_dragged = card
	card.scale = Vector2(DEFAULT_CARD_SCALE, DEFAULT_CARD_SCALE)


#func finish_drag():
	#card_being_dragged.scale = Vector2(CARD_BIGGER_SCALE, CARD_BIGGER_SCALE) 
	#var card_slot_found = raycast_check_for_card_slot()
	#if card_slot_found and not card_slot_found.card_in_slot:
		## Card dropped in empty slot
		#if card_being_dragged.card_type == card_slot_found.card_slot_type:
			## Card dropped in correct type of slot
			#if card_being_dragged.card_type == "Creature" && played_creature_card_this_turn:
				#player_hand_reference.add_card_to_hand(card_being_dragged, DEFAULT_CARD_MOVE_SPEED)
				#card_being_dragged = null
				#return
				#
			## Card placed in slot
			#card_being_dragged.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
			#card_being_dragged.z_index = -1
			#is_hovering_on_card = false
			#card_being_dragged.card_slot_card_is_in = card_slot_found
			#player_hand_reference.remove_card_from_hand(card_being_dragged)
				#
			## Player Card Offset
			#var offset = Vector2(14, -16) #Fine-tune to fix misalignment for player
			#card_being_dragged.position = card_slot_found.position + offset
			#card_slot_found.card_in_slot = true
			#card_slot_found.get_node("Area2D/CollisionShape2D").disabled = true
			#
			#if card_being_dragged.card_type == "Creature":
				#$"../BattleManager".player_cards_on_battlefield.append(card_being_dragged)
				#played_creature_card_this_turn = true
				#$"../BattleManager".recently_summoned_creatures.append(card_being_dragged)
			#
			#if card_being_dragged.ability_script:
				#card_being_dragged.ability_script.trigger_ability($"../BattleManager", card_being_dragged, $"../InputManager", "card_placed")
				#
				#
			#card_being_dragged = null
			#return
			#
	## If not dropped in a slot, put it back in the hand
	#player_hand_reference.add_card_to_hand(card_being_dragged, DEFAULT_CARD_MOVE_SPEED)
	#card_being_dragged = null
#func finish_drag():
	## Reset card scale when drag finishes
	#card_being_dragged.scale = Vector2(CARD_BIGGER_SCALE, CARD_BIGGER_SCALE) 
	#
	## Check if the card is hovering over a valid card slot
	#var card_slot_found = raycast_check_for_card_slot()
	#if card_slot_found and not card_slot_found.card_in_slot:
		## Validate placement using can_place_card
		#if can_place_card(card_being_dragged, card_slot_found):
			#if card_being_dragged.card_type == "Creature" and played_creature_card_this_turn:
				## Prevent multiple creature cards being played per turn
				#player_hand_reference.add_card_to_hand(card_being_dragged, DEFAULT_CARD_MOVE_SPEED)
				#card_being_dragged = null
				#return
			#
			## Card placed in slot
			#card_being_dragged.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
			#card_being_dragged.z_index = -1
			#is_hovering_on_card = false
			#card_being_dragged.card_slot_card_is_in = card_slot_found
			#player_hand_reference.remove_card_from_hand(card_being_dragged)
			#
			## Adjust position to align properly in the slot
			#var offset = Vector2(14, -16)  # Fine-tune to fix misalignment for player cards
			#card_being_dragged.position = card_slot_found.position + offset
			#card_slot_found.card_in_slot = true
			#card_slot_found.get_node("Area2D/CollisionShape2D").disabled = true
			#
			## Handle creature-specific logic
			#if card_being_dragged.card_type == "Creature":
				#$"../BattleManager".player_cards_on_battlefield.append(card_being_dragged)
				#played_creature_card_this_turn = true
				#$"../BattleManager".recently_summoned_creatures.append(card_being_dragged)
			#
			## Trigger ability if the card has one
			#if card_being_dragged.ability_script:
				#card_being_dragged.ability_script.trigger_ability($"../BattleManager", card_being_dragged, $"../InputManager", "card_placed")
				#
			## Clear the dragged card reference
			#card_being_dragged = null
			#return
			#
	## If not dropped in a valid slot, return it to the player's hand
	#player_hand_reference.add_card_to_hand(card_being_dragged, DEFAULT_CARD_MOVE_SPEED)
	#card_being_dragged = null
func finish_drag():
	card_being_dragged.scale = Vector2(CARD_BIGGER_SCALE, CARD_BIGGER_SCALE) 
	var card_slot_found = raycast_check_for_card_slot()
	
	if card_slot_found and not card_slot_found.card_in_slot:
		if can_place_card(card_being_dragged, card_slot_found):
			# Update position and alignment
			card_being_dragged.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
			card_being_dragged.z_index = -1
			card_being_dragged.card_slot_card_is_in = card_slot_found
			player_hand_reference.remove_card_from_hand(card_being_dragged)
			
			card_being_dragged.position = card_slot_found.position + Vector2(14, -16)
			card_slot_found.card_in_slot = true
			card_slot_found.get_node("Area2D/CollisionShape2D").disabled = true
			
			# Add to battlefield or appropriate area
			if card_being_dragged.card_type == "Creature":
				$"../BattleManager".player_cards_on_battlefield.append(card_being_dragged)
				played_creature_card_this_turn = true
				$"../BattleManager".recently_summoned_creatures.append(card_being_dragged)
			elif card_being_dragged.card_type in ["Spell", "Trap"]:
				$"../BattleManager".handle_special_card(card_being_dragged)
				
			# Trigger card ability
			if card_being_dragged.ability_script:
				card_being_dragged.ability_script.trigger_ability($"../BattleManager", card_being_dragged, $"../InputManager", "card_placed")
				
			card_being_dragged = null
			return
			
	# Return card to hand if not placed
	player_hand_reference.add_card_to_hand(card_being_dragged, DEFAULT_CARD_MOVE_SPEED)
	card_being_dragged = null


# Opponent Card Logic
#func place_opponent_card_in_slot(card, card_slot_found):
	#if card_slot_found and not card_slot_found.card_in_slot:
		#if card.card_type == card_slot_found.card_slot_type:
			#card.position = card_slot_found.position
			#card.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
			#card.z_index = -1
			#
			## Ensure opponent cards connect to hover signals
			#connect_card_signals(card)
			#
			#card_slot_found.card_in_slot = true
			#return true
		#else:
			#card.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
			#card.z_index = -1
			#
			#var opponent_offset = Vector2(0, 0)
			#var initial_position = card_slot_found.global_position + opponent_offset
			#
			#card.global_position = initial_position
			#card_slot_found.card_in_slot = true
			#
			#return true  # Successfully placed card
	#return false
func place_opponent_card_in_slot(card, card_slot_found):
	if card_slot_found and not card_slot_found.card_in_slot:
		if card.card_type == card_slot_found.card_slot_type:
			card.position = card_slot_found.position
			card.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
			card.z_index = -1
			card_slot_found.card_in_slot = true
			
			# Connect hover signals for opponent cards
			connect_card_signals(card, true)  # Pass true to indicate it's an opponent card
			
			# Handle opponent-specific logic
			$"../BattleManager".opponent_cards_on_battlefield.append(card)
			return true
		else:
			card.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
			card.z_index = -1
			
			var opponent_offset = Vector2(0, 0)
			var initial_position = card_slot_found.global_position + opponent_offset
			
			card.global_position = initial_position
			card_slot_found.card_in_slot = true
			
			return true  # Successfully placed card
	return false


func unselect_selected_creature():
	if selected_creature:
		selected_creature.position.y += 20
		selected_creature = null


func connect_card_signals(card, is_opponent_card = false):
	card.connect("hovered", on_hovered_over_card)
	card.connect("hovered_off", on_hovered_off_card)
	
	if is_opponent_card:
		card.connect("hovered", on_hovered_over_opponent_card)
		card.connect("hovered_off", on_hovered_off_opponent_card)


func on_hovered_over_opponent_card(card):
	var parent_card = get_node_or_null("..")  # Adjust path if necessary
	if not parent_card:
		return
		
	if not is_hovering_on_card:
		is_hovering_on_card = true
		highlight_card(card, true, true)  # Ensure highlight_card handles the flag correctly
		print("Hovering over opponent's card: ", card.name)


func on_hovered_off_opponent_card(card):
	# Reset highlight and overlay when hover ends
	if not card.defeated and !card_being_dragged:
		highlight_card(card, false, true)  # Ensure highlight_card handles the flag correctly
		var new_card_hovered = raycast_check_for_card()
		if new_card_hovered:
			highlight_card(new_card_hovered, true, true)
		else:
			is_hovering_on_card = false
	print("Stopped hovering over opponent card:", card.name)


func on_left_click_released():
	if card_being_dragged:
		finish_drag()

# Function to handle hover over card
func on_hovered_over_card(card):
	print("Hovered over card: ", card.name)  # For debugging
	if not is_hovering_on_card:
		is_hovering_on_card = true
		
		# Check if the card is in an opponent slot
		var opponent_slot = raycast_check_for_opponent_card_slot()
		var opponent_card = raycast_check_for_opponent_card_slot()
		#if opponent_slot and opponent_slot.card_in_slot:
			#print("Hovering over opponent card in slot:", opponent_slot.name)
			#highlight_card(card, true, true)  # Pass true to indicate it's an opponent card
		#else:
			#highlight_card(card, true)  # Regular player card hover
		if opponent_card:
			print("Hovering over opponent card:", opponent_card.name)
			highlight_card(opponent_card, true, true)  # Pass true to indicate it's an opponent card
		else:
			print("Hovering over player card:", card.name)
			highlight_card(card, true)  # Regular player card hover


# Function to handle hover off card
func on_hovered_off_card(card):
	# Reset highlight and overlay when hover ends
	if not card.defeated and !card_being_dragged:
		highlight_card(card, false)
		
		# Check if there's another card under the mouse
		var new_card_hovered = raycast_check_for_card()
		if new_card_hovered:
			highlight_card(new_card_hovered, true)
		else:
			is_hovering_on_card = false


func on_card_placed_in_slot(card):
	# Reset the card appearance immediately after placement
	highlight_card(card, false)
	
	# Ensure no overlay or hover persists
	if $OverlayCard:
		$OverlayCard.queue_free()
	
	last_hovered_card = null
	is_hovering_on_card = false


func on_card_interacted(card):
	if card.has_meta("is_overlay"):
		# Ignore interactions with overlays
		return


func create_or_update_overlay(card):
	var overlay_card: Sprite2D
	if not has_node("OverlayCard"):
		# Create a new overlay card if it doesn't exist
		overlay_card = Sprite2D.new()
		overlay_card.name = "OverlayCard"
		overlay_card.scale = Vector2(0.5, 0.5)
		overlay_card.position = Vector2(1600, 500)  # Position the overlay card
		overlay_card.z_index = 100  # Ensure it's rendered above other elements
		add_child(overlay_card)
	else:
		# Get the existing overlay card
		overlay_card = get_node("OverlayCard")
	
	# Ensure we handle both player and opponent cards
	if card.has_node("CardImage"):
		# Update the overlay texture for the new card being hovered over
		overlay_card.texture = card.get_node("CardImage").texture
#func create_or_update_overlay(card):
	#if card and card.has_method("get_card_slot_card_is_in"):
		#if card.card_slot_card_is_in:
			## Proceed with logic to create or update the overlay
			#var overlay_card: Sprite2D
			#if not has_node("OverlayCard"):
				#overlay_card = Sprite2D.new()
				#overlay_card.name = "OverlayCard"
				#overlay_card.scale = Vector2(0.5, 0.5)
				#overlay_card.position = Vector2(1600, 500)  # Position the overlay card
				#overlay_card.z_index = 100  # Ensure it's rendered above other elements
				#add_child(overlay_card)
			#else:
				#overlay_card = get_node("OverlayCard")
			#
			#if card.has_node("CardImage"):
				#overlay_card.texture = card.get_node("CardImage").texture
		#else:
			#print("Invalid 'card_slot_card_is_in' value:", card.card_slot_card_is_in)
	#else:
		#print("Invalid card or missing property 'card_slot_card_is_in':", card)


#func highlight_card(card, hovered, is_opponent_card = false):
	#if not card:
		#return
	#
	#if hovered:
		## Card is hovered
		#if card.card_slot_card_is_in:
			#card.scale = Vector2(CARD_SLOT_BIGGER_SCALE, CARD_SLOT_BIGGER_SCALE)
			#card.z_index = 1
		#else:
			#card.scale = Vector2(CARD_BIGGER_SCALE, CARD_BIGGER_SCALE)
			#
		## Create or update overlay
		#create_or_update_overlay(card)
	#else:
		## Card is no longer hovered
		#if card.card_slot_card_is_in:
			## For cards in slots
			#card.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
			#card.z_index = -1  # Reset z-index
		#else:
			## For cards in hand
			#card.scale = Vector2(DEFAULT_CARD_SCALE, DEFAULT_CARD_SCALE)
		## Remove the overlay if it exists
		#if $OverlayCard:
			#$OverlayCard.queue_free()
	#
	## Additional logic for opponent cards
	#if is_opponent_card:
		#if hovered:
			## Apply opponent-specific highlight or logic if necessary
			#print("Hovering over an opponent card in a slot.")
		#else:
			#print("No longer hovering over an opponent card.")
func highlight_card(card, hovered, is_opponent_card = false):
	if not card:
		return
		
	if hovered:
		if is_opponent_card:
			# Specific logic for opponent cards
			card.scale = Vector2(CARD_SLOT_BIGGER_SCALE, CARD_SLOT_BIGGER_SCALE)
			card.z_index = 1
			print("Opponent card hovered: ", card.name)
		else:
			# Logic for player cards
			if card.card_slot_card_is_in:
				card.scale = Vector2(CARD_SLOT_BIGGER_SCALE, CARD_SLOT_BIGGER_SCALE)
				card.z_index = 1
			else:
				card.scale = Vector2(CARD_BIGGER_SCALE, CARD_BIGGER_SCALE)
		
		# Create or update overlay
		create_or_update_overlay(card)
	else:
		# Reset card scale and remove overlay
		if is_opponent_card:
			card.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
			card.z_index = -1
			print("No longer hovering over opponent card: ", card.name)
		else:
			if card.card_slot_card_is_in:
				card.scale = Vector2(CARD_SMALLER_SCALE, CARD_SMALLER_SCALE)
				card.z_index = -1
			else:
				card.scale = Vector2(DEFAULT_CARD_SCALE, DEFAULT_CARD_SCALE)
		
		if $OverlayCard:
			$OverlayCard.queue_free()


func raycast_check_for_card_slot():
	var space_state = get_world_2d().direct_space_state
	var parameters = PhysicsPointQueryParameters2D.new()
	parameters.position = get_global_mouse_position()
	parameters.collide_with_areas = true
	parameters.collision_mask = COLLISION_MASK_CARD_SLOT
	var result = space_state.intersect_point(parameters)
	if result.size() > 0:
		return result[0].collider.get_parent()
	return null


func raycast_check_for_opponent_card_slot():
	var space_state = get_world_2d().direct_space_state
	var parameters = PhysicsPointQueryParameters2D.new()
	parameters.position = get_global_mouse_position()
	parameters.collide_with_areas = true
	parameters.collision_mask = COLLISION_MASK_CARD_SLOT  # Use the correct collision mask
	
	var result = space_state.intersect_point(parameters)
	if result.size() > 0:
		print("Raycast detected objects:", result)  # Debug print
		for item in result:
			var collider = item.collider
			print("Collider detected:", collider.name)  # Debug print
			if collider and collider.is_in_group("OpponentCardslot"):
				print("Opponent card slot detected:", collider.name)
				# Return the card inside the slot, not the slot itself
				var card = collider.get_parent().get_node("Card")  # Assuming collider is the Area2D child of CreatureCardSlot
				if card:
					print("Card found in slot:", card.name)
					return card
	else:
		print("Raycast detected no objects.")  # Debug print
	return null


func raycast_check_for_opponent_card():
	var space_state = get_world_2d().direct_space_state
	var parameters = PhysicsPointQueryParameters2D.new()
	parameters.position = get_global_mouse_position()
	parameters.collide_with_areas = true
	parameters.collision_mask = COLLISION_MASK_CARD  # Ensure opponent cards and slots use this mask
	
	var result = space_state.intersect_point(parameters)
	if result.size() > 0:
		# Check for the group if necessary
		for item in result:
			var collider = item.collider
			if collider and collider.is_in_group("OpponentCardslot"):
				return collider  # Return the opponent card or slot
		#return get_card_with_highest_z_index(result)  # Fallback for cards
	else:
		print("Raycast detected no objects.")  # Debug print
	return null


func raycast_check_for_card():
	var space_state = get_world_2d().direct_space_state
	var parameters = PhysicsPointQueryParameters2D.new()
	parameters.position = get_global_mouse_position()
	parameters.collide_with_areas = true
	parameters.collision_mask = COLLISION_MASK_CARD
	var result = space_state.intersect_point(parameters)
	if result.size() > 0:
		return get_card_with_highest_z_index(result)
	return null
	
	
func get_card_with_highest_z_index(cards):
	# Assume the first card in cards array has the highest z index
	var highest_z_card = cards[0].collider.get_parent()
	var highest_z_index = highest_z_card.z_index
	print("Initial highest z-index:", highest_z_index)  # Debug print
	
	# Loop through the rest of the cards checking for a higher z index
	for i in range(1, cards.size()):
		var current_card = cards[i].collider.get_parent()
		print("Current card z-index:", current_card.z_index)  # Debug print
		if current_card.z_index > highest_z_index:
			highest_z_card = current_card
			highest_z_index = current_card.z_index
	return highest_z_card

func reset_played_creature():
	played_creature_card_this_turn = false

Please update your post to use preformatted text for code snippets with ```. Otherwise this is unreadable.

2 Likes

I’m sorry. I edited it now. Hope this is better.