extends Node const SAVE_PATH = "user://achievements.json" const _POPUP_SCRIPT = preload("res://scripts/achievement_popup.gd") var achievements: Array = [] func _ready() -> void: _define() _load() check_all() func _define() -> void: achievements = [ { "id": "first_blood", "title": "First Blood", "description": "Kill your very first enemy.", "condition_type": "kills_total", "threshold": 1, "reward": "+10 Max HP at the start of every run", "color": Color(0.55, 0.08, 0.08), "icon_char": "I", "unlocked": false }, { "id": "slime_slayer", "title": "Slime Slayer", "description": "Kill 25 Blue Slimes.", "condition_type": "kills_blue_slime", "threshold": 25, "reward": "Shurikens pierce 1 additional enemy", "color": Color(0.15, 0.2, 0.65), "icon_char": "B", "unlocked": false }, { "id": "fire_bane", "title": "Fire Bane", "description": "Kill 50 Fire Slimes.", "condition_type": "kills_fire_slime", "threshold": 50, "reward": "Fireball permanently deals +20% more damage", "color": Color(0.75, 0.28, 0.05), "icon_char": "F", "unlocked": false }, { "id": "apprentice", "title": "Apprentice", "description": "Cast 50 spells in total.", "condition_type": "spells_total", "threshold": 50, "reward": "Cauldron brews with only 2 fruits instead of 3", "color": Color(0.45, 0.1, 0.7), "icon_char": "A", "unlocked": false }, { "id": "slime_hunter", "title": "Slime Hunter", "description": "Kill 100 enemies in total.", "condition_type": "kills_total", "threshold": 100, "reward": "Each run starts with 1 random fruit already in the cauldron", "color": Color(0.15, 0.5, 0.15), "icon_char": "H", "unlocked": false }, { "id": "survivor", "title": "Survivor", "description": "Survive 3 minutes in a single run.", "condition_type": "survive_best", "threshold": 180, "reward": "Regenerate 5 HP every 10 seconds", "color": Color(0.1, 0.4, 0.65), "icon_char": "S", "unlocked": false }, { "id": "veteran", "title": "Veteran", "description": "Play 15 runs.", "condition_type": "runs_played", "threshold": 15, "reward": "Start each run with 1 random perk already applied", "color": Color(0.6, 0.45, 0.05), "icon_char": "V", "unlocked": false }, { "id": "archmage", "title": "Archmage", "description": "Cast 300 spells in total.", "condition_type": "spells_total", "threshold": 300, "reward": "All spells permanently deal +15% more damage", "color": Color(0.25, 0.05, 0.5), "icon_char": "M", "unlocked": false }, { "id": "iron_witch", "title": "Iron Witch", "description": "Survive 7 minutes in a single run.", "condition_type": "survive_best", "threshold": 420, "reward": "+25 Max HP permanently", "color": Color(0.25, 0.35, 0.5), "icon_char": "W", "unlocked": false }, { "id": "exterminator", "title": "Exterminator", "description": "Kill 500 enemies in total.", "condition_type": "kills_total", "threshold": 500, "reward": "Level-up screen shows 4 perk cards instead of 3", "color": Color(0.65, 0.05, 0.05), "icon_char": "X", "unlocked": false }, ] func check_all() -> void: var newly_unlocked: Array = [] for ach in achievements: if ach.unlocked: continue if _meets(ach): ach.unlocked = true newly_unlocked.append(ach) if not newly_unlocked.is_empty(): _save() for ach in newly_unlocked: _show_popup(ach) func _show_popup(ach: Dictionary) -> void: var popup = _POPUP_SCRIPT.new() get_tree().root.add_child(popup) popup.show_unlock(ach) func _meets(ach: Dictionary) -> bool: var d = StatsManager.data var t: int = ach.threshold match ach.condition_type: "kills_total": var total = 0 for v in d["kills"].values(): total += v return total >= t "kills_blue_slime": return d["kills"]["blue_slime"] >= t "kills_fire_slime": return d["kills"]["fire_slime"] >= t "spells_total": var total = 0 for v in d["spells_cast"].values(): total += v return total >= t "survive_best": return d["longest_run_seconds"] >= t "runs_played": return d["runs_played"] >= t return false func reset() -> void: for ach in achievements: ach.unlocked = false _save() func get_unlocked_count() -> int: var n = 0 for ach in achievements: if ach.unlocked: n += 1 return n func _save() -> void: var ids: Array = [] for ach in achievements: if ach.unlocked: ids.append(ach.id) var f = FileAccess.open(SAVE_PATH, FileAccess.WRITE) if f == null: return f.store_string(JSON.stringify(ids)) f.close() func _load() -> void: if not FileAccess.file_exists(SAVE_PATH): return var f = FileAccess.open(SAVE_PATH, FileAccess.READ) if f == null: return var j = JSON.new() var text = f.get_as_text() f.close() if j.parse(text) != OK: return var ids = j.get_data() if typeof(ids) != TYPE_ARRAY: return for ach in achievements: ach.unlocked = ach.id in ids