221 lines
6.7 KiB
GDScript
221 lines
6.7 KiB
GDScript
extends Node
|
|
|
|
signal profile_changed
|
|
|
|
const SAVE_PATH := "user://profile.json"
|
|
|
|
var profile: Dictionary = {
|
|
"xp": 0,
|
|
"level": 1,
|
|
"current_streak": 0,
|
|
"best_streak": 0,
|
|
"badges": [],
|
|
"completed_question_ids": [],
|
|
"correct_question_ids": [],
|
|
"question_wrong_counts": {},
|
|
"missed_question_ids": [],
|
|
"system_stats": {},
|
|
"block_history": [],
|
|
"daily_date": "",
|
|
"daily_questions_answered": 0,
|
|
"daily_xp_earned": 0,
|
|
"daily_goal_questions": 10
|
|
}
|
|
|
|
func _ready() -> void:
|
|
load_profile()
|
|
|
|
|
|
func load_profile() -> void:
|
|
if not FileAccess.file_exists(SAVE_PATH):
|
|
_ensure_daily_current()
|
|
save_profile()
|
|
return
|
|
|
|
var file := FileAccess.open(SAVE_PATH, FileAccess.READ)
|
|
if file == null:
|
|
push_error("Unable to open profile save.")
|
|
return
|
|
|
|
var parsed = JSON.parse_string(file.get_as_text())
|
|
if typeof(parsed) == TYPE_DICTIONARY:
|
|
for key in profile.keys():
|
|
if parsed.has(key):
|
|
profile[key] = parsed[key]
|
|
_ensure_tracking_fields()
|
|
_ensure_daily_current()
|
|
profile_changed.emit()
|
|
|
|
|
|
func save_profile() -> void:
|
|
var file := FileAccess.open(SAVE_PATH, FileAccess.WRITE)
|
|
if file == null:
|
|
push_error("Unable to write profile save.")
|
|
return
|
|
file.store_string(JSON.stringify(profile, "\t"))
|
|
|
|
|
|
func get_profile() -> Dictionary:
|
|
_ensure_daily_current()
|
|
return profile.duplicate(true)
|
|
|
|
|
|
func apply_block_results(results: Dictionary) -> void:
|
|
_ensure_daily_current()
|
|
profile["current_streak"] = int(results.get("ending_streak", profile["current_streak"]))
|
|
profile["best_streak"] = max(int(profile["best_streak"]), int(profile["current_streak"]))
|
|
profile["daily_questions_answered"] = int(profile["daily_questions_answered"]) + int(results.get("total", 0))
|
|
|
|
var unlocked_badges: Array = results.get("badges_unlocked", [])
|
|
for badge in unlocked_badges:
|
|
if not profile["badges"].has(badge):
|
|
profile["badges"].append(badge)
|
|
|
|
var answers: Array = results.get("answers", [])
|
|
for answer in answers:
|
|
var question: Dictionary = answer.get("question", {})
|
|
var question_id := str(question.get("id", ""))
|
|
if question_id != "" and not profile["completed_question_ids"].has(question_id):
|
|
profile["completed_question_ids"].append(question_id)
|
|
_apply_question_tracking(question_id, bool(answer.get("is_correct", false)))
|
|
|
|
var system_name := str(question.get("system", "General"))
|
|
_apply_system_result(system_name, bool(answer.get("is_correct", false)))
|
|
|
|
var summary: Dictionary = {
|
|
"timestamp": Time.get_datetime_string_from_system(),
|
|
"mode": results.get("mode", "Tutor"),
|
|
"system": results.get("system", "All Systems"),
|
|
"correct": results.get("correct", 0),
|
|
"total": results.get("total", 0),
|
|
"xp_earned": 0
|
|
}
|
|
profile["block_history"].push_front(summary)
|
|
if profile["block_history"].size() > 12:
|
|
profile["block_history"].resize(12)
|
|
|
|
save_profile()
|
|
profile_changed.emit()
|
|
|
|
|
|
func reset_profile() -> void:
|
|
profile = {
|
|
"xp": 0,
|
|
"level": 1,
|
|
"current_streak": 0,
|
|
"best_streak": 0,
|
|
"badges": [],
|
|
"completed_question_ids": [],
|
|
"correct_question_ids": [],
|
|
"question_wrong_counts": {},
|
|
"missed_question_ids": [],
|
|
"system_stats": {},
|
|
"block_history": [],
|
|
"daily_date": _today_string(),
|
|
"daily_questions_answered": 0,
|
|
"daily_xp_earned": 0,
|
|
"daily_goal_questions": 10
|
|
}
|
|
save_profile()
|
|
profile_changed.emit()
|
|
|
|
|
|
func reset_qbank_memory() -> void:
|
|
_ensure_tracking_fields()
|
|
profile["completed_question_ids"] = []
|
|
profile["correct_question_ids"] = []
|
|
profile["question_wrong_counts"] = {}
|
|
profile["missed_question_ids"] = []
|
|
save_profile()
|
|
profile_changed.emit()
|
|
|
|
|
|
func reset_qbank_eligibility() -> void:
|
|
_ensure_tracking_fields()
|
|
profile["completed_question_ids"] = []
|
|
profile["correct_question_ids"] = []
|
|
save_profile()
|
|
profile_changed.emit()
|
|
|
|
|
|
func apply_question_results(results: Dictionary) -> void:
|
|
_ensure_daily_current()
|
|
profile["current_streak"] = int(results.get("ending_streak", profile["current_streak"]))
|
|
profile["best_streak"] = max(int(profile["best_streak"]), int(profile["current_streak"]))
|
|
profile["daily_questions_answered"] = int(profile["daily_questions_answered"]) + int(results.get("total", 0))
|
|
|
|
var unlocked_badges: Array = results.get("badges_unlocked", [])
|
|
for badge in unlocked_badges:
|
|
if not profile["badges"].has(badge):
|
|
profile["badges"].append(badge)
|
|
|
|
var answers: Array = results.get("answers", [])
|
|
for answer in answers:
|
|
var question: Dictionary = answer.get("question", {})
|
|
var question_id := str(question.get("id", ""))
|
|
if question_id != "" and not profile["completed_question_ids"].has(question_id):
|
|
profile["completed_question_ids"].append(question_id)
|
|
_apply_question_tracking(question_id, bool(answer.get("is_correct", false)), bool(results.get("mastered", true)))
|
|
_apply_system_result(str(question.get("system", "General")), bool(answer.get("is_correct", false)))
|
|
|
|
save_profile()
|
|
profile_changed.emit()
|
|
|
|
|
|
func record_wrong_attempt(question: Dictionary) -> void:
|
|
_ensure_daily_current()
|
|
var question_id := str(question.get("id", ""))
|
|
_apply_question_tracking(question_id, false, false)
|
|
save_profile()
|
|
profile_changed.emit()
|
|
|
|
|
|
func _ensure_daily_current() -> void:
|
|
_ensure_tracking_fields()
|
|
var today := _today_string()
|
|
var changed := false
|
|
if not profile.has("daily_goal_questions"):
|
|
profile["daily_goal_questions"] = 10
|
|
changed = true
|
|
if str(profile.get("daily_date", "")) != today:
|
|
profile["daily_date"] = today
|
|
profile["daily_questions_answered"] = 0
|
|
profile["daily_xp_earned"] = 0
|
|
changed = true
|
|
if changed:
|
|
save_profile()
|
|
|
|
|
|
func _ensure_tracking_fields() -> void:
|
|
if not profile.has("correct_question_ids"):
|
|
profile["correct_question_ids"] = []
|
|
if not profile.has("question_wrong_counts"):
|
|
profile["question_wrong_counts"] = {}
|
|
if not profile.has("missed_question_ids"):
|
|
profile["missed_question_ids"] = []
|
|
|
|
|
|
func _apply_question_tracking(question_id: String, is_correct: bool, mastered: bool = true) -> void:
|
|
if question_id == "":
|
|
return
|
|
if is_correct and mastered:
|
|
if not profile["correct_question_ids"].has(question_id):
|
|
profile["correct_question_ids"].append(question_id)
|
|
elif not is_correct:
|
|
if not profile["missed_question_ids"].has(question_id):
|
|
profile["missed_question_ids"].append(question_id)
|
|
profile["question_wrong_counts"][question_id] = int(profile["question_wrong_counts"].get(question_id, 0)) + 1
|
|
|
|
|
|
func _apply_system_result(system_name: String, is_correct: bool) -> void:
|
|
if not profile["system_stats"].has(system_name):
|
|
profile["system_stats"][system_name] = {"seen": 0, "correct": 0}
|
|
profile["system_stats"][system_name]["seen"] = int(profile["system_stats"][system_name]["seen"]) + 1
|
|
if is_correct:
|
|
profile["system_stats"][system_name]["correct"] = int(profile["system_stats"][system_name]["correct"]) + 1
|
|
|
|
|
|
func _today_string() -> String:
|
|
var date := Time.get_datetime_dict_from_system()
|
|
return "%04d-%02d-%02d" % [int(date["year"]), int(date["month"]), int(date["day"])]
|