Compare commits
30 Commits
4489eb5f76
...
card-rewor
Author | SHA1 | Date | |
---|---|---|---|
3c02ae63f9 | |||
ce0bc104ff | |||
7cddb502b4 | |||
d1ef1d6f4f | |||
fc3a31d144 | |||
1d9c3fc473 | |||
5937b4db69 | |||
9217834726 | |||
c463bf0adc | |||
98357b5dec | |||
1beb89ea84 | |||
dd73e1b477 | |||
9bb3308487 | |||
90912c062f | |||
b82f5e4c19 | |||
fe38fc49bf | |||
a40ccab3c8 | |||
f5edba7402 | |||
b9a07a8c47 | |||
0f88bd8f7b | |||
c75aec06ac | |||
d726290cf2 | |||
fafaf404ab | |||
77da7cf6b2 | |||
21f7d9de04 | |||
6100a50754 | |||
bf9244c2b7 | |||
a3fb627310 | |||
c085a93c49 | |||
29042d5d05 |
206
caching.gd
Normal file
206
caching.gd
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
var _req_headers: PackedStringArray
|
||||||
|
|
||||||
|
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 _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
|
||||||
|
|
||||||
|
|
||||||
|
## 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)
|
||||||
|
|
||||||
|
|
||||||
|
## 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 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()
|
||||||
|
err = img.load_png_from_buffer(resp[3])
|
||||||
|
if err != OK:
|
||||||
|
push_error(_cache_error("IMG_LOADING") + "Couldn't load the image.")
|
||||||
|
return FAILED
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
var httpr = HTTPRequest.new()
|
||||||
|
add_child(httpr)
|
||||||
|
|
||||||
|
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 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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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!"
|
||||||
|
)
|
1
caching.gd.uid
Normal file
1
caching.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cml6c3fdbyy75
|
126
card.gd
126
card.gd
@ -1,126 +0,0 @@
|
|||||||
extends TextureRect
|
|
||||||
## The card class [br][br]
|
|
||||||
##
|
|
||||||
##
|
|
||||||
## Contains helper text for the text, the cards ID, and the image path.
|
|
||||||
## The goal of this class is to make card management easier.
|
|
||||||
|
|
||||||
# we want to use this to convert the mana cost into text
|
|
||||||
# in the helper text box, but thats for the future
|
|
||||||
const ManaCosts = preload("res://data/mana.gd")
|
|
||||||
|
|
||||||
signal cache_done
|
|
||||||
|
|
||||||
var card_id = "placedholder_id"
|
|
||||||
var card_name = "placeholder_name"
|
|
||||||
var card_type = "placeholder_card_type"
|
|
||||||
var oracle_text = "placeholder_oracle_text"
|
|
||||||
var _png_path = "placeholder_image_path"
|
|
||||||
var _jpg_path = "placeholder_image_path"
|
|
||||||
|
|
||||||
func _card_error(error_type: String) -> String:
|
|
||||||
return "CARD::" + card_id + "::" + error_type + "\n"
|
|
||||||
|
|
||||||
func _init(id) -> void:
|
|
||||||
card_id = id
|
|
||||||
|
|
||||||
func _ready() -> void:
|
|
||||||
if (_check_cache(card_id)):
|
|
||||||
return
|
|
||||||
await _do_cache_grab()
|
|
||||||
|
|
||||||
func _do_cache_grab() -> void:
|
|
||||||
await _do_http_request_card()
|
|
||||||
await _do_http_request_imgs(_png_path, true)
|
|
||||||
await _do_http_request_imgs(_jpg_path, false)
|
|
||||||
cache_done.emit()
|
|
||||||
|
|
||||||
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 _do_http_request_imgs(image_path: String, png: bool) -> void:
|
|
||||||
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(_card_error("GET_REQUEST") + "An error occurred in the Scryfall request.")
|
|
||||||
var response = 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:
|
|
||||||
push_error(_card_error("IMG_LOADING") + "Couldn't load the image.")
|
|
||||||
|
|
||||||
img.save_png("user://card_cache/" + card_id + ("/card.png" if png else "/card.jpg"))
|
|
||||||
img = null
|
|
||||||
|
|
||||||
func _do_http_request_card() -> 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/" + card_id, headers)
|
|
||||||
if error != OK:
|
|
||||||
push_error(_card_error("GET_REQUEST") + "An error occurred in the Scryfall request.")
|
|
||||||
|
|
||||||
var response = await httpr.request_completed
|
|
||||||
|
|
||||||
|
|
||||||
if response[0] != HTTPRequest.RESULT_SUCCESS:
|
|
||||||
push_error(_card_error("GET_REQUEST") + "Failed to fetch card data from Scryfall")
|
|
||||||
return
|
|
||||||
|
|
||||||
var unprocessed_body = response[3].get_string_from_utf8()
|
|
||||||
var card_content = JSON.parse_string(unprocessed_body)
|
|
||||||
if (card_content == null):
|
|
||||||
push_error(_card_error("PARSING") + "Failed to parse the Scryfall card results.")
|
|
||||||
|
|
||||||
var dir = DirAccess.open("user://")
|
|
||||||
dir.make_dir_recursive("user://card_cache/" + card_id + "/") # lets ensure the path is there
|
|
||||||
dir = null
|
|
||||||
|
|
||||||
var card_cache = FileAccess.open("user://card_cache/" + card_id + "/card.json", FileAccess.WRITE)
|
|
||||||
card_cache.store_string(unprocessed_body) # cache the json response
|
|
||||||
card_cache = null # closes the file
|
|
||||||
|
|
||||||
var image_uris = card_content["image_uris"]
|
|
||||||
_png_path = image_uris["png"]
|
|
||||||
_jpg_path = image_uris["normal"]
|
|
||||||
|
|
||||||
## load_card
|
|
||||||
##
|
|
||||||
## Loads the card, returns false, and triggers
|
|
||||||
## a cache fetch if the card is not in the cache,
|
|
||||||
## otherwise sets the cards variables if the cache is present.
|
|
||||||
func load_card() -> bool:
|
|
||||||
if !_check_cache(card_id):
|
|
||||||
await _do_cache_grab()
|
|
||||||
push_error(_card_error("CACHE_FAIL") + "Cache wasn't ready, this card will need to be reinitialized")
|
|
||||||
return false
|
|
||||||
|
|
||||||
|
|
||||||
var ondisk_card = FileAccess.open("user://card_cache/" + card_id + "/card.json", FileAccess.READ)
|
|
||||||
var card_json = JSON.parse_string(ondisk_card.get_as_text())
|
|
||||||
|
|
||||||
card_name = card_json["name"]
|
|
||||||
card_type = card_json["type_line"]
|
|
||||||
oracle_text = card_json["oracle_text"]
|
|
||||||
|
|
||||||
var img = Image.new()
|
|
||||||
img.load("user://card_cache/" + card_id + "/card.jpg")
|
|
||||||
texture = ImageTexture.create_from_image(img)
|
|
||||||
|
|
||||||
ondisk_card = null
|
|
||||||
return true
|
|
2
data/consts.gd
Normal file
2
data/consts.gd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
const APP_NAME = "MTG_UNTAP_CLONE"
|
||||||
|
const APP_VERSION = "0.1"
|
1
data/consts.gd.uid
Normal file
1
data/consts.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bo0256822a2ss
|
11
data/mana.gd
11
data/mana.gd
@ -1,10 +1 @@
|
|||||||
enum ManaCosts {
|
enum ManaCosts { WHITE, BLUE, BLACK, RED, GREEN, COLOURLESS, GENERIC, LIFE }
|
||||||
WHITE,
|
|
||||||
BLUE,
|
|
||||||
BLACK,
|
|
||||||
RED,
|
|
||||||
GREEN,
|
|
||||||
COLOURLESS,
|
|
||||||
GENERIC,
|
|
||||||
LIFE
|
|
||||||
}
|
|
||||||
|
52
deck_input.gd
Normal file
52
deck_input.gd
Normal 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
1
deck_input.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://c0vxigp5y302o
|
44
field.gd
Normal file
44
field.gd
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
extends TextureRect
|
||||||
|
|
||||||
|
var _screen_size: Vector2
|
||||||
|
var _colors: Array[Color]
|
||||||
|
|
||||||
|
var _card_class = preload("res://scenes/card/card.tscn")
|
||||||
|
|
||||||
|
|
||||||
|
# Called when the node enters the scene tree for the first time.
|
||||||
|
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_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", _screen_size)
|
||||||
|
|
||||||
|
add_child(card)
|
||||||
|
|
||||||
|
|
||||||
|
func set_colors(colors: Array[Color]) -> void:
|
||||||
|
_colors = colors
|
||||||
|
|
||||||
|
# TODO: Method to take list of colors, split into this format of dictionary, and apply as gradient.
|
||||||
|
var gradient_data := {
|
||||||
|
0.0: Color.MAROON,
|
||||||
|
1.0: Color.MAROON,
|
||||||
|
}
|
||||||
|
|
||||||
|
var gradient := Gradient.new()
|
||||||
|
gradient.offsets = gradient_data.keys()
|
||||||
|
gradient.colors = gradient_data.values()
|
||||||
|
|
||||||
|
var gradient_texture = GradientTexture1D.new()
|
||||||
|
gradient_texture.gradient = gradient
|
||||||
|
|
||||||
|
texture = gradient_texture
|
||||||
|
|
||||||
|
|
||||||
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
pass
|
1
field.gd.uid
Normal file
1
field.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://cqutu8u3qenu0
|
9
field.tscn
Normal file
9
field.tscn
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://clnevm4xcexrs"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://cqutu8u3qenu0" path="res://field.gd" id="1_6e7u2"]
|
||||||
|
|
||||||
|
[node name="Field" type="TextureRect"]
|
||||||
|
offset_top = 540.0
|
||||||
|
offset_right = 1520.0
|
||||||
|
offset_bottom = 1080.0
|
||||||
|
script = ExtResource("1_6e7u2")
|
15
hand.gd
Normal file
15
hand.gd
Normal 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
1
hand.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dvu4gdhqjejeo
|
24
library.gd
24
library.gd
@ -2,34 +2,40 @@ extends Node2D
|
|||||||
|
|
||||||
var _card_class = preload("res://card.gd")
|
var _card_class = preload("res://card.gd")
|
||||||
|
|
||||||
var lib_cards
|
# Library cards are represented as an array of card IDs.
|
||||||
var num_cards
|
var lib_cards: Array[String]
|
||||||
|
var num_cards: int = 0
|
||||||
|
|
||||||
|
|
||||||
func _load_card_callback(card) -> void:
|
func _load_card_callback(card) -> void:
|
||||||
card.load_card()
|
card.load_card()
|
||||||
|
|
||||||
func _init(card_ids: Array) -> void:
|
|
||||||
|
func _init(_decklist: Dictionary) -> void:
|
||||||
lib_cards = Array()
|
lib_cards = Array()
|
||||||
var temp_card
|
for card in _decklist:
|
||||||
for id in card_ids:
|
var _num = _decklist[card]
|
||||||
temp_card = _card_class.new(id)
|
num_cards += _num
|
||||||
temp_card.cache_done.connect(_load_card_callback.bind(temp_card))
|
for i in _num:
|
||||||
lib_cards.push_back(temp_card)
|
lib_cards.push_back(card)
|
||||||
num_cards += 1
|
|
||||||
|
|
||||||
func add_cards(cards: Array, top: bool) -> void:
|
func add_cards(cards: Array, top: bool) -> void:
|
||||||
for card in cards:
|
for card in cards:
|
||||||
add_card(card, top)
|
add_card(card, top)
|
||||||
|
|
||||||
|
|
||||||
func add_card(card, top: bool) -> void:
|
func add_card(card, top: bool) -> void:
|
||||||
if top:
|
if top:
|
||||||
lib_cards.push_front(card)
|
lib_cards.push_front(card)
|
||||||
else:
|
else:
|
||||||
lib_cards.push_back(card)
|
lib_cards.push_back(card)
|
||||||
|
|
||||||
|
|
||||||
func shuffle() -> void:
|
func shuffle() -> void:
|
||||||
lib_cards.shuffle()
|
lib_cards.shuffle()
|
||||||
|
|
||||||
|
|
||||||
func draw_cards(num) -> Array:
|
func draw_cards(num) -> Array:
|
||||||
var ret = Array()
|
var ret = Array()
|
||||||
for i in num:
|
for i in num:
|
||||||
|
43
player.gd
43
player.gd
@ -1,32 +1,41 @@
|
|||||||
extends Node2D
|
extends Node2D
|
||||||
|
|
||||||
var _card_class = preload("res://card.gd")
|
var _card_class = preload("res://scenes/card/card.tscn")
|
||||||
|
|
||||||
var card
|
var field_scene = preload("res://field.tscn")
|
||||||
|
var fields: Array[Node] = []
|
||||||
|
|
||||||
func _on_request_completed(result, response_code, headers, body):
|
var decks: Array[Dictionary]
|
||||||
var json = JSON.parse_string(body.get_string_from_utf8())
|
|
||||||
print(json["name"])
|
|
||||||
|
|
||||||
func _test_func():
|
|
||||||
card.load_card()
|
func _load_decks():
|
||||||
print(card.card_id)
|
if !FileAccess.file_exists("user://decks.json"):
|
||||||
print(card.card_name)
|
return # no loaded decks
|
||||||
print(card.card_type)
|
|
||||||
print(card.oracle_text)
|
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.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
# TODO: Create 2-4 player instances as children of this tabletop node.
|
# The first field in the array will be the player's own field.
|
||||||
card = _card_class.new("d3f10f07-7cfe-4a6f-8de6-373e367a731b")
|
# Might be a better idea to have that in a seperate variable? idk
|
||||||
|
|
||||||
|
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)
|
add_child(card)
|
||||||
|
|
||||||
card.cache_done.connect(_test_func)
|
#fields.append(field_scene.instantiate())
|
||||||
|
#var colors: Array[Color] = [Color(1, 0, 1)]
|
||||||
pass # Replace with function body.
|
#fields[0].set_colors(colors)
|
||||||
|
#add_child(fields[0])
|
||||||
|
|
||||||
|
|
||||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
func _process(delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
pass
|
pass
|
||||||
|
21
player.tscn
21
player.tscn
@ -1,22 +1,21 @@
|
|||||||
[gd_scene load_steps=4 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://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"]
|
[ext_resource type="Script" uid="uid://bc51go8t8uvts" path="res://library.gd" id="2_onrkg"]
|
||||||
[ext_resource type="Script" uid="uid://b3yqd1qu7dyq" path="res://card.gd" id="3_i3pqv"]
|
|
||||||
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_onrkg"]
|
||||||
|
size = Vector2(1920, 200)
|
||||||
|
|
||||||
[node name="Player" type="Node2D"]
|
[node name="Player" type="Node2D"]
|
||||||
script = ExtResource("1_4flbx")
|
script = ExtResource("1_4flbx")
|
||||||
|
|
||||||
[node name="Field" type="Node2D" parent="."]
|
[node name="Hand" type="StaticBody2D" parent="."]
|
||||||
|
script = ExtResource("2_i3pqv")
|
||||||
|
|
||||||
[node name="Hand" type="Node2D" parent="."]
|
[node name="CollisionArea" type="CollisionShape2D" parent="Hand"]
|
||||||
|
position = Vector2(960, 980)
|
||||||
|
shape = SubResource("RectangleShape2D_onrkg")
|
||||||
|
|
||||||
[node name="Library" type="Node2D" parent="."]
|
[node name="Library" type="Node2D" parent="."]
|
||||||
script = ExtResource("2_onrkg")
|
script = ExtResource("2_onrkg")
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="."]
|
|
||||||
offset_left = -1.0
|
|
||||||
offset_top = 1.0
|
|
||||||
offset_right = 39.0
|
|
||||||
offset_bottom = 41.0
|
|
||||||
script = ExtResource("3_i3pqv")
|
|
||||||
|
@ -15,6 +15,20 @@ run/main_scene="uid://b4ldtb3gw0jlu"
|
|||||||
config/features=PackedStringArray("4.4", "Forward Plus")
|
config/features=PackedStringArray("4.4", "Forward Plus")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
[display]
|
||||||
|
|
||||||
|
window/size/viewport_width=1920
|
||||||
|
window/size/viewport_height=1080
|
||||||
|
window/size/mode=3
|
||||||
|
|
||||||
[editor_plugins]
|
[editor_plugins]
|
||||||
|
|
||||||
enabled=PackedStringArray("res://addons/godot_vim/plugin.cfg")
|
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)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
144
scenes/card/card.gd
Normal file
144
scenes/card/card.gd
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
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.
|
||||||
|
|
||||||
|
# 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: String) -> void:
|
||||||
|
card_id = id
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
var load_status = _load_card()
|
||||||
|
if load_status != OK:
|
||||||
|
# TODO: No need to push another error as the failure state of loading does that already,
|
||||||
|
# if the card is not cached, perhaps a placeholder blank card can be used instead?
|
||||||
|
# Setting that up can be put here later...
|
||||||
|
push_error("Failed to load card.")
|
||||||
|
|
||||||
|
|
||||||
|
func _load_card() -> Error:
|
||||||
|
if _load_data() != OK:
|
||||||
|
return FAILED
|
||||||
|
|
||||||
|
if _load_image() != OK:
|
||||||
|
return FAILED
|
||||||
|
|
||||||
|
return OK
|
||||||
|
|
||||||
|
|
||||||
|
func _load_data() -> Error:
|
||||||
|
var cached_json = FileAccess.get_file_as_string("user://card_cache/" + card_id + "/card.json")
|
||||||
|
|
||||||
|
if cached_json.is_empty():
|
||||||
|
push_error("%s\nCard json data was not found in cache" % _card_error("CACHE"))
|
||||||
|
return FAILED
|
||||||
|
|
||||||
|
var card_json = JSON.parse_string(cached_json)
|
||||||
|
|
||||||
|
if card_json == null:
|
||||||
|
push_error("%s\nCard json data is could not be parsed as valid json" % _card_error("DATA"))
|
||||||
|
return FAILED
|
||||||
|
|
||||||
|
card_name = card_json["name"]
|
||||||
|
card_type = card_json["type_line"]
|
||||||
|
oracle_text = card_json["oracle_text"]
|
||||||
|
|
||||||
|
return OK
|
||||||
|
|
||||||
|
|
||||||
|
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.png")
|
||||||
|
|
||||||
|
if cached_img.is_empty():
|
||||||
|
push_error("%sCard on-board image was not found in cache" % _card_error("CACHE"))
|
||||||
|
return FAILED
|
||||||
|
|
||||||
|
var image = Image.new()
|
||||||
|
var image_status: Error = image.load_png_from_buffer(cached_img)
|
||||||
|
|
||||||
|
if image_status != OK:
|
||||||
|
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)
|
||||||
|
|
||||||
|
$Sprite2D.texture = image_texture
|
||||||
|
|
||||||
|
return OK
|
24
scenes/card/card.tscn
Normal file
24
scenes/card/card.tscn
Normal 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
0
scenes/card/input.gd
Normal file
1
scenes/card/input.gd.uid
Normal file
1
scenes/card/input.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dhgk6fhw8oua0
|
22
scenes/card/tween.gd
Normal file
22
scenes/card/tween.gd
Normal 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
1
scenes/card/tween.gd.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://bkk0pyypi1id7
|
20
tabletop.gd
20
tabletop.gd
@ -1,19 +1,37 @@
|
|||||||
extends Node2D
|
extends Node2D
|
||||||
|
|
||||||
var player_class = preload("res://player.gd")
|
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.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready() -> void:
|
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.
|
# TODO: Create 2-4 player instances as children of this tabletop node.
|
||||||
|
|
||||||
var player = player_class.new()
|
var player = player_class.new()
|
||||||
add_child(player)
|
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.
|
pass # Replace with function body.
|
||||||
|
|
||||||
|
|
||||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
func _process(delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
pass
|
pass
|
||||||
|
@ -1,7 +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"]
|
[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"]
|
[node name="Tabletop" type="Node2D"]
|
||||||
position = Vector2(-5760, -16768)
|
|
||||||
script = ExtResource("1_3we3x")
|
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")
|
||||||
|
Reference in New Issue
Block a user