Compare commits

20 Commits

Author SHA1 Message Date
3c02ae63f9 organizes card scene, exports tween functionality to its own node and script 2025-04-27 22:49:45 -04:00
ce0bc104ff modifications to the card scene to make it sort of work with its new nodes 2025-04-27 22:02:36 -04:00
7cddb502b4 wip: reworking cards to use sprites and fixing up some stuff related to them 2025-04-27 20:05:28 -04:00
d1ef1d6f4f fixes tapping while dragging issue 2025-04-27 19:02:18 -04:00
fc3a31d144 converts file = null to file.close() 2025-04-27 17:46:06 -04:00
1d9c3fc473 fixes caching not working properly 2025-04-27 17:27:39 -04:00
5937b4db69 cleans up some code 2025-04-27 13:14:56 -04:00
9217834726 fixes tapped cards having a weird drag offset 2025-04-27 13:08:15 -04:00
c463bf0adc another print 2025-04-27 13:03:49 -04:00
98357b5dec removes prints 2025-04-27 13:00:53 -04:00
1beb89ea84 adds tapping/untapping 2025-04-27 12:55:09 -04:00
dd73e1b477 removes print statements 2025-04-27 12:23:40 -04:00
9bb3308487 guts the field code and allows for card dragging 2025-04-27 12:22:39 -04:00
90912c062f deck list importing (mtgo) is now working 2025-04-24 20:55:09 -04:00
b82f5e4c19 adds some documentation 2025-04-24 18:43:26 -04:00
fe38fc49bf completely redoes the caching api 2025-04-24 18:25:28 -04:00
a40ccab3c8 deck input changes 2025-04-24 13:20:57 -04:00
f5edba7402 some small fixes, mtgo is done 2025-04-24 11:45:29 -04:00
b9a07a8c47 adds basic ui template 2025-04-24 01:47:24 -04:00
0f88bd8f7b uses lanczos interpolation to make small cards not look terrible 2025-04-23 22:27:04 -04:00
23 changed files with 473 additions and 96 deletions

View File

@ -1,88 +1,206 @@
extends Node
var _jpg_path
var _png_path
var _req_headers: PackedStringArray
signal cache_done
var _bulk_data: Array
signal fetch_done
var _emitted_done = 0
signal fetch_start
var _emitted_start = 0
var _consts = preload("res://data/consts.gd")
func _all_downloads_done() -> bool:
return _emitted_done == _emitted_start
func _setup_cache_in_mem():
var file = FileAccess.open("user://bulk.json", FileAccess.READ)
_bulk_data = JSON.parse_string(file.get_as_text())
file.close()
func setup() -> Error:
if !FileAccess.file_exists("user://bulk.json"):
get_bulk_data(false)
push_error("Bulk Data was not downloaded! Downloading now!")
return FAILED
if !_all_downloads_done():
push_error("Not done downloading Bulk Data.")
return FAILED
_setup_cache_in_mem()
return OK
func _init() -> void:
_req_headers = PackedStringArray(
["User-Agent: " + _consts.APP_NAME + "/" + _consts.APP_VERSION, "Accept: */*"]
)
fetch_done.connect(_on_end_emit)
fetch_start.connect(_on_start_emit)
func _on_start_emit() -> void:
_emitted_start += 1
func _on_end_emit() -> void:
_emitted_done += 1
func has_emitted_all() -> bool:
return _emitted_start == _emitted_done
func _cache_error(err: String) -> String:
return "CACHE::ERROR::" + err + "\n"
func _check_cache(id: String) -> bool:
if !FileAccess.file_exists("user://card_cache/" + id + "/card.json"):
return false
if !FileAccess.file_exists("user://card_cache/" + id + "/card.png"):
return false
if !FileAccess.file_exists("user://card_cache/" + id + "/card.jpg"):
return false
return true
func _get_dict_from_file(filepath: String) -> Dictionary:
var file = FileAccess.open(filepath, FileAccess.READ)
var data = JSON.parse_string(file.get_as_text())
return data
func fetch_card(id: String) -> void:
if _check_cache(id):
return
await _do_http_request_card(id)
OS.delay_msec(100)
await _do_http_request_imgs(id, _png_path, true)
OS.delay_msec(100)
await _do_http_request_imgs(id, _jpg_path, false)
cache_done.emit()
## get_card_data_from_name
##
## _name: String [br]
## A wrapper for searching for a card by name. Use **get_card_data_from_id** where possible, as it avoids an expensive search for the new card, if the card has been cached already.
func get_card_data_from_name(_name: String) -> Dictionary:
return _get_card_data_from_bulk("name", _name)
func _do_http_request_imgs(id: String, image_path: String, png: bool) -> void:
## get_card_data_from_id
##
## id: String [br]
## This is the preferred wrapper to use when fetching card data, it checks the cache for preexisting data and uses that if it's available. Otherwise, it will search the bulk json for the data.
func get_card_data_from_id(id: String) -> Dictionary:
if FileAccess.file_exists("user://card_cache/" + id + "/card.json"):
return _get_dict_from_file("user://card_cache/" + id + "/card.json")
return _get_card_data_from_bulk("id", id)
func _get_card_data_from_bulk(field: String, search_query: String) -> Dictionary:
var selected_entry = null
for entry in _bulk_data:
if entry[field] == search_query:
selected_entry = entry
break
continue
if selected_entry == null:
return {}
if selected_entry["image_status"] != "missing":
_fetch_card_img(selected_entry)
var dir = DirAccess.open("user://")
dir.make_dir_recursive("user://card_cache/" + selected_entry["id"] + "/")
dir = null
var file = FileAccess.open(
"user://card_cache/" + selected_entry["id"] + "/card.json", FileAccess.WRITE
)
file.store_line(JSON.stringify(selected_entry, "\t"))
file.close()
print("Card: " + selected_entry["name"] + "(" + selected_entry["id"] + ") found, and cached.")
return selected_entry
func _fetch_card_img(data: Dictionary) -> Error:
fetch_start.emit()
if FileAccess.file_exists("user://card_cache/" + data["id"] + "card.png"):
return OK
var httpr = HTTPRequest.new()
add_child(httpr)
var headers = PackedStringArray(["User-Agent: MTGUntapClone/0.1", "Accept: */*"])
var error = httpr.request(image_path, headers)
if error != OK:
push_error(_cache_error("GET_REQUEST") + "An error occurred in the Scryfall request.")
var response = await httpr.request_completed
var err = httpr.request((data["image_uris"])["png"], _req_headers)
if err != OK:
push_error(_cache_error("GET_REQUEST") + "An error occured in the Scryfall request.")
return FAILED
var resp = await httpr.request_completed
var img = Image.new()
var imgerr
if png:
imgerr = img.load_png_from_buffer(response[3])
else:
imgerr = img.load_jpg_from_buffer(response[3])
if imgerr != OK:
err = img.load_png_from_buffer(resp[3])
if err != OK:
push_error(_cache_error("IMG_LOADING") + "Couldn't load the image.")
return FAILED
img.save_png("user://card_cache/" + id + ("/card.png" if png else "/card.jpg"))
var dir = DirAccess.open("user://")
dir.make_dir_recursive("user://card_cache/" + data["id"] + "/")
dir = null
img.save_png("user://card_cache/" + data["id"] + "/card.png")
img = null
fetch_done.emit()
return OK
func get_bulk_data(force: bool) -> Error:
if FileAccess.file_exists("user://bulk.json"):
if force:
DirAccess.remove_absolute("user://bulk.json")
else:
return OK
func _do_http_request_card(id: String) -> void:
var httpr = HTTPRequest.new()
add_child(httpr)
#httpr.request_completed.connect(_scryfall_card_response)
var headers = PackedStringArray(["User-Agent: MTGUntapClone/0.1", "Accept: */*"])
var error = httpr.request("https://api.scryfall.com/cards/" + id, headers)
var error = httpr.request("https://api.scryfall.com/bulk-data/unique-artwork", _req_headers)
if error != OK:
push_error(_cache_error("GET_REQUEST") + "An error occurred in the Scryfall request.")
return FAILED
var response = await httpr.request_completed
if response[0] != HTTPRequest.RESULT_SUCCESS:
push_error(_cache_error("GET_REQUEST") + "Failed to fetch card data from Scryfall")
return
return FAILED
var unprocessed_body = response[3].get_string_from_utf8()
var card_content = JSON.parse_string(unprocessed_body)
if card_content == null:
push_error(_cache_error("PARSING") + "Failed to parse the Scryfall card results.")
return FAILED
var dir = DirAccess.open("user://")
dir.make_dir_recursive("user://card_cache/" + id + "/") # lets ensure the path is there
dir = null
error = httpr.request(card_content["download_uri"], _req_headers)
if error != OK:
push_error(_cache_error("GET_REQUEST") + "An error occurred in the Scryfall request.")
return FAILED
var card_cache = FileAccess.open("user://card_cache/" + id + "/card.json", FileAccess.WRITE)
card_cache.store_string(unprocessed_body) # cache the json response
card_cache = null # closes the file
response = await httpr.request_completed
if response[0] != HTTPRequest.RESULT_SUCCESS:
push_error(_cache_error("GET_REQUEST") + "Failed to fetch card data from Scryfall")
return FAILED
var image_uris = card_content["image_uris"]
_png_path = image_uris["png"]
_jpg_path = image_uris["normal"]
unprocessed_body = response[3].get_string_from_utf8()
card_content = JSON.parse_string(unprocessed_body)
if card_content == null:
push_error(_cache_error("PARSING") + "Failed to parse the Scryfall card results.")
return FAILED
var data_cache = FileAccess.open("user://bulk.json", FileAccess.WRITE)
data_cache.store_string(unprocessed_body)
data_cache.close()
fetch_done.emit()
return OK
func _notification(what):
if what == NOTIFICATION_PREDELETE:
if !_all_downloads_done():
push_error(
"ERR::MEM::CACHE\nCache being deleted before all threads have finished processing!"
)

View File

@ -1,12 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://cah3mvdnom1xg"]
[ext_resource type="Script" uid="uid://b3yqd1qu7dyq" path="res://card.gd" id="1_kikvd"]
[node name="Card" type="TextureRect"]
offset_left = 794.0
offset_top = 79.0
offset_right = 919.0
offset_bottom = 254.0
expand_mode = 5
stretch_mode = 4
script = ExtResource("1_kikvd")

2
data/consts.gd Normal file
View File

@ -0,0 +1,2 @@
const APP_NAME = "MTG_UNTAP_CLONE"
const APP_VERSION = "0.1"

1
data/consts.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://bo0256822a2ss

52
deck_input.gd Normal file
View File

@ -0,0 +1,52 @@
extends Node
var _caching = preload("res://caching.gd")
var _decklist
var cards: String
func _init(_cards: String) -> void:
cards = _cards
_decklist = Dictionary()
func _convert_mtgo_to_cache_lookup(decklist: String) -> Dictionary:
var _cards = {}
var lines = decklist.split("\n")
for line in lines:
var words = line.split(" ", false, 1)
if words.size() != 2:
continue
_cards[words[1]] = words[0]
return _cards
func _do_free(cache) -> void:
if !cache.has_emitted_all():
return
cache.queue_free()
func do_decklist_grab(decklist: String) -> void:
var cache = _caching.new()
add_child(cache)
var queries = _convert_mtgo_to_cache_lookup(decklist)
for query in queries:
var entry = cache.get_card_data_from_name(query)
_decklist[entry["id"]] = queries[query]
cache.fetch_done.connect(_do_free.bind(cache))
func _show_decklist() -> void:
for card in _decklist:
print(card + " : " + _decklist[card])
func _ready() -> void:
do_decklist_grab(cards)
_show_decklist()

1
deck_input.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://c0vxigp5y302o

View File

@ -3,7 +3,7 @@ extends TextureRect
var _screen_size: Vector2
var _colors: Array[Color]
var _card_scene = preload("res://card.tscn")
var _card_class = preload("res://scenes/card/card.tscn")
# Called when the node enters the scene tree for the first time.
@ -11,11 +11,11 @@ func _ready() -> void:
# TODO: Calculate this field's scale and position based on which no# field this is.
_screen_size = get_viewport_rect().size
var card = _card_scene.instantiate()
var card = _card_class.instantiate()
# TODO: Currently working with an already-cached card with a known ID to load this.
# Later on, the cards should be pulling the IDs directly from the library's list of IDs.
card.init("d3f10f07-7cfe-4a6f-8de6-373e367a731b")
card.init("d3f10f07-7cfe-4a6f-8de6-373e367a731b", _screen_size)
add_child(card)
@ -40,5 +40,5 @@ func set_colors(colors: Array[Color]) -> void:
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
func _process(_delta: float) -> void:
pass

View File

@ -4,6 +4,6 @@
[node name="Field" type="TextureRect"]
offset_top = 540.0
offset_right = 1920.0
offset_right = 1520.0
offset_bottom = 1080.0
script = ExtResource("1_6e7u2")

15
hand.gd Normal file
View File

@ -0,0 +1,15 @@
extends StaticBody2D
var cards: Array[Node] = []
var _card_class = preload("res://scenes/card/card.tscn")
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass

1
hand.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://dvu4gdhqjejeo

View File

@ -3,7 +3,7 @@ extends Node2D
var _card_class = preload("res://card.gd")
# Library cards are represented as an array of card IDs.
var lib_cards: Array[String] = []
var lib_cards: Array[String]
var num_cards: int = 0
@ -11,19 +11,13 @@ func _load_card_callback(card) -> void:
card.load_card()
func _init(card_ids: Array) -> void:
func _init(_decklist: Dictionary) -> void:
lib_cards = Array()
var temp_card
for id in card_ids:
temp_card = _card_class.new(id)
temp_card.cache_done.connect(_load_card_callback.bind(temp_card))
lib_cards.push_back(temp_card)
num_cards += 1
func init(card_ids: Array[String]) -> void:
for id in card_ids:
pass
for card in _decklist:
var _num = _decklist[card]
num_cards += _num
for i in _num:
lib_cards.push_back(card)
func add_cards(cards: Array, top: bool) -> void:

View File

@ -1,21 +1,41 @@
extends Node2D
# var _card_class = preload("res://card.gd")
var _card_class = preload("res://scenes/card/card.tscn")
var field_scene = preload("res://field.tscn")
var fields: Array[Node] = []
var decks: Array[Dictionary]
func _load_decks():
if !FileAccess.file_exists("user://decks.json"):
return # no loaded decks
var file = FileAccess.open("user://decks.json", FileAccess.READ)
decks = JSON.parse_string(file.get_as_text())
file.close()
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
# The first field in the array will be the player's own field.
# Might be a better idea to have that in a seperate variable? idk
fields.append(field_scene.instantiate())
var colors: Array[Color] = [Color(1, 0, 1)]
fields[0].set_colors(colors)
add_child(fields[0])
var card = _card_class.instantiate()
# TODO: Currently working with an already-cached card with a known ID to load this.
# Later on, the cards should be pulling the IDs directly from the library's list of IDs.
card.init("d3f10f07-7cfe-4a6f-8de6-373e367a731b")
add_child(card)
#fields.append(field_scene.instantiate())
#var colors: Array[Color] = [Color(1, 0, 1)]
#fields[0].set_colors(colors)
#add_child(fields[0])
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
func _process(_delta: float) -> void:
pass

View File

@ -1,12 +1,21 @@
[gd_scene load_steps=3 format=3 uid="uid://cx0vga81xwckh"]
[gd_scene load_steps=5 format=3 uid="uid://cx0vga81xwckh"]
[ext_resource type="Script" uid="uid://w2rqm1u7p7im" path="res://player.gd" id="1_4flbx"]
[ext_resource type="Script" uid="uid://dvu4gdhqjejeo" path="res://hand.gd" id="2_i3pqv"]
[ext_resource type="Script" uid="uid://bc51go8t8uvts" path="res://library.gd" id="2_onrkg"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_onrkg"]
size = Vector2(1920, 200)
[node name="Player" type="Node2D"]
script = ExtResource("1_4flbx")
[node name="Hand" type="Node2D" parent="."]
[node name="Hand" type="StaticBody2D" parent="."]
script = ExtResource("2_i3pqv")
[node name="CollisionArea" type="CollisionShape2D" parent="Hand"]
position = Vector2(960, 980)
shape = SubResource("RectangleShape2D_onrkg")
[node name="Library" type="Node2D" parent="."]
script = ExtResource("2_onrkg")

View File

@ -24,3 +24,11 @@ window/size/mode=3
[editor_plugins]
enabled=PackedStringArray()
[input]
MAIN={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
]
}

View File

@ -1,23 +1,81 @@
extends TextureRect
extends Node2D
## The card class
##
## Represents an instance of a card to be displayed on the tabletop.
## Contains helper text for the text, the cards ID, and the image path.
# TODO: Implement card utilities such as mana cost, land value, etc using api data.
const ManaCosts = preload("res://data/mana.gd")
# Card information.
var card_id: String
var card_name: String
var card_type: String
var oracle_text: String
# Card properties.
var tapped: bool
# Card input state.
var is_focused: bool # Is the card a focus?
var is_dragging: bool # Is the card currently being dragged?
var mouse_offset: Vector2
func _physics_process(delta: float) -> void:
if is_focused:
# TODO: Export handling keypresses to its own area.
if Input.is_action_just_pressed("MAIN"):
tapped = not tapped
$TweenController.tap(tapped, delta)
if is_dragging:
$TweenController.move_to(get_global_mouse_position() - mouse_offset, delta)
func _on_input_event(viewport: Node, event: InputEvent, shape_idx: int) -> void:
if event is not InputEventMouseButton:
return
match event.button_index:
# MOUSE BUTTONS
MOUSE_BUTTON_LEFT:
if event.pressed:
Input.set_default_cursor_shape(Input.CURSOR_DRAG)
mouse_offset = get_global_mouse_position() - global_position
is_dragging = true
else:
Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND)
is_dragging = false
MOUSE_BUTTON_RIGHT:
# TODO: Tooltip menu for right-button clicking on cards.
pass
func _on_mouse_entered() -> void:
# Do not care about mouse entering if we're dragging the card.
if is_dragging:
return
Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND)
$TweenController.scale(1.05)
is_focused = true
func _on_mouse_exited() -> void:
# Do not care about mouse exiting if we're dragging the card.
if is_dragging:
return
Input.set_default_cursor_shape(Input.CURSOR_ARROW)
$TweenController.scale(1.0)
is_focused = false
func _card_error(error_type: String) -> String:
return "ERROR::CARD::%s::%s::%s::\n" % [card_id, card_name, error_type]
func init(id) -> void:
func init(id: String) -> void:
card_id = id
@ -62,7 +120,7 @@ func _load_data() -> Error:
func _load_image() -> Error:
# NOTE: Assuming we're going with using the .png cards on board.
var cached_img = FileAccess.get_file_as_bytes("user://card_cache/" + card_id + "/card.jpg")
var cached_img = FileAccess.get_file_as_bytes("user://card_cache/" + card_id + "/card.png")
if cached_img.is_empty():
push_error("%sCard on-board image was not found in cache" % _card_error("CACHE"))
@ -75,9 +133,12 @@ func _load_image() -> Error:
push_error("%sCard on-board image failed to load correctly" % _card_error("IMAGE"))
return FAILED
var size = $Area2D/CollisionShape2D.shape.size
image.resize(int(size.x), int(size.y), Image.INTERPOLATE_LANCZOS)
var image_texture = ImageTexture.new()
image_texture.set_image(image)
texture = image_texture
$Sprite2D.texture = image_texture
return OK

24
scenes/card/card.tscn Normal file
View File

@ -0,0 +1,24 @@
[gd_scene load_steps=4 format=3 uid="uid://cah3mvdnom1xg"]
[ext_resource type="Script" uid="uid://b3yqd1qu7dyq" path="res://scenes/card/card.gd" id="1_kikvd"]
[ext_resource type="Script" uid="uid://bkk0pyypi1id7" path="res://scenes/card/tween.gd" id="2_imta7"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kikvd"]
size = Vector2(125, 175)
[node name="Card" type="Node2D"]
script = ExtResource("1_kikvd")
[node name="Sprite2D" type="Sprite2D" parent="."]
[node name="Area2D" type="Area2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
shape = SubResource("RectangleShape2D_kikvd")
[node name="TweenController" type="Node2D" parent="."]
script = ExtResource("2_imta7")
[connection signal="input_event" from="Area2D" to="." method="_on_input_event"]
[connection signal="mouse_entered" from="Area2D" to="." method="_on_mouse_entered"]
[connection signal="mouse_exited" from="Area2D" to="." method="_on_mouse_exited"]

0
scenes/card/input.gd Normal file
View File

1
scenes/card/input.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://dhgk6fhw8oua0

22
scenes/card/tween.gd Normal file
View File

@ -0,0 +1,22 @@
extends Node
@export var move_speed = 1.0
@export var tap_speed = 5.0
@export var scale_speed = 0.1
func move_to(location: Vector2, delta: float) -> void:
var tween = create_tween()
tween.tween_property(get_parent(), "position", location, delta * move_speed)
func tap(tapped: bool, delta: float) -> void:
var tween = create_tween()
var rotation = 90 if tapped else 0
tween.tween_property(get_parent(), "rotation_degrees", rotation, delta * tap_speed)
func scale(scalar: float) -> void:
var tween = create_tween()
var new_scale = Vector2.ONE * scalar
tween.tween_property(get_parent(), "scale", new_scale, scale_speed)

1
scenes/card/tween.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://bkk0pyypi1id7

View File

@ -1,18 +1,37 @@
extends Node2D
var player_class = preload("res://player.gd")
var deck_input = preload("res://deck_input.gd")
var _caching = preload("res://caching.gd")
var cards = "1 All That Glitters\n1 Ancestral Mask\n1 Angelic Destiny\n1 Arcane Signet\n1 Archon of Sun's Grace\n1 Austere Command\n1 Banishing Light\n1 Bear Umbra\n1 Blossoming Sands\n1 Canopy Vista\n1 Celestial Mantle\n1 Collective Resistance\n1 Command Tower\n1 Danitha Capashen, Paragon\n1 Danitha, New Benalia's Light\n1 Darksteel Mutation\n1 Daybreak Coronet\n1 Destiny Spinner\n1 Eidolon of Blossoms\n1 Eidolon of Countless Battles\n1 Ellivere of the Wild Court\n1 Enchantress's Presence\n1 Envoy of the Ancestors\n1 Ethereal Armor\n1 Fertile Ground\n13 Forest\n1 Frantic Strength\n1 Generous Gift\n1 Gilded Lotus\n1 Glittering Frost\n1 Grasp of Fate\n1 Gylwain, Casting Director\n1 Hall of Heliod's Generosity\n1 Heliod's Pilgrim\n1 Hidden Grotto\n1 Horrid Vigor\n1 Idyllic Tutor\n1 Jukai Naturalist\n1 Kenrith's Transformation\n1 Kor Spiritdancer\n1 Krosan Verge\n1 Light-Paws, Emperor's Voice\n1 Luminous Broodmoth\n1 Mantle of the Ancients\n1 Overgrowth\n1 Overprotect\n1 Pacifism\n14 Plains\n1 Rancor\n1 Retether\n1 Rogue's Passage\n1 Sage's Reverie\n1 Sanctum Weaver\n1 Selesnya Guildgate\n1 Setessan Champion\n1 Shalai, Voice of Plenty\n1 Snake Umbra\n1 Sol Ring\n1 Solemnity\n1 Songbirds' Blessing\n1 Starfield Mystic\n1 Swords to Plowshares\n1 Tanglespan Lookout\n1 Timber Paladin\n1 Timely Ward\n1 Tithe Takern1 Transcendent Envoy\n1 Twinblade Blessing\n1 Umbra Mystic\n1 Unfinished Business\n1 Utopia Sprawl\n1 Wild Growth\n1 Winds of Rath\n1 Yenna, Redtooth Regent\n1 Sythis, Harvest's Hand"
func _bulk_callback(cache) -> void:
cache.setup()
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
var cache = _caching.new()
add_child(cache)
if cache.setup() != OK:
cache.fetch_done.connect(_bulk_callback.bind(cache))
# TODO: Create 2-4 player instances as children of this tabletop node.
var player = player_class.new()
add_child(player)
move_child(player, 0)
cache.get_card_data_from_name("1996 World Champion")
# var deck = deck_input.new(cards)
# add_child(deck)
pass # Replace with function body.
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
func _process(_delta: float) -> void:
pass

View File

@ -1,6 +1,46 @@
[gd_scene load_steps=2 format=3 uid="uid://b4ldtb3gw0jlu"]
[gd_scene load_steps=5 format=3 uid="uid://b4ldtb3gw0jlu"]
[ext_resource type="Script" uid="uid://cfkew150yl1y3" path="res://tabletop.gd" id="1_3we3x"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3we3x"]
bg_color = Color(0, 0, 0, 1)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_d43bn"]
bg_color = Color(0.6, 0.6, 0.6, 0.709804)
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pqag1"]
bg_color = Color(0.6, 0.6, 0.6, 0)
[node name="Tabletop" type="Node2D"]
script = ExtResource("1_3we3x")
[node name="UI" type="Control" parent="."]
layout_mode = 3
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
[node name="BigBar" type="PanelContainer" parent="UI"]
layout_mode = 0
offset_left = 1520.0
offset_right = 1920.0
offset_bottom = 1080.0
theme_override_styles/panel = SubResource("StyleBoxFlat_3we3x")
[node name="Items" type="VBoxContainer" parent="UI/BigBar"]
layout_mode = 2
theme_override_constants/separation = 0
[node name="MenuArea" type="PanelContainer" parent="UI/BigBar/Items"]
custom_minimum_size = Vector2(0, 540)
layout_mode = 2
[node name="ChatArea" type="PanelContainer" parent="UI/BigBar/Items"]
custom_minimum_size = Vector2(0, 270)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_d43bn")
[node name="LibraryArea" type="PanelContainer" parent="UI/BigBar/Items"]
custom_minimum_size = Vector2(0, 270)
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_pqag1")