Untitled

 avatar
unknown
plain_text
a month ago
5.3 kB
12
Indexable
extends Control
class_name CardsHand

@export var card_scene: PackedScene
@export var fan_max_angle_deg: float = 30.0
@export var fan_radius: float = 260.0
@export var fan_spacing: float = 140.0
@export var bottom_padding: float = 40.0
@export var fly_in_offset_y: float = 300.0
@export var fly_in_duration: float = 0.45
@export var fly_in_stagger: float = 0.06
@export var fly_out_offset: Vector2 = Vector2(900.0, 500.0)
@export var fly_out_duration: float = 0.35
@export var fly_out_stagger: float = 0.04
@export var fly_out_extra_rotation_deg: float = 18.0

var _card_targets: Dictionary = {}
var _suppress_reflow: bool = false

func init() -> void:
	pass

func get_cards_data() -> Array[CardData]:
	var out: Array[CardData] = []
	for child in get_children():
		var card := child as IngameCard
		if card != null:
			out.append(card.card_data)
	return out

func _on_card_tree_exited() -> void:
	if _suppress_reflow:
		return
	call_deferred("reflow_fan")

func reflow_fan() -> void:
	var list: Array[IngameCard] = []
	for c in get_children():
		var ic := c as IngameCard
		if ic != null:
			list.append(ic)
	var count: int = list.size()
	if count == 0:
		_card_targets.clear()
		return

	var pivot: Vector2 = size * 0.5
	var center_index: float = (count - 1) * 0.5
	var max_index_dist: float = max(1.0, center_index)

	for i in count:
		var card_node: IngameCard = list[i]
		var t: float = (float(i) - center_index) / max_index_dist
		var angle_rad: float = deg_to_rad(fan_max_angle_deg) * t
		var x: float = t * fan_spacing * center_index
		var y: float = fan_radius * (1.0 - cos(angle_rad)) + bottom_padding
		card_node.pivot_offset = Vector2(card_node.size.x * 0.5, card_node.size.y)
		var pivot_pos: Vector2 = pivot + Vector2(x, y)
		var pos: Vector2 = pivot_pos - card_node.pivot_offset
		_card_targets[card_node] = {"pos": pos, "rot": angle_rad}
		card_node.set_rest_pose(pos, angle_rad)
		var dnd := card_node.get_node_or_null("DragNDropFeature") as DragNDropFeature
		if dnd != null and not dnd.is_dragging():
			if dnd.is_peeking():
				card_node.z_index = dnd.z_index_peek
			else:
				card_node.z_index = i
		card_node.apply_layout_from_hand(true)

func set_cards(cards: Array[CardData]) -> void:
	_suppress_reflow = true
	for child in get_children():
		remove_child(child)
		child.free()
	_card_targets.clear()
	_suppress_reflow = false

	if cards.is_empty():
		return

	var pivot: Vector2 = size * 0.5
	var count: int = cards.size()
	var center_index: float = (count - 1) * 0.5
	var max_index_dist: float = max(1.0, center_index)

	for i in count:
		var card_node := card_scene.instantiate() as IngameCard
		add_child(card_node)
		card_node.tree_exited.connect(_on_card_tree_exited)
		card_node.init(cards[i])

		var t: float = (float(i) - center_index) / max_index_dist
		var angle_rad: float = deg_to_rad(fan_max_angle_deg) * t
		var x: float = t * fan_spacing * center_index
		var y: float = fan_radius * (1.0 - cos(angle_rad)) + bottom_padding

		card_node.pivot_offset = Vector2(card_node.size.x * 0.5, card_node.size.y)

		var pivot_pos: Vector2 = pivot + Vector2(x, y)
		var target_pos: Vector2 = pivot_pos - card_node.pivot_offset
		_card_targets[card_node] = {
			"pos": target_pos,
			"rot": angle_rad,
		}
		card_node.configure_hand(self, target_pos, angle_rad)
		card_node.z_index = i

		card_node.visible = false
		card_node.position = Vector2(pivot_pos.x, pivot_pos.y + fly_in_offset_y) - card_node.pivot_offset
		card_node.rotation = angle_rad * 0.35

func launch_start_turn_animation() -> void:
	var i: int = 0
	for child in get_children():
		var card := child as IngameCard
		if card == null:
			continue
		if not _card_targets.has(card):
			continue

		var target_dict: Dictionary = _card_targets[card]
		var target_pos: Vector2 = target_dict["pos"]
		var target_rot: float = target_dict["rot"]

		card.visible = true

		var tween := create_tween()
		tween.set_trans(Tween.TRANS_BACK)
		tween.set_ease(Tween.EASE_OUT)
		if i > 0 and fly_in_stagger > 0.0:
			tween.tween_interval(float(i) * fly_in_stagger)
		tween.tween_property(card, "position", target_pos, fly_in_duration)
		tween.parallel().tween_property(card, "rotation", target_rot, fly_in_duration)

		i += 1

func launch_end_turn_animation() -> void:
	var cards: Array[IngameCard] = []
	for child in get_children():
		var card := child as IngameCard
		if card != null:
			cards.append(card)

	if cards.is_empty():
		_card_targets.clear()
		return

	for c in cards:
		if c.tree_exited.is_connected(_on_card_tree_exited):
			c.tree_exited.disconnect(_on_card_tree_exited)

	for idx in cards.size():
		var card := cards[idx]
		card.set_interactive(false)

		var tween := create_tween()
		tween.set_trans(Tween.TRANS_QUAD)
		tween.set_ease(Tween.EASE_IN)
		if idx > 0 and fly_out_stagger > 0.0:
			tween.tween_interval(float(idx) * fly_out_stagger)

		tween.tween_property(card, "position", card.position + fly_out_offset, fly_out_duration)
		tween.parallel().tween_property(
			card,
			"rotation",
			card.rotation + deg_to_rad(fly_out_extra_rotation_deg),
			fly_out_duration
		)
		tween.tween_callback(Callable(card, "queue_free"))

	_card_targets.clear()
Editor is loading...
Leave a Comment