extends Node3D var _camera: Camera3D var _light: DirectionalLight3D var _table: MeshInstance3D var _cards: Array[Node3D] = [] var _flipping := false func _ready() -> void: _camera = Camera3D.new() _camera.position = Vector3(0, 9, 5.5) _camera.look_at(Vector3(0, 0, -0.5), Vector3.UP) _camera.current = true add_child(_camera) _light = DirectionalLight3D.new() _light.position = Vector3(2, 8, 2) _light.look_at(Vector3.ZERO, Vector3.UP) _light.light_color = Color("ffe8cc") _light.shadow_enabled = true add_child(_light) var ambient := WorldEnvironment.new() var env := Environment.new() env.ambient_light_color = Color("667799") env.ambient_light_energy = 0.3 env.background_mode = Environment.BG_COLOR env.background_color = Color("0e0e1a") ambient.environment = env add_child(ambient) _table = _make_table() add_child(_table) var card_data := [ {"pos": Vector3(-1.6, 0.05, 0), "ranks": [7, 6, 2, 7], "color": Color("4a90d9"), "name": "Ifrit"}, {"pos": Vector3(-0.5, 0.05, 0), "ranks": [5, 3, 8, 4], "color": Color("d94a4a"), "name": "Tonberry"}, {"pos": Vector3(0.5, 0.05, 0), "ranks": [9, 4, 7, 3], "color": Color("4a90d9"), "name": "Shiva"}, {"pos": Vector3(1.6, 0.05, 0), "ranks": [3, 8, 5, 6], "color": Color("d94a4a"), "name": "Odin"}, ] for d in card_data: var card := _make_card(d.ranks, d.color, d.name) card.position = d.pos add_child(card) _cards.append(card) var board_positions: Array[Vector3] = [] var bx := -1.1 var by := 0.05 for row in 3: for col in 3: board_positions.append(Vector3(bx + col * 1.1, by, -1.8 + row * 1.5)) for i in board_positions.size(): var slot := _make_slot() slot.position = board_positions[i] slot.set_meta("board_idx", i) add_child(slot) _instructions_label() func _make_table() -> MeshInstance3D: var mesh := BoxMesh.new() mesh.size = Vector3(12, 0.2, 8) var mat := StandardMaterial3D.new() mat.albedo_color = Color("2a5a2a") mat.roughness = 0.85 var mi := MeshInstance3D.new() mi.mesh = mesh mi.material_override = mat mi.position.y = -0.15 mi.create_trimesh_collision() return mi func _make_slot() -> MeshInstance3D: var mesh := PlaneMesh.new() mesh.size = Vector2(0.95, 1.15) var mat := StandardMaterial3D.new() mat.albedo_color = Color("1a3a1a") mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA mat.albedo_color.a = 0.6 var mi := MeshInstance3D.new() mi.mesh = mesh mi.material_override = mat mi.rotation.x = -PI / 2 return mi func _make_card(ranks: Array, card_color: Color, card_name: String) -> Node3D: var root := Node3D.new() var mesh := BoxMesh.new() mesh.size = Vector3(0.85, 0.02, 1.1) var front_mat := StandardMaterial3D.new() front_mat.albedo_color = card_color front_mat.roughness = 0.5 var mi := MeshInstance3D.new() mi.mesh = mesh mi.material_override = front_mat mi.rotation.x = -PI / 2 root.add_child(mi) var col := CollisionShape3D.new() var shape := BoxShape3D.new() shape.size = Vector3(0.85, 0.1, 1.1) col.shape = shape col.rotation.x = -PI / 2 var body := StaticBody3D.new() body.collision_layer = 2 body.add_child(col) root.add_child(body) var edge_mat := StandardMaterial3D.new() edge_mat.albedo_color = Color("222233") edge_mat.roughness = 0.8 var positions := [ Vector3(0, 0.005, -0.42), Vector3(0.35, 0.005, 0), Vector3(0, 0.005, 0.38), Vector3(-0.35, 0.005, 0), ] for i in 4: var label := Label3D.new() var v: int = ranks[i] label.text = "A" if v >= 10 else str(v) label.font_size = 22 label.outline_size = 4 label.modulate = Color.WHITE label.billboard = BaseMaterial3D.BILLBOARD_ENABLED label.position = positions[i] root.add_child(label) var name_label := Label3D.new() name_label.text = card_name name_label.font_size = 11 name_label.outline_size = 3 name_label.modulate = Color(1, 1, 1, 0.8) name_label.billboard = BaseMaterial3D.BILLBOARD_ENABLED name_label.position = Vector3(0, 0.005, 0.15) name_label.pixel_size = 0.003 root.add_child(name_label) root.set_meta("card_color", card_color) return root func _instructions_label() -> void: var label := Label3D.new() label.text = "Click a card to flip it | R to reset" label.font_size = 18 label.outline_size = 4 label.modulate = Color("f0c040") label.billboard = BaseMaterial3D.BILLBOARD_ENABLED label.position = Vector3(0, 0.5, -4) add_child(label) func _input(event: InputEvent) -> void: if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: _pick_card(event) if event is InputEventKey and event.pressed and event.keycode == KEY_R: for card in _cards: card.rotation = Vector3.ZERO card.scale = Vector3.ONE func _pick_card(event: InputEventMouseButton) -> void: if _flipping: return var from := _camera.project_ray_origin(event.position) var dir := _camera.project_ray_normal(event.position) var space_state := get_world_3d().direct_space_state var query := PhysicsRayQueryParameters3D.create(from, from + dir * 20) query.collision_mask = 0xFFFFFFFF var result := space_state.intersect_ray(query) if result.is_empty(): return var hit: Object = result.collider for card in _cards: for child in card.get_children(): if child is StaticBody3D and child == hit: _flip_card(card) return func _flip_card(card: Node3D) -> void: _flipping = true var tween := create_tween() tween.set_parallel(true) tween.tween_property(card, "rotation:y", PI, 0.3).set_ease(Tween.EASE_IN) tween.tween_property(card, "position:y", 0.4, 0.15).set_trans(Tween.TRANS_SINE) tween.chain() tween.tween_property(card, "rotation:y", PI * 2, 0.3).set_ease(Tween.EASE_OUT) tween.tween_property(card, "position:y", 0.05, 0.15).set_trans(Tween.TRANS_SINE) tween.chain() tween.tween_callback(func(): _flipping = false)