initial commit

This commit is contained in:
mr0xb 2025-12-27 00:20:33 -05:00
commit 94847a50a2
42 changed files with 2063 additions and 0 deletions

View file

@ -0,0 +1,321 @@
extends Node
signal decks_loaded()
signal deck_saved(deck: Deck)
signal deck_deleted(deck_id: String)
signal progress_updated(deck_id: String)
const DECKS_DIR = "user://data/decks/"
const PROGRESS_FILE = "user://data/progress/progress.json"
var decks: Array[Deck] = []
var progress_data: Dictionary = {}
var user_id: String = "local"
func _ready() -> void:
_ensure_directories_exist()
load_all_decks()
load_progress()
func _ensure_directories_exist() -> void:
DirAccess.make_dir_recursive_absolute("user://data/decks/")
DirAccess.make_dir_recursive_absolute("user://data/progress/")
func load_all_decks() -> void:
decks.clear()
var dir = DirAccess.open(DECKS_DIR)
if not dir:
push_warning("Could not open decks directory, creating it...")
_ensure_directories_exist()
decks_loaded.emit()
return
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if file_name.ends_with(".json"):
var deck = load_deck_from_file(DECKS_DIR + file_name)
if deck:
decks.append(deck)
file_name = dir.get_next()
dir.list_dir_end()
decks.sort_custom(func(a, b): return a.updated_at > b.updated_at)
print("Loaded %d deck(s)" % decks.size())
decks_loaded.emit()
func load_deck_from_file(file_path: String) -> Deck:
print("Loading Deck From File: ", file_path)
var file = FileAccess.open(file_path, FileAccess.READ)
if not file:
push_error("Failed to open deck file: " + file_path)
return null
var json_string = file.get_as_text()
file.close()
var json = JSON.new()
var error = json.parse(json_string)
if error != OK:
push_error("Failed to parse JSON in file: %s (Error: %s)" % [file_path, json.get_error_message()])
return null
var deck = Deck.new()
deck.from_dict(json.data)
return deck
func save_deck(deck: Deck) -> bool:
if not deck:
push_error("Cannot save null deck")
return false
deck.updated_at = Time.get_datetime_string_from_system(true, true) + "Z"
var file_path = DECKS_DIR + deck.id + ".json"
print("Saving Deck to: ", file_path)
var file = FileAccess.open(file_path, FileAccess.WRITE)
if not file:
push_error("Failed to create deck file: " + file_path)
return false
var deck_dict = deck.to_dict()
var json_string = JSON.stringify(deck_dict, "\t")
file.store_string(json_string)
file.close()
var found = false
for i in range(decks.size()):
if decks[i].id == deck.id:
decks[i] = deck
found = true
break
if not found:
decks.append(deck)
decks.sort_custom(func(a, b): return a.updated_at > b.updated_at)
print("Saved deck: %s (%d cards)" % [deck.name, deck.get_card_count()])
deck_saved.emit(deck)
return true
func delete_deck(deck_id: String) -> bool:
var file_path = DECKS_DIR + deck_id + ".json"
if FileAccess.file_exists(file_path):
var error = DirAccess.remove_absolute(file_path)
if error != OK:
push_error("Failed to delete deck file: " + file_path)
return false
for i in range(decks.size()):
if decks[i].id == deck_id:
decks.remove_at(i)
break
if progress_data.has(deck_id):
progress_data.erase(deck_id)
save_progress()
print("Deleted deck: " + deck_id)
deck_deleted.emit(deck_id)
return true
func get_deck_by_id(deck_id: String) -> Deck:
for deck in decks:
if deck.id == deck_id:
return deck
return null
func load_progress() -> void:
if not FileAccess.file_exists(PROGRESS_FILE):
print("No progress file found, starting fresh")
return
var file = FileAccess.open(PROGRESS_FILE, FileAccess.READ)
if not file:
push_error("Failed to open progress file")
return
var json_string = file.get_as_text()
file.close()
var json = JSON.new()
var error = json.parse(json_string)
if error != OK:
push_error("Failed to parse progress JSON")
return
var data = json.data
user_id = data.get("user_id", "local")
var deck_progress = data.get("deck_progress", {})
for deck_id in deck_progress.keys():
var progress = Progress.new()
progress.from_dict(deck_progress[deck_id])
progress_data[deck_id] = progress
print("Loaded progress for %d deck(s)" % progress_data.size())
func save_progress() -> void:
var deck_progress = {}
for deck_id in progress_data.keys():
deck_progress[deck_id] = progress_data[deck_id].to_dict()
var data = {
"version": "1.0",
"user_id": user_id,
"deck_progress": deck_progress
}
var file = FileAccess.open(PROGRESS_FILE, FileAccess.WRITE)
if not file:
push_error("Failed to save progress file")
return
var json_string = JSON.stringify(data, "\t")
file.store_string(json_string)
file.close()
print("Saved progress for %d deck(s)" % progress_data.size())
func get_or_create_progress(deck_id: String) -> Progress:
if not progress_data.has(deck_id):
var progress = Progress.new()
progress.deck_id = deck_id
progress_data[deck_id] = progress
return progress_data[deck_id]
func record_quiz_result(deck_id: String, card_id: String, is_correct: bool) -> void:
var progress = get_or_create_progress(deck_id)
progress.record_answer(card_id, is_correct)
save_progress()
progress_updated.emit(deck_id)
func get_progress(deck_id: String) -> Progress:
return progress_data.get(deck_id, null)
func download_deck_from_api(api_url: String) -> void:
print("Downloading deck from API: " + api_url)
var http_request = HTTPRequest.new()
add_child(http_request)
http_request.request_completed.connect(_on_api_deck_downloaded.bind(http_request))
var error = http_request.request(api_url)
if error != OK:
push_error("Failed to start HTTP request: " + str(error))
http_request.queue_free()
func _on_api_deck_downloaded(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http_request: HTTPRequest) -> void:
if not http_request:
return
if result != HTTPRequest.RESULT_SUCCESS:
push_error("HTTP request failed with result: " + str(result))
http_request.queue_free()
return
if response_code != 200:
push_error("API returned error code: " + str(response_code))
http_request.queue_free()
return
var json_string = body.get_string_from_utf8()
var json = JSON.new()
var parse_error = json.parse(json_string)
if parse_error != OK:
push_error("Failed to parse API response JSON")
http_request.queue_free()
return
var deck = Deck.new()
deck.from_dict(json.data)
save_deck(deck)
print("Successfully downloaded and saved deck: " + deck.name)
http_request.queue_free()
func upload_deck_to_api(deck: Deck, api_url: String, auth_token: String = "") -> void:
print("Uploading deck to API: " + deck.name)
var http_request = HTTPRequest.new()
add_child(http_request)
http_request.request_completed.connect(_on_api_deck_uploaded.bind(http_request))
var headers = ["Content-Type: application/json"]
if auth_token != "":
headers.append("Authorization: Bearer " + auth_token)
var deck_dict = deck.to_dict()
var json_string = JSON.stringify(deck_dict)
var error = http_request.request(api_url, headers, HTTPClient.METHOD_POST, json_string)
if error != OK:
push_error("Failed to start HTTP upload request: " + str(error))
http_request.queue_free()
func _on_api_deck_uploaded(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http_request: HTTPRequest) -> void:
if not http_request:
return
if result != HTTPRequest.RESULT_SUCCESS:
push_error("HTTP upload failed with result: " + str(result))
http_request.queue_free()
return
if response_code == 200 or response_code == 201:
print("Deck successfully uploaded to API!")
# Optionally parse response to get the deck ID from server
var json_string = body.get_string_from_utf8()
print("Server response: " + json_string)
else:
push_error("API returned error code: " + str(response_code))
http_request.queue_free()
func browse_decks_from_api(api_url: String) -> void:
print("Browsing decks from API: " + api_url)
var http_request = HTTPRequest.new()
add_child(http_request)
http_request.request_completed.connect(_on_api_decks_browsed.bind(http_request))
var error = http_request.request(api_url)
if error != OK:
push_error("Failed to start browse request: " + str(error))
http_request.queue_free()
func _on_api_decks_browsed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http_request: HTTPRequest) -> void:
if not http_request:
return
if result != HTTPRequest.RESULT_SUCCESS or response_code != 200:
push_error("Browse request failed")
http_request.queue_free()
return
var json_string = body.get_string_from_utf8()
var json = JSON.new()
if json.parse(json_string) == OK:
var decks_list = json.data
print("Found %d decks from API" % decks_list.size())
for deck_data in decks_list:
print(" - " + deck_data.get("deck", {}).get("name", "Unknown"))
http_request.queue_free()

View file

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

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://c5nyvx3hkldgq"]
[ext_resource type="Script" path="res://scenes/autoloads/data_manager.gd" id="1_qw8yd"]
[node name="DataManager" type="Node"]
script = ExtResource("1_qw8yd")

View file

@ -0,0 +1,62 @@
extends Node
signal scene_changed(scene_name: String)
var current_scene: Node = null
var scene_container: Node = null
const MAIN_MENU = "res://scenes/ui/main_menu/main_menu.tscn"
const DECK_MANAGER = "res://scenes/ui/deck_manager/deck_manager.tscn"
const QUIZ_SCENE = "res://scenes/ui/quiz_mode/quiz_scene.tscn"
func _ready() -> void:
await get_tree().process_frame
var root = get_tree().root
var main = root.get_node_or_null("Main")
if main:
scene_container = main.get_node_or_null("CanvasLayer/SceneContainer")
if scene_container:
change_scene(MAIN_MENU)
else:
push_error("SceneContainer not found in Main scene")
else:
push_warning("Main scene not found yet, waiting for initialization")
func change_scene(scene_path: String) -> void:
if not scene_container:
push_error("Cannot change scene - scene_container is null")
return
if current_scene:
current_scene.queue_free()
await current_scene.tree_exited
if not ResourceLoader.exists(scene_path):
push_error("Scene does not exist: " + scene_path)
return
var new_scene_resource = load(scene_path)
if not new_scene_resource:
push_error("Failed to load scene: " + scene_path)
return
var new_scene = new_scene_resource.instantiate()
scene_container.add_child(new_scene)
current_scene = new_scene
print("Changed scene to: " + scene_path)
scene_changed.emit(scene_path)
func start_quiz(deck: Deck) -> void:
change_scene(QUIZ_SCENE)
await scene_changed
if current_scene and current_scene.has_method("start_quiz"):
current_scene.start_quiz(deck)
func return_to_main_menu() -> void:
change_scene(MAIN_MENU)
func open_deck_manager() -> void:
change_scene(DECK_MANAGER)

View file

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

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://dv8yxmk7qp5qn"]
[ext_resource type="Script" path="res://scenes/autoloads/game_manager.gd" id="1_8xr2p"]
[node name="GameManager" type="Node"]
script = ExtResource("1_8xr2p")