2025-07-08 15:58:46 +02:00
|
|
|
class_name PlayerHand
|
|
|
|
|
extends Node3D
|
|
|
|
|
|
|
|
|
|
## Distance cards will be apart when there is a small amount of them
|
2025-07-15 09:51:22 +02:00
|
|
|
var card_distance_max: float = 0.18
|
2025-07-08 15:58:46 +02:00
|
|
|
|
|
|
|
|
## Distance cards will be apart when there is a large amount of them
|
2025-07-15 09:51:22 +02:00
|
|
|
var card_distance_min: float = 0.09
|
2025-07-08 15:58:46 +02:00
|
|
|
|
2025-07-16 09:35:32 +02:00
|
|
|
# Card distances for hands of other players
|
|
|
|
|
var _card_distance_max_others: float = 0.03
|
|
|
|
|
var _card_distance_min_others: float = 0.01
|
|
|
|
|
|
|
|
|
|
var _is_owned: bool
|
|
|
|
|
|
2025-07-08 15:58:46 +02:00
|
|
|
## How many test cards to add
|
|
|
|
|
@export var card_scene: PackedScene
|
2025-07-16 09:35:32 +02:00
|
|
|
@export var card_container: Node3D
|
2025-07-08 15:58:46 +02:00
|
|
|
@export_range(0, 20, 0.1, "radians_as_degrees") var card_angle: float
|
|
|
|
|
|
2025-07-16 09:35:32 +02:00
|
|
|
@export var name_label: Label3D
|
2025-07-16 11:15:05 +02:00
|
|
|
@export var rank_label: Label3D
|
2025-07-16 09:35:32 +02:00
|
|
|
@export var hide_client_name: bool = false
|
2025-07-18 14:10:39 +02:00
|
|
|
@export var card_disallowed_label: Label
|
2025-07-16 09:35:32 +02:00
|
|
|
|
2025-07-08 16:48:41 +02:00
|
|
|
var _selected_card: PlayingCard = null
|
2025-07-18 14:10:39 +02:00
|
|
|
var _cards_in_hand_ids: Array[int] = []
|
2025-07-18 14:17:44 +02:00
|
|
|
var _allow_clicking_cards: bool = false
|
2025-07-18 14:10:39 +02:00
|
|
|
|
|
|
|
|
var _trump_id: int = -1
|
|
|
|
|
var _first_played_card_id: int = -1
|
2025-07-08 16:48:41 +02:00
|
|
|
|
2025-07-08 17:07:32 +02:00
|
|
|
signal card_selected(card_id: int)
|
|
|
|
|
signal card_played(card_id: int)
|
|
|
|
|
|
2025-07-16 09:35:32 +02:00
|
|
|
func _ready() -> void:
|
|
|
|
|
if hide_client_name:
|
|
|
|
|
name_label.hide()
|
2025-07-08 15:58:46 +02:00
|
|
|
|
2025-07-16 09:45:44 +02:00
|
|
|
func set_player_name(player_name: String) -> void:
|
|
|
|
|
name_label.text = player_name
|
|
|
|
|
|
2025-07-08 15:58:46 +02:00
|
|
|
func add_card(card: PlayingCard) -> void:
|
2025-07-16 09:35:32 +02:00
|
|
|
card_container.add_child(card)
|
2025-07-08 15:58:46 +02:00
|
|
|
_arrange_cards()
|
|
|
|
|
|
2025-07-16 09:35:32 +02:00
|
|
|
func add_cards(card_ids: Array[int], is_owned: bool) -> void:
|
|
|
|
|
_is_owned = is_owned
|
2025-07-15 09:09:04 +02:00
|
|
|
card_ids.sort()
|
|
|
|
|
for card_id in card_ids:
|
|
|
|
|
var card: PlayingCard = card_scene.instantiate()
|
2025-07-16 09:35:32 +02:00
|
|
|
if is_owned:
|
2025-07-15 09:09:04 +02:00
|
|
|
card.card_clicked.connect(_on_card_clicked)
|
|
|
|
|
card.setup_card_details(card_id)
|
|
|
|
|
add_card(card)
|
2025-07-18 14:10:39 +02:00
|
|
|
_cards_in_hand_ids.append(card_id)
|
2025-07-18 12:57:12 +02:00
|
|
|
if card_ids.size() == 1:
|
|
|
|
|
card.rotate_y(PI)
|
2025-07-15 09:09:04 +02:00
|
|
|
await get_tree().create_timer(0.16).timeout
|
|
|
|
|
|
2025-07-16 11:59:52 +02:00
|
|
|
func remove_card_by_id(card_id: int) -> void:
|
|
|
|
|
for card in card_container.get_children():
|
|
|
|
|
if card.card_details.card_id == card_id:
|
|
|
|
|
remove_card(card)
|
|
|
|
|
return
|
|
|
|
|
|
2025-07-08 15:58:46 +02:00
|
|
|
func remove_card(card: PlayingCard) -> void:
|
2025-07-16 09:35:32 +02:00
|
|
|
card_container.remove_child(card)
|
2025-07-15 14:42:06 +02:00
|
|
|
card.queue_free()
|
2025-07-18 14:10:39 +02:00
|
|
|
_cards_in_hand_ids.erase(card.card_details.card_id)
|
2025-07-08 15:58:46 +02:00
|
|
|
_arrange_cards()
|
|
|
|
|
|
|
|
|
|
func _arrange_cards() -> void:
|
2025-07-16 09:35:32 +02:00
|
|
|
var cards = card_container.get_children()
|
2025-07-08 15:58:46 +02:00
|
|
|
# Only arrange cards if there are any!
|
|
|
|
|
if cards.size() > 0:
|
|
|
|
|
var card_distance: float = _get_card_distance(cards.size() - 1)
|
|
|
|
|
var desired_width: float = (card_distance * (cards.size() - 1))
|
|
|
|
|
var card_position = -desired_width / 2.0
|
|
|
|
|
for card in cards:
|
|
|
|
|
card.move_to(card_position)
|
|
|
|
|
card_position += card_distance
|
|
|
|
|
card.rotation.y = card_angle
|
|
|
|
|
|
|
|
|
|
func _get_card_distance(card_count: int) -> float:
|
2025-07-16 09:35:32 +02:00
|
|
|
if _is_owned:
|
|
|
|
|
return card_distance_min + ((card_distance_max - card_distance_min) * (1.0 - (float(card_count) / 19.0)))
|
|
|
|
|
else:
|
|
|
|
|
return _card_distance_min_others + ((_card_distance_max_others - _card_distance_min_others) * (1.0 - (float(card_count) / 19.0)))
|
2025-07-08 15:58:46 +02:00
|
|
|
|
|
|
|
|
func _on_card_clicked(playing_card: PlayingCard) -> void:
|
2025-07-18 14:17:44 +02:00
|
|
|
if not _allow_clicking_cards:
|
|
|
|
|
return
|
|
|
|
|
|
2025-07-18 14:10:39 +02:00
|
|
|
print("check card is " + str(_first_played_card_id))
|
|
|
|
|
if not _is_card_allowed(
|
|
|
|
|
playing_card.card_details.card_id,
|
|
|
|
|
_first_played_card_id,
|
|
|
|
|
_cards_in_hand_ids,
|
|
|
|
|
):
|
|
|
|
|
_notify_disallowed_card()
|
|
|
|
|
return
|
|
|
|
|
|
2025-07-08 16:48:41 +02:00
|
|
|
if _selected_card:
|
|
|
|
|
_selected_card.unselect()
|
|
|
|
|
if _selected_card == playing_card:
|
|
|
|
|
_selected_card = null
|
|
|
|
|
else:
|
|
|
|
|
_selected_card = playing_card
|
|
|
|
|
playing_card.select()
|
2025-07-08 17:07:32 +02:00
|
|
|
|
|
|
|
|
if _selected_card:
|
|
|
|
|
card_selected.emit(_selected_card.card_details.card_id)
|
|
|
|
|
else:
|
|
|
|
|
card_selected.emit(-1)
|
|
|
|
|
|
|
|
|
|
func _on_play_card_button_pressed() -> void:
|
|
|
|
|
if not _selected_card:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
card_played.emit(_selected_card.card_details.card_id)
|
2025-07-15 14:42:06 +02:00
|
|
|
remove_card(_selected_card)
|
2025-07-08 17:07:32 +02:00
|
|
|
_selected_card = null
|
|
|
|
|
card_selected.emit(-1)
|
2025-07-18 14:17:44 +02:00
|
|
|
_allow_clicking_cards = false
|
|
|
|
|
|
|
|
|
|
func allow_clicking_cards() -> void:
|
|
|
|
|
_allow_clicking_cards = true
|
|
|
|
|
|
|
|
|
|
func disallow_clicking_cards() -> void:
|
|
|
|
|
_allow_clicking_cards = false
|
2025-07-16 11:15:05 +02:00
|
|
|
|
|
|
|
|
func display_rank(rank: int, score: int) -> void:
|
|
|
|
|
rank_label.text = "#{rank} ({pts} pts)".format({
|
|
|
|
|
"rank": rank,
|
|
|
|
|
"pts": score,
|
|
|
|
|
})
|
2025-07-18 14:10:39 +02:00
|
|
|
|
|
|
|
|
func _is_card_allowed(requested_card_id: int, first_played_card_id: int, hand_card_ids: Array[int]) -> bool:
|
|
|
|
|
var card_color: Card.CardColor = CardManager.get_card_color(requested_card_id)
|
|
|
|
|
var first_played_color = CardManager.get_card_color(first_played_card_id)
|
|
|
|
|
|
|
|
|
|
print("card details: req: {r}, play: {p}, hand: {han}".format({
|
|
|
|
|
"r": requested_card_id,
|
|
|
|
|
"p": first_played_card_id,
|
|
|
|
|
"han": hand_card_ids,
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
# no card played yet, everything is allowed
|
|
|
|
|
if first_played_card_id == -1:
|
|
|
|
|
print("no first played")
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
if first_played_color == Card.CardColor.WHITE:
|
|
|
|
|
print("first is white")
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
# card is white, may play
|
|
|
|
|
if card_color == Card.CardColor.WHITE:
|
|
|
|
|
print("yours is white, may play")
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
# requested card is same color as laid card; may play
|
|
|
|
|
if first_played_color == card_color:
|
|
|
|
|
print("colors match")
|
|
|
|
|
return true
|
|
|
|
|
# requested card is of different color
|
|
|
|
|
else:
|
|
|
|
|
var colors_in_hand: Array[Card.CardColor] = []
|
|
|
|
|
for id in hand_card_ids:
|
|
|
|
|
colors_in_hand.append(CardManager.get_card_color(id))
|
|
|
|
|
|
|
|
|
|
# player has played color, must play requested color
|
|
|
|
|
if colors_in_hand.has(first_played_color):
|
|
|
|
|
print("has played color, must not play")
|
|
|
|
|
return false
|
|
|
|
|
# player does not have played color, may play anything
|
|
|
|
|
else:
|
|
|
|
|
print("does not have played color, may play")
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
func _notify_disallowed_card() -> void:
|
|
|
|
|
if card_disallowed_label:
|
|
|
|
|
card_disallowed_label.show()
|
|
|
|
|
await get_tree().create_timer(1.0).timeout
|
|
|
|
|
card_disallowed_label.hide()
|
|
|
|
|
|
|
|
|
|
func _on_web_client_tcp_trump_received(trump_id: int) -> void:
|
|
|
|
|
_trump_id = trump_id
|
|
|
|
|
|
|
|
|
|
func _on_web_client_tcp_received_first_card(card_id: int) -> void:
|
|
|
|
|
_first_played_card_id = card_id
|
|
|
|
|
|
|
|
|
|
func _on_web_client_tcp_reset_first_card() -> void:
|
|
|
|
|
_first_played_card_id = -1
|