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