extends Control class_name QuestJournal enum Tab { MAIN, SIDE, COMPLETED } const QUEST_ENTRY_SCENE := preload("res://game/quest_journal/quest_entry.tscn") const OBJECTIVE_ROW_SCENE := preload("res://game/quest_journal/objective_row.tscn") const REWARD_ROW_SCENE := preload("res://game/quest_journal/reward_row.tscn") @onready var tab_bar: TabBar = $MarginContainer/HBoxContainer/LeftPanel/TabBar @onready var quest_list: VBoxContainer = $MarginContainer/HBoxContainer/LeftPanel/ScrollContainer/QuestList @onready var footer_label: Label = $MarginContainer/HBoxContainer/LeftPanel/FooterLabel @onready var detail_title: Label = $MarginContainer/HBoxContainer/RightPanel/DetailScroll/DetailContent/DetailTitle @onready var detail_category: Label = $MarginContainer/HBoxContainer/RightPanel/DetailScroll/DetailContent/DetailCategory @onready var detail_description: RichTextLabel = $MarginContainer/HBoxContainer/RightPanel/DetailScroll/DetailContent/DetailDescription @onready var objectives_list: VBoxContainer = $MarginContainer/HBoxContainer/RightPanel/DetailScroll/DetailContent/ObjectivesList @onready var rewards_list: VBoxContainer = $MarginContainer/HBoxContainer/RightPanel/DetailScroll/DetailContent/RewardsList var _current_tab: Tab = Tab.MAIN var _selected_quest_id: StringName = &"" func _ready() -> void: tab_bar.tab_changed.connect(_on_tab_changed) GameEvents.quest_started.connect(_on_quest_changed) GameEvents.quest_completed.connect(_on_quest_changed) GameEvents.quest_objective_updated.connect(_on_objective_updated) refresh() func _on_tab_changed(index: int) -> void: _current_tab = index as Tab refresh() func _on_quest_changed(_id: StringName) -> void: refresh() func _on_objective_updated(_qid: StringName, _idx: int, _cur: int, _req: int) -> void: refresh() # simple et suffisant # --- Rafraîchissement global --- func refresh() -> void: _clear_list() var quests := _get_quests_for_tab(_current_tab) if quests.is_empty(): _show_empty_state() _clear_detail() return # Tri : principales par chapter_index, secondaires par titre if _current_tab == Tab.MAIN: quests.sort_custom(func(a, b): return a.chapter_index < b.chapter_index) else: quests.sort_custom(func(a, b): return a.title < b.title) for quest in quests: var entry := QUEST_ENTRY_SCENE.instantiate() entry.setup(quest, _current_tab == Tab.COMPLETED) entry.quest_selected.connect(_on_quest_selected) quest_list.add_child(entry) # Auto-sélection : garde la sélection actuelle si elle est toujours valide var still_valid := quests.any(func(q): return q.quest_id == _selected_quest_id) if still_valid: _on_quest_selected(_selected_quest_id) elif quests.size() > 0: _on_quest_selected(quests[0].quest_id) else: _clear_detail() _update_footer() func _get_quests_for_tab(tab: Tab) -> Array[QuestData]: var result: Array[QuestData] = [] match tab: Tab.MAIN: for q in QuestManager.active_quests.values(): if q.category == QuestData.Category.MAIN: result.append(q) Tab.SIDE: for q in QuestManager.active_quests.values(): if q.category == QuestData.Category.SIDE: result.append(q) Tab.COMPLETED: for q in QuestManager.completed_quests.values(): result.append(q) return result func _clear_list() -> void: for child in quest_list.get_children(): child.queue_free() func _show_empty_state() -> void: var label := Label.new() label.text = "Aucune quête dans cette catégorie." label.add_theme_color_override("font_color", Color(0.6, 0.6, 0.6)) label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER quest_list.add_child(label) func _update_footer() -> void: var main_count := 0 var side_count := 0 for q in QuestManager.active_quests.values(): if q.category == QuestData.Category.MAIN: main_count += 1 elif q.category == QuestData.Category.SIDE: side_count += 1 footer_label.text = "Principales : %d | Secondaires : %d | Terminées : %d" % [ main_count, side_count, QuestManager.completed_quests.size() ] # --- Détail d'une quête --- func _on_quest_selected(quest_id: StringName) -> void: _selected_quest_id = quest_id var quest: QuestData = QuestManager.all_quests.get(quest_id) if quest == null: _clear_detail() return detail_title.text = quest.title detail_category.text = _category_label(quest.category) detail_description.text = quest.description # Objectifs for child in objectives_list.get_children(): child.queue_free() for obj in quest.objectives: var row := OBJECTIVE_ROW_SCENE.instantiate() row.setup(obj, _current_tab == Tab.COMPLETED) objectives_list.add_child(row) # Récompenses for child in rewards_list.get_children(): child.queue_free() for reward in quest.rewards: var row := REWARD_ROW_SCENE.instantiate() row.setup(reward) rewards_list.add_child(row) func _clear_detail() -> void: detail_title.text = "" detail_category.text = "" detail_description.text = "" for child in objectives_list.get_children(): child.queue_free() for child in rewards_list.get_children(): child.queue_free() func _category_label(cat: QuestData.Category) -> String: match cat: QuestData.Category.MAIN: return "QUÊTE PRINCIPALE" QuestData.Category.SIDE: return "QUÊTE SECONDAIRE" QuestData.Category.HIDDEN: return "QUÊTE SECRÈTE" return ""