194 lines
5.1 KiB
GDScript
194 lines
5.1 KiB
GDScript
class_name CardVisual
|
|
extends Node3D
|
|
|
|
## CardVisual - 3D visual representation of a card
|
|
|
|
signal clicked(card_visual: CardVisual)
|
|
signal hovered(card_visual: CardVisual)
|
|
signal unhovered(card_visual: CardVisual)
|
|
|
|
# Card dimensions (standard card ratio ~2.5:3.5)
|
|
const CARD_WIDTH: float = 0.63
|
|
const CARD_HEIGHT: float = 0.88
|
|
const CARD_THICKNESS: float = 0.02
|
|
|
|
# Associated card instance
|
|
var card_instance: CardInstance = null
|
|
|
|
# Visual state
|
|
var is_highlighted: bool = false
|
|
var is_selected: bool = false
|
|
var is_hovered: bool = false
|
|
|
|
# Animation
|
|
var target_position: Vector3 = Vector3.ZERO
|
|
var target_rotation: Vector3 = Vector3.ZERO
|
|
var move_speed: float = 10.0
|
|
var is_animating: bool = false
|
|
|
|
# Components
|
|
var mesh_instance: MeshInstance3D
|
|
var collision_shape: CollisionShape3D
|
|
var static_body: StaticBody3D
|
|
var material: StandardMaterial3D
|
|
|
|
# Colors
|
|
var normal_color: Color = Color.WHITE
|
|
var highlight_color: Color = Color(1.2, 1.2, 0.8)
|
|
var selected_color: Color = Color(0.8, 1.2, 0.8)
|
|
var dull_tint: Color = Color(0.7, 0.7, 0.7)
|
|
|
|
func _ready() -> void:
|
|
_create_card_mesh()
|
|
_setup_collision()
|
|
|
|
func _create_card_mesh() -> void:
|
|
mesh_instance = MeshInstance3D.new()
|
|
add_child(mesh_instance)
|
|
|
|
# Create box mesh for card
|
|
var box = BoxMesh.new()
|
|
box.size = Vector3(CARD_WIDTH, CARD_THICKNESS, CARD_HEIGHT)
|
|
mesh_instance.mesh = box
|
|
|
|
# Create material
|
|
material = StandardMaterial3D.new()
|
|
material.albedo_color = Color.WHITE
|
|
mesh_instance.material_override = material
|
|
|
|
func _setup_collision() -> void:
|
|
static_body = StaticBody3D.new()
|
|
add_child(static_body)
|
|
|
|
collision_shape = CollisionShape3D.new()
|
|
var shape = BoxShape3D.new()
|
|
shape.size = Vector3(CARD_WIDTH, CARD_THICKNESS * 2, CARD_HEIGHT)
|
|
collision_shape.shape = shape
|
|
static_body.add_child(collision_shape)
|
|
|
|
# Connect input
|
|
static_body.input_event.connect(_on_input_event)
|
|
static_body.mouse_entered.connect(_on_mouse_entered)
|
|
static_body.mouse_exited.connect(_on_mouse_exited)
|
|
|
|
func _process(delta: float) -> void:
|
|
# Animate position
|
|
if is_animating:
|
|
position = position.lerp(target_position, move_speed * delta)
|
|
rotation = rotation.lerp(target_rotation, move_speed * delta)
|
|
|
|
if position.distance_to(target_position) < 0.01 and rotation.distance_to(target_rotation) < 0.01:
|
|
position = target_position
|
|
rotation = target_rotation
|
|
is_animating = false
|
|
|
|
# Update dull visual
|
|
_update_visual_state()
|
|
|
|
## Initialize with a card instance
|
|
func setup(card: CardInstance) -> void:
|
|
card_instance = card
|
|
_load_card_texture()
|
|
_update_visual_state()
|
|
|
|
## Load the card's texture
|
|
func _load_card_texture() -> void:
|
|
if not card_instance or not card_instance.card_data:
|
|
return
|
|
|
|
var texture = CardDatabase.get_card_texture(card_instance.card_data)
|
|
if texture:
|
|
material.albedo_texture = texture
|
|
else:
|
|
# Use element color as fallback
|
|
var element_color = Enums.element_to_color(card_instance.get_element())
|
|
material.albedo_color = element_color
|
|
|
|
## Update visual state based on card state
|
|
func _update_visual_state() -> void:
|
|
if not material:
|
|
return
|
|
|
|
var color = normal_color
|
|
|
|
if is_selected:
|
|
color = selected_color
|
|
elif is_highlighted:
|
|
color = highlight_color
|
|
|
|
# Apply dull tint
|
|
if card_instance and card_instance.is_dull():
|
|
color = color * dull_tint
|
|
|
|
material.albedo_color = color
|
|
|
|
## Set card as highlighted
|
|
func set_highlighted(highlighted: bool) -> void:
|
|
is_highlighted = highlighted
|
|
_update_visual_state()
|
|
|
|
## Set card as selected
|
|
func set_selected(selected: bool) -> void:
|
|
is_selected = selected
|
|
_update_visual_state()
|
|
|
|
## Move card to position with animation
|
|
func move_to(pos: Vector3, rot: Vector3 = Vector3.ZERO) -> void:
|
|
target_position = pos
|
|
target_rotation = rot
|
|
is_animating = true
|
|
|
|
## Move card instantly
|
|
func set_position_instant(pos: Vector3, rot: Vector3 = Vector3.ZERO) -> void:
|
|
position = pos
|
|
rotation = rot
|
|
target_position = pos
|
|
target_rotation = rot
|
|
is_animating = false
|
|
|
|
## Set dull rotation (90 degrees)
|
|
func set_dull_visual(is_dull: bool) -> void:
|
|
if is_dull:
|
|
target_rotation.y = deg_to_rad(90)
|
|
else:
|
|
target_rotation.y = 0
|
|
is_animating = true
|
|
|
|
## Input handlers
|
|
func _on_input_event(_camera: Node, event: InputEvent, _position: Vector3, _normal: Vector3, _shape_idx: int) -> void:
|
|
if event is InputEventMouseButton:
|
|
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
|
clicked.emit(self)
|
|
|
|
func _on_mouse_entered() -> void:
|
|
is_hovered = true
|
|
hovered.emit(self)
|
|
# Slight raise on hover
|
|
if not is_animating:
|
|
target_position.y = position.y + 0.1
|
|
is_animating = true
|
|
|
|
func _on_mouse_exited() -> void:
|
|
is_hovered = false
|
|
unhovered.emit(self)
|
|
# Return to normal height
|
|
if not is_animating and card_instance:
|
|
target_position.y = position.y - 0.1
|
|
is_animating = true
|
|
|
|
## Get display info for UI
|
|
func get_card_info() -> Dictionary:
|
|
if not card_instance or not card_instance.card_data:
|
|
return {}
|
|
|
|
var data = card_instance.card_data
|
|
return {
|
|
"name": data.name,
|
|
"type": Enums.card_type_to_string(data.type),
|
|
"element": Enums.element_to_string(data.get_primary_element()),
|
|
"cost": data.cost,
|
|
"power": data.power if data.type == Enums.CardType.FORWARD else 0,
|
|
"job": data.job,
|
|
"is_dull": card_instance.is_dull()
|
|
}
|