Compare commits
21 Commits
card-rewor
...
main
Author | SHA1 | Date | |
---|---|---|---|
1a5372f713 | |||
c125d1b70a | |||
bd5b734d79 | |||
31175b4ecd | |||
76a23aab5c | |||
6eb80f768b | |||
1fe7500dd2 | |||
1c5c4728a3 | |||
75767e927d | |||
c684a00f3a | |||
a496bb3982 | |||
a83261cf09 | |||
32bf3be0cd | |||
999c7989f3 | |||
a7aad7d99f | |||
2f5185d22c | |||
933e6b715c | |||
45919dc5ae | |||
709859abad | |||
04ae9b856b | |||
86cc3bf8a8 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
||||
|
||||
# custom ignores
|
||||
symbol_cache/
|
||||
|
122
caching.gd
122
caching.gd
@ -32,6 +32,8 @@ func setup() -> Error:
|
||||
push_error("Not done downloading Bulk Data.")
|
||||
return FAILED
|
||||
|
||||
_fetch_mana_symbols()
|
||||
|
||||
_setup_cache_in_mem()
|
||||
return OK
|
||||
|
||||
@ -73,7 +75,7 @@ func _get_dict_from_file(filepath: String) -> Dictionary:
|
||||
## _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)
|
||||
return _get_card_data_from_bulk(_search_results_name(_name))
|
||||
|
||||
|
||||
## get_card_data_from_id
|
||||
@ -83,36 +85,123 @@ func get_card_data_from_name(_name: String) -> Dictionary:
|
||||
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)
|
||||
|
||||
return _get_card_data_from_bulk(_search_results_generic("id", id))
|
||||
|
||||
|
||||
func _get_card_data_from_bulk(field: String, search_query: String) -> Dictionary:
|
||||
var selected_entry = null
|
||||
func _search_results_name(search_query: String) -> Dictionary:
|
||||
for entry in _bulk_data:
|
||||
if entry[field] == search_query:
|
||||
selected_entry = entry
|
||||
break
|
||||
if entry["layout"] == "art_series":
|
||||
continue
|
||||
|
||||
if selected_entry == null:
|
||||
var entry_name = entry["name"]
|
||||
if entry_name.contains("//"):
|
||||
entry_name = entry_name.left(entry_name.find("//") - 1)
|
||||
if entry_name == search_query:
|
||||
return entry
|
||||
push_error("Could not find desired card {" + search_query + "}")
|
||||
return {}
|
||||
|
||||
if selected_entry["image_status"] != "missing":
|
||||
_fetch_card_img(selected_entry)
|
||||
|
||||
func _search_results_generic(field: String, search_query: String) -> Dictionary:
|
||||
for entry in _bulk_data:
|
||||
if entry["layout"] == "art_series":
|
||||
continue
|
||||
if entry[field] == search_query:
|
||||
return entry[field]
|
||||
|
||||
push_error("Could not find desired card {" + search_query + "}")
|
||||
return {}
|
||||
|
||||
|
||||
func _get_card_data_from_bulk(dict_entry: Dictionary) -> Dictionary:
|
||||
if dict_entry["image_status"] != "missing":
|
||||
_fetch_card_img(dict_entry)
|
||||
|
||||
var dir = DirAccess.open("user://")
|
||||
dir.make_dir_recursive("user://card_cache/" + selected_entry["id"] + "/")
|
||||
dir.make_dir_recursive("user://card_cache/" + dict_entry["id"] + "/")
|
||||
dir = null
|
||||
|
||||
var file = FileAccess.open(
|
||||
"user://card_cache/" + selected_entry["id"] + "/card.json", FileAccess.WRITE
|
||||
"user://card_cache/" + dict_entry["id"] + "/card.json", FileAccess.WRITE
|
||||
)
|
||||
file.store_line(JSON.stringify(selected_entry, "\t"))
|
||||
file.store_line(JSON.stringify(dict_entry, "\t"))
|
||||
file.close()
|
||||
|
||||
print("Card: " + selected_entry["name"] + "(" + selected_entry["id"] + ") found, and cached.")
|
||||
print("Card: " + dict_entry["name"] + " (" + dict_entry["id"] + ") found, and cached.")
|
||||
|
||||
return selected_entry
|
||||
return dict_entry
|
||||
|
||||
|
||||
func _get_mana_img(symbol: String, img_url: String) -> Error:
|
||||
fetch_start.emit()
|
||||
if FileAccess.file_exists("res://symbol_cache/" + symbol + ".svg"):
|
||||
return OK
|
||||
|
||||
var httpr = HTTPRequest.new()
|
||||
add_child(httpr)
|
||||
|
||||
var err = httpr.request(img_url, _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_svg_from_buffer(resp[3])
|
||||
if err != OK:
|
||||
push_error(_cache_error("IMG_LOADING") + "Couldn't load the image.")
|
||||
return FAILED
|
||||
|
||||
if img.get_size() == Vector2i(100, 100):
|
||||
print("resizing")
|
||||
img.resize(20, 20, Image.INTERPOLATE_LANCZOS)
|
||||
|
||||
img.save_png(
|
||||
"res://symbol_cache/" + symbol.replace("/", "-").replace("{", "").replace("}", "") + ".png"
|
||||
)
|
||||
|
||||
img = null
|
||||
|
||||
fetch_done.emit()
|
||||
|
||||
return OK
|
||||
|
||||
|
||||
func _fetch_mana_symbols() -> Error:
|
||||
var mana_symbols: Dictionary = Dictionary()
|
||||
if DirAccess.dir_exists_absolute("res://symbol_cache"):
|
||||
return OK
|
||||
else:
|
||||
DirAccess.make_dir_absolute("res://symbol_cache")
|
||||
|
||||
var httpr = HTTPRequest.new()
|
||||
add_child(httpr)
|
||||
|
||||
var err = httpr.request("https://api.scryfall.com/symbology", _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 unprocessed_body = resp[3].get_string_from_utf8()
|
||||
var json_body = JSON.parse_string(unprocessed_body)
|
||||
for icon in json_body["data"]:
|
||||
err = await _get_mana_img(icon["symbol"], icon["svg_uri"])
|
||||
if err != OK:
|
||||
push_error("Couldn't fetch mana symbol " + icon["symbol"])
|
||||
mana_symbols[icon["symbol"]] = (
|
||||
"res://symbol_cache/"
|
||||
+ icon["symbol"].replace("/", "-").replace("{", "").replace("}", "")
|
||||
+ ".png"
|
||||
)
|
||||
print(icon["symbol"] + " image cached.")
|
||||
|
||||
var file = FileAccess.open("res://symbol_cache/symbols.json", FileAccess.WRITE)
|
||||
file.store_line(JSON.stringify(mana_symbols))
|
||||
file.close()
|
||||
|
||||
print("Done caching mana symbols.")
|
||||
return OK
|
||||
|
||||
|
||||
func _fetch_card_img(data: Dictionary) -> Error:
|
||||
@ -153,7 +242,6 @@ func get_bulk_data(force: bool) -> Error:
|
||||
DirAccess.remove_absolute("user://bulk.json")
|
||||
else:
|
||||
return OK
|
||||
|
||||
var httpr = HTTPRequest.new()
|
||||
add_child(httpr)
|
||||
|
||||
|
@ -4,14 +4,29 @@ var _caching = preload("res://caching.gd")
|
||||
|
||||
var _decklist
|
||||
|
||||
var cards: String
|
||||
|
||||
|
||||
func _init(_cards: String) -> void:
|
||||
cards = _cards
|
||||
func _init() -> void:
|
||||
_decklist = Dictionary()
|
||||
|
||||
|
||||
func _write_to_decks(_decks: Array) -> void:
|
||||
if FileAccess.file_exists("user://decks.json"):
|
||||
DirAccess.remove_absolute("user://decks.json")
|
||||
|
||||
var file = FileAccess.open("user://decks.json", FileAccess.WRITE)
|
||||
file.store_line(JSON.stringify(_decks))
|
||||
file.close()
|
||||
|
||||
|
||||
func load_decks() -> Array:
|
||||
if !FileAccess.file_exists("user://decks.json"):
|
||||
return []
|
||||
var file = FileAccess.open("user://decks.json", FileAccess.READ)
|
||||
var file_text = file.get_as_text()
|
||||
file.close()
|
||||
return JSON.parse_string(file_text)
|
||||
|
||||
|
||||
func _convert_mtgo_to_cache_lookup(decklist: String) -> Dictionary:
|
||||
var _cards = {}
|
||||
var lines = decklist.split("\n")
|
||||
@ -30,23 +45,29 @@ func _do_free(cache) -> void:
|
||||
cache.queue_free()
|
||||
|
||||
|
||||
func do_decklist_grab(decklist: String) -> void:
|
||||
func add_new_deck(cards: String, _name: String, about = "") -> void:
|
||||
var _queries = _convert_mtgo_to_cache_lookup(cards)
|
||||
_do_decklist_cache(_queries)
|
||||
|
||||
var _decks = load_decks()
|
||||
_decks.push_back({"name": _name, "about": about, "decklist": _queries})
|
||||
_write_to_decks(_decks)
|
||||
|
||||
|
||||
func _do_decklist_cache(_queries: Dictionary) -> void:
|
||||
var cache = _caching.new()
|
||||
add_child(cache)
|
||||
cache.setup()
|
||||
|
||||
var queries = _convert_mtgo_to_cache_lookup(decklist)
|
||||
for query in queries:
|
||||
for query in _queries:
|
||||
var entry = cache.get_card_data_from_name(query)
|
||||
_decklist[entry["id"]] = queries[query]
|
||||
|
||||
if entry.size() == 0:
|
||||
push_error("Failed to find card: " + query)
|
||||
continue
|
||||
_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()
|
||||
|
6
event_bus.gd
Normal file
6
event_bus.gd
Normal file
@ -0,0 +1,6 @@
|
||||
extends Node
|
||||
|
||||
@warning_ignore("unused_signal")
|
||||
signal card_on_hover(card_info, card_image)
|
||||
@warning_ignore("unused_signal")
|
||||
signal card_on_unhover
|
1
event_bus.gd.uid
Normal file
1
event_bus.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://b5re77jjgr8ca
|
@ -1,6 +1,6 @@
|
||||
extends Node2D
|
||||
|
||||
var _card_class = preload("res://card.gd")
|
||||
var _card_class = preload("res://scenes/card/card.gd")
|
||||
|
||||
# Library cards are represented as an array of card IDs.
|
||||
var lib_cards: Array[String]
|
||||
|
17
player.gd
17
player.gd
@ -8,27 +8,22 @@ 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
|
||||
|
||||
var card = _card_class.instantiate()
|
||||
card.init("d3f10f07-7cfe-4a6f-8de6-373e367a731b")
|
||||
add_child(card)
|
||||
card.position = Vector2(100, 100)
|
||||
|
||||
# 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)]
|
||||
|
@ -15,6 +15,10 @@ run/main_scene="uid://b4ldtb3gw0jlu"
|
||||
config/features=PackedStringArray("4.4", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[autoload]
|
||||
|
||||
EventBus="*res://event_bus.gd"
|
||||
|
||||
[display]
|
||||
|
||||
window/size/viewport_width=1920
|
||||
@ -32,3 +36,8 @@ MAIN={
|
||||
"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)
|
||||
]
|
||||
}
|
||||
SELECT={
|
||||
"deadzone": 0.2,
|
||||
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
@ -5,140 +5,68 @@ extends Node2D
|
||||
## 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
|
||||
var card_info: Dictionary
|
||||
var cached_image: Image
|
||||
|
||||
# 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 hovered: bool # Is the mouse currently on this card?
|
||||
var dragging: bool # Is the card currently being dragged?
|
||||
var focused: bool # Is this card currently a focus?
|
||||
|
||||
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 init(id: String) -> void:
|
||||
card_info["id"] = id
|
||||
|
||||
|
||||
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
|
||||
# This is called when we want to apply the behaviour of the mouse being
|
||||
# inside/outside the card when we can't trigger the enter/exit triggers.
|
||||
func check_hover() -> void:
|
||||
if hovered:
|
||||
_on_mouse_entered()
|
||||
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
|
||||
_on_mouse_exited()
|
||||
|
||||
|
||||
func error(error_type: String) -> String:
|
||||
return "ERROR::CARD::%s::%s::%s::\n" % [card_info["id"], card_info["name"], error_type]
|
||||
|
||||
|
||||
func colission_size() -> Vector2:
|
||||
return $Area2D/CollisionShape2D.shape.size
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
focused = hovered or dragging
|
||||
|
||||
$InputHandler.handle_inputs(delta)
|
||||
$TweenController.handle_constant_tweens(delta)
|
||||
|
||||
|
||||
func _on_mouse_entered() -> void:
|
||||
# Do not care about mouse entering if we're dragging the card.
|
||||
if is_dragging:
|
||||
hovered = true
|
||||
|
||||
# Do not apply any more effects if we're dragging the card.
|
||||
if dragging:
|
||||
return
|
||||
|
||||
Input.set_default_cursor_shape(Input.CURSOR_POINTING_HAND)
|
||||
$TweenController.scale(1.05)
|
||||
is_focused = true
|
||||
EventBus.emit_signal("card_on_hover", card_info, cached_image)
|
||||
|
||||
|
||||
func _on_mouse_exited() -> void:
|
||||
# Do not care about mouse exiting if we're dragging the card.
|
||||
if is_dragging:
|
||||
hovered = false
|
||||
|
||||
# Do not apply any more effects if we're dragging the card.
|
||||
if 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
|
||||
EventBus.emit_signal("card_on_unhover")
|
||||
|
@ -1,7 +1,9 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://cah3mvdnom1xg"]
|
||||
[gd_scene load_steps=6 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"]
|
||||
[ext_resource type="Script" uid="uid://dhgk6fhw8oua0" path="res://scenes/card/input.gd" id="3_vtcvk"]
|
||||
[ext_resource type="Script" uid="uid://vckbno504iay" path="res://scenes/card/load.gd" id="4_g65cd"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_kikvd"]
|
||||
size = Vector2(125, 175)
|
||||
@ -16,9 +18,14 @@ script = ExtResource("1_kikvd")
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"]
|
||||
shape = SubResource("RectangleShape2D_kikvd")
|
||||
|
||||
[node name="TweenController" type="Node2D" parent="."]
|
||||
[node name="TweenController" type="Node" parent="."]
|
||||
script = ExtResource("2_imta7")
|
||||
|
||||
[connection signal="input_event" from="Area2D" to="." method="_on_input_event"]
|
||||
[node name="InputHandler" type="Node" parent="."]
|
||||
script = ExtResource("3_vtcvk")
|
||||
|
||||
[node name="DataLoader" type="Node" parent="."]
|
||||
script = ExtResource("4_g65cd")
|
||||
|
||||
[connection signal="mouse_entered" from="Area2D" to="." method="_on_mouse_entered"]
|
||||
[connection signal="mouse_exited" from="Area2D" to="." method="_on_mouse_exited"]
|
||||
|
@ -0,0 +1,27 @@
|
||||
extends Node
|
||||
|
||||
var card: Node
|
||||
var tween_controller: Node
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
card = get_parent()
|
||||
tween_controller = card.get_node("TweenController")
|
||||
|
||||
|
||||
func handle_inputs(delta: float) -> void:
|
||||
if not card.focused:
|
||||
# TODO: Global card actions, e.g. untapping everything.
|
||||
return
|
||||
|
||||
if Input.is_action_just_pressed("MAIN"):
|
||||
card.tapped = not card.tapped
|
||||
tween_controller.tap(card.tapped, delta)
|
||||
|
||||
if Input.is_action_just_pressed("SELECT"):
|
||||
card.dragging = true
|
||||
Input.set_default_cursor_shape(Input.CURSOR_DRAG)
|
||||
card.mouse_offset = card.get_global_mouse_position() - card.global_position
|
||||
if Input.is_action_just_released("SELECT"):
|
||||
card.dragging = false
|
||||
card.check_hover()
|
83
scenes/card/load.gd
Normal file
83
scenes/card/load.gd
Normal file
@ -0,0 +1,83 @@
|
||||
extends Node
|
||||
|
||||
var card: Node
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
card = get_parent()
|
||||
|
||||
if _load_card() != 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.card_info["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.card_info["name"] = card_json["name"]
|
||||
card.card_info["type"] = card_json["type_line"]
|
||||
card.card_info["desc"] = card_json["oracle_text"]
|
||||
card.card_info["cost"] = card_json["mana_cost"]
|
||||
|
||||
return OK
|
||||
|
||||
|
||||
func _load_image() -> Error:
|
||||
var cached_img = FileAccess.get_file_as_bytes(
|
||||
"user://card_cache/" + card.card_info["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 cache_image = Image.new()
|
||||
var image_status: Error = cache_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
|
||||
|
||||
card.cached_image = cache_image
|
||||
|
||||
var image = Image.new()
|
||||
image_status = 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 = card.colission_size()
|
||||
image.resize(int(size.x), int(size.y), Image.INTERPOLATE_LANCZOS)
|
||||
|
||||
var image_texture = ImageTexture.new()
|
||||
image_texture.set_image(image)
|
||||
|
||||
var card_sprite = card.get_node("Sprite2D")
|
||||
card_sprite.texture = image_texture
|
||||
|
||||
return OK
|
1
scenes/card/load.gd.uid
Normal file
1
scenes/card/load.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://vckbno504iay
|
@ -4,19 +4,32 @@ extends Node
|
||||
@export var tap_speed = 5.0
|
||||
@export var scale_speed = 0.1
|
||||
|
||||
var card: Node
|
||||
|
||||
# TODO: Figure out elastic tween transitions for bounciness.
|
||||
|
||||
|
||||
func handle_constant_tweens(delta: float) -> void:
|
||||
if card.dragging:
|
||||
move_to(card.get_global_mouse_position() - card.mouse_offset, delta)
|
||||
|
||||
|
||||
func move_to(location: Vector2, delta: float) -> void:
|
||||
var tween = create_tween()
|
||||
tween.tween_property(get_parent(), "position", location, delta * move_speed)
|
||||
tween.tween_property(card, "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)
|
||||
tween.tween_property(card, "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)
|
||||
tween.tween_property(card, "scale", new_scale, scale_speed)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
card = get_parent()
|
||||
|
17
scenes/tooltip/card_image.gd
Normal file
17
scenes/tooltip/card_image.gd
Normal file
@ -0,0 +1,17 @@
|
||||
extends TextureRect
|
||||
|
||||
|
||||
func _set_tip_image(_card_info: Dictionary, card_image: Image) -> void:
|
||||
card_image.resize(int(size.x / 1.75), int(size.y), Image.INTERPOLATE_LANCZOS)
|
||||
var tex = ImageTexture.new()
|
||||
tex.set_image(card_image)
|
||||
texture = tex
|
||||
|
||||
|
||||
func _clear_tip_image() -> void:
|
||||
texture = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
EventBus.connect("card_on_hover", _set_tip_image)
|
||||
EventBus.connect("card_on_unhover", _clear_tip_image)
|
1
scenes/tooltip/card_image.gd.uid
Normal file
1
scenes/tooltip/card_image.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cpvbftm0swoa6
|
39
scenes/tooltip/card_text.gd
Normal file
39
scenes/tooltip/card_text.gd
Normal file
@ -0,0 +1,39 @@
|
||||
extends RichTextLabel
|
||||
|
||||
var mana_symbols: Dictionary
|
||||
|
||||
func _convert_text_to_symbol(_text: String):
|
||||
var last_idx = 0
|
||||
for symbol in mana_symbols:
|
||||
last_idx = 0
|
||||
while _text.find(symbol, last_idx) != -1:
|
||||
_text = _text.replace(symbol, "[img]" + mana_symbols[symbol] + "[/img]")
|
||||
last_idx = _text.find(symbol, last_idx) + symbol.length()
|
||||
return _text
|
||||
|
||||
|
||||
func _set_tip_text(card_info: Dictionary, _card_image: Image) -> void:
|
||||
# TODO: add more card formatting, check all of the logos, very niche icons will be affected i believe since they're
|
||||
# different sizes
|
||||
# shrink text if we use too much space for it, etc
|
||||
text = "[b]" + card_info["name"] + "[/b]\t"
|
||||
text += _convert_text_to_symbol(card_info["cost"]) + "\n"
|
||||
text += "[i]" + card_info["type"] + "[/i]\n"
|
||||
|
||||
text += _convert_text_to_symbol(card_info["desc"])
|
||||
|
||||
|
||||
func _clear_tip_text() -> void:
|
||||
text = ""
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if !FileAccess.file_exists("res://symbol_cache/symbols.json"):
|
||||
push_error("Symbols haven't been cached yet!")
|
||||
return
|
||||
var file = FileAccess.open("res://symbol_cache/symbols.json", FileAccess.READ)
|
||||
mana_symbols = JSON.parse_string(file.get_as_text())
|
||||
file.close()
|
||||
|
||||
EventBus.connect("card_on_hover", _set_tip_text)
|
||||
EventBus.connect("card_on_unhover", _clear_tip_text)
|
1
scenes/tooltip/card_text.gd.uid
Normal file
1
scenes/tooltip/card_text.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://b8tioen4n1rip
|
96
tabletop.gd
96
tabletop.gd
@ -4,7 +4,91 @@ 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"
|
||||
|
||||
var cards = "1 Arcane Signet
|
||||
1 Austere Command
|
||||
1 Bartolomé del Presidio
|
||||
1 Blade of the Bloodchief
|
||||
1 Blood Artist
|
||||
1 Bloodghast
|
||||
1 Bloodline Necromancer
|
||||
1 Bloodtracker
|
||||
1 Bojuka Bog
|
||||
1 Butcher of Malakir
|
||||
1 Carmen, Cruel Skymarcher
|
||||
1 Champion of Dusk
|
||||
1 Charismatic Conqueror
|
||||
1 Command Tower
|
||||
1 Commander's Sphere
|
||||
1 Cordial Vampire
|
||||
1 Crossway Troublemakers
|
||||
1 Cruel Celebrant
|
||||
1 Damn
|
||||
1 Drana, Liberator of Malakir
|
||||
1 Dusk Legion Sergeant
|
||||
1 Dusk Legion Zealot
|
||||
1 Elenda, the Dusk Rose
|
||||
1 Elenda's Hierophant
|
||||
1 Etchings of the Chosen
|
||||
1 Exquisite Blood
|
||||
1 Falkenrath Noble
|
||||
1 Glass-Cast Heart
|
||||
1 Heirloom Blade
|
||||
1 Indulgent Aristocrat
|
||||
1 Isolated Chapel
|
||||
1 Kindred Boon
|
||||
1 Legion Lieutenant
|
||||
1 March of the Canonized
|
||||
1 Martyr of Dusk
|
||||
1 Master of Dark Rites
|
||||
1 Mavren Fein, Dusk Apostle
|
||||
1 Mind Stone
|
||||
1 Myriad Landscape
|
||||
1 New Blood
|
||||
1 Nighthawk Scavenger
|
||||
1 Oathsworn Vampire
|
||||
1 Olivia's Wrath
|
||||
1 Order of Sacred Dusk
|
||||
1 Orzhov Basilica
|
||||
1 Orzhov Signet
|
||||
1 Pact of the Serpent
|
||||
1 Path of Ancestry
|
||||
1 Patron of the Vein
|
||||
4 Plains
|
||||
4 Plains
|
||||
1 Promise of Aclazotz
|
||||
1 Radiant Destiny
|
||||
1 Redemption Choir
|
||||
1 Return to Dust
|
||||
1 Rogue's Passage
|
||||
1 Sanctum Seeker
|
||||
1 Secluded Courtyard
|
||||
1 Shineshadow Snarl
|
||||
1 Sol Ring
|
||||
1 Sorin, Lord of Innistrad
|
||||
7 Swamp
|
||||
6 Swamp
|
||||
1 Swiftfoot Boots
|
||||
1 Swords to Plowshares
|
||||
1 Tainted Field
|
||||
1 Talisman of Hierarchy
|
||||
1 Temple of Silence
|
||||
1 Temple of the False God
|
||||
1 Timothar, Baron of Bats
|
||||
1 Twilight Prophet
|
||||
1 Unclaimed Territory
|
||||
1 Utter End
|
||||
1 Vault of the Archangel
|
||||
1 Village Rites
|
||||
1 Viscera Seer
|
||||
1 Voldaren Estate
|
||||
1 Vona, Butcher of Magan
|
||||
1 Wayfarer's Bauble
|
||||
1 Welcoming Vampire
|
||||
1 Windbrisk Heights
|
||||
1 Yahenni, Undying Partisan
|
||||
|
||||
1 Clavileño, First of the Blessed"
|
||||
|
||||
|
||||
func _bulk_callback(cache) -> void:
|
||||
@ -26,10 +110,14 @@ func _ready() -> void:
|
||||
|
||||
cache.get_card_data_from_name("1996 World Champion")
|
||||
|
||||
# var deck = deck_input.new(cards)
|
||||
# add_child(deck)
|
||||
var deck = deck_input.new()
|
||||
add_child(deck)
|
||||
deck.add_new_deck(cards, "Blood rites")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
|
@ -1,6 +1,8 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://b4ldtb3gw0jlu"]
|
||||
[gd_scene load_steps=7 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://b8tioen4n1rip" path="res://scenes/tooltip/card_text.gd" id="2_d43bn"]
|
||||
[ext_resource type="Script" uid="uid://cpvbftm0swoa6" path="res://scenes/tooltip/card_image.gd" id="2_pqag1"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3we3x"]
|
||||
bg_color = Color(0, 0, 0, 1)
|
||||
@ -35,6 +37,23 @@ theme_override_constants/separation = 0
|
||||
custom_minimum_size = Vector2(0, 540)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="UI/BigBar/Items/MenuArea"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="UI/BigBar/Items/MenuArea/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(400, 300)
|
||||
layout_direction = 2
|
||||
layout_mode = 2
|
||||
stretch_mode = 5
|
||||
script = ExtResource("2_pqag1")
|
||||
|
||||
[node name="RichTextLabel" type="RichTextLabel" parent="UI/BigBar/Items/MenuArea/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(0, 230)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
fit_content = true
|
||||
script = ExtResource("2_d43bn")
|
||||
|
||||
[node name="ChatArea" type="PanelContainer" parent="UI/BigBar/Items"]
|
||||
custom_minimum_size = Vector2(0, 270)
|
||||
layout_mode = 2
|
||||
|
Loading…
x
Reference in New Issue
Block a user