Add chapter overview briefing menu

This commit is contained in:
2026-06-18 04:05:30 +09:00
parent f0d941c273
commit 4c3621bc85
7 changed files with 141 additions and 8 deletions

View File

@@ -650,6 +650,33 @@ func get_scenario_count_in_chapter(scenario_id: String) -> int:
return scenario_ids.size()
func get_chapter_progress_entries() -> Array:
var entries := []
for chapter_index in range(chapter_order.size()):
var chapter_id := chapter_order[chapter_index]
var scenario_ids: Array = chapter_scenario_ids.get(chapter_id, [])
var completed_count := 0
var current_in_chapter := false
var current_scenario_title := ""
for scenario_id in scenario_ids:
if completed_scenarios.has(scenario_id):
completed_count += 1
if scenario_id == current_scenario_id:
current_in_chapter = true
current_scenario_title = get_scenario_title(scenario_id)
entries.append({
"id": chapter_id,
"title": str(chapter_titles.get(chapter_id, chapter_id)),
"position": chapter_index + 1,
"chapter_count": chapter_order.size(),
"completed": completed_count,
"total": scenario_ids.size(),
"current": current_in_chapter,
"current_scenario_title": current_scenario_title
})
return entries
func get_next_scenario_id(scenario_id: String) -> String:
var index := scenario_order.find(scenario_id)
if index == -1 or index + 1 >= scenario_order.size():

View File

@@ -65,6 +65,10 @@ var briefing_title_label: Label
var briefing_location_label: Label
var briefing_objective_label: Label
var briefing_label: Label
var chapter_button: Button
var chapter_menu: VBoxContainer
var chapter_list: VBoxContainer
var chapter_status_label: Label
var shop_button: Button
var shop_menu: VBoxContainer
var shop_buy_button: Button
@@ -354,6 +358,11 @@ func _create_hud() -> void:
prep_button_row.add_theme_constant_override("separation", 8)
briefing_column.add_child(prep_button_row)
chapter_button = Button.new()
chapter_button.text = "Chapters"
chapter_button.pressed.connect(_on_chapter_pressed)
prep_button_row.add_child(chapter_button)
shop_button = Button.new()
shop_button.text = "Shop"
shop_button.pressed.connect(_on_shop_pressed)
@@ -374,6 +383,25 @@ func _create_hud() -> void:
formation_button.pressed.connect(_on_formation_pressed)
prep_button_row.add_child(formation_button)
chapter_menu = VBoxContainer.new()
chapter_menu.visible = false
chapter_menu.add_theme_constant_override("separation", 6)
briefing_column.add_child(chapter_menu)
chapter_status_label = Label.new()
chapter_status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
chapter_status_label.custom_minimum_size = Vector2(600, 22)
chapter_menu.add_child(chapter_status_label)
var chapter_scroll := ScrollContainer.new()
chapter_scroll.custom_minimum_size = Vector2(600, 190)
chapter_menu.add_child(chapter_scroll)
chapter_list = VBoxContainer.new()
chapter_list.custom_minimum_size = Vector2(580, 0)
chapter_list.add_theme_constant_override("separation", 4)
chapter_scroll.add_child(chapter_list)
shop_menu = VBoxContainer.new()
shop_menu.visible = false
shop_menu.add_theme_constant_override("separation", 6)
@@ -757,7 +785,9 @@ func _handle_key(event: InputEventKey) -> void:
return
_on_begin_battle_pressed()
elif event.keycode == KEY_ESCAPE:
if shop_menu != null and shop_menu.visible:
if chapter_menu != null and chapter_menu.visible:
_hide_chapter_menu()
elif shop_menu != null and shop_menu.visible:
_hide_shop_menu()
elif armory_menu != null and armory_menu.visible:
_hide_armory_menu()
@@ -1823,6 +1853,7 @@ func _load_scenario(scenario_id: String) -> void:
_hide_tactic_menu()
_hide_item_menu()
_hide_equip_menu()
_hide_chapter_menu()
_hide_shop_menu()
_hide_armory_menu()
_hide_roster_menu()
@@ -1915,11 +1946,14 @@ func _show_briefing() -> void:
briefing_label.text = body
briefing_panel.visible = true
battle_started = false
_hide_chapter_menu()
_hide_shop_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
var prep_locked := _is_prebattle_prep_locked()
if chapter_button != null:
chapter_button.disabled = campaign_state.get_chapter_progress_entries().is_empty()
if shop_button != null:
shop_button.disabled = prep_locked or (state.get_shop_item_ids().is_empty() and _get_sellable_item_ids().is_empty())
if armory_button != null:
@@ -1940,6 +1974,7 @@ func _start_battle_from_briefing() -> void:
_play_bgm("battle")
if briefing_panel != null:
briefing_panel.visible = false
_hide_chapter_menu()
_hide_shop_menu()
_hide_armory_menu()
_hide_roster_menu()
@@ -1949,6 +1984,69 @@ func _start_battle_from_briefing() -> void:
_update_hud()
func _on_chapter_pressed() -> void:
if battle_started or briefing_panel == null or not briefing_panel.visible:
return
_play_ui_click()
if chapter_menu != null and chapter_menu.visible:
_hide_chapter_menu()
else:
_hide_shop_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
_rebuild_chapter_menu()
chapter_menu.visible = true
_update_hud()
func _hide_chapter_menu() -> void:
if chapter_menu == null:
return
chapter_menu.visible = false
func _rebuild_chapter_menu() -> void:
if chapter_list == null:
return
for child in chapter_list.get_children():
chapter_list.remove_child(child)
child.queue_free()
var entries := campaign_state.get_chapter_progress_entries()
if chapter_status_label != null:
chapter_status_label.text = "%s chapters | %d/%d battles complete" % [
campaign_state.campaign_title,
campaign_state.completed_scenarios.size(),
campaign_state.get_scenario_count()
]
if entries.is_empty():
var empty_label := Label.new()
empty_label.text = "No campaign chapters are defined."
chapter_list.add_child(empty_label)
return
for entry in entries:
var completed := int(entry.get("completed", 0))
var total := int(entry.get("total", 0))
var current := bool(entry.get("current", false))
var status := "Current" if current else ("Complete" if total > 0 and completed >= total else "Upcoming")
var current_title := str(entry.get("current_scenario_title", ""))
var line := "Chapter %02d/%02d - %s [%s] %d/%d" % [
int(entry.get("position", 0)),
int(entry.get("chapter_count", 0)),
entry.get("title", ""),
status,
completed,
total
]
if current and not current_title.is_empty():
line += "\nCurrent battle: %s" % current_title
var chapter_label := Label.new()
chapter_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
chapter_label.custom_minimum_size = Vector2(560, 0)
chapter_label.text = line
chapter_list.add_child(chapter_label)
func _on_shop_pressed() -> void:
if battle_started or _is_prebattle_prep_locked() or briefing_panel == null or not briefing_panel.visible:
return
@@ -1957,6 +2055,7 @@ func _on_shop_pressed() -> void:
_hide_shop_menu()
else:
shop_sell_mode = state.get_shop_item_ids().is_empty() and not _get_sellable_item_ids().is_empty()
_hide_chapter_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
@@ -1978,6 +2077,7 @@ func _on_armory_pressed() -> void:
if armory_menu != null and armory_menu.visible:
_hide_armory_menu()
else:
_hide_chapter_menu()
_hide_shop_menu()
_hide_roster_menu()
_hide_formation_menu()
@@ -1999,6 +2099,7 @@ func _on_roster_pressed() -> void:
if roster_menu != null and roster_menu.visible:
_hide_roster_menu()
else:
_hide_chapter_menu()
_hide_shop_menu()
_hide_armory_menu()
_hide_formation_menu()
@@ -2020,6 +2121,7 @@ func _on_formation_pressed() -> void:
if formation_menu != null and formation_menu.visible:
_hide_formation_menu()
else:
_hide_chapter_menu()
_hide_shop_menu()
_hide_armory_menu()
_hide_roster_menu()
@@ -2037,7 +2139,8 @@ func _hide_formation_menu() -> void:
func _is_prep_menu_visible() -> bool:
return (
(shop_menu != null and shop_menu.visible)
(chapter_menu != null and chapter_menu.visible)
or (shop_menu != null and shop_menu.visible)
or (armory_menu != null and armory_menu.visible)
or (roster_menu != null and roster_menu.visible)
or (formation_menu != null and formation_menu.visible)
@@ -2764,6 +2867,7 @@ func _show_campaign_complete() -> void:
_hide_tactic_menu()
_hide_item_menu()
_hide_equip_menu()
_hide_chapter_menu()
_hide_shop_menu()
_hide_armory_menu()
_hide_roster_menu()
@@ -2799,6 +2903,7 @@ func _on_new_campaign_pressed() -> void:
_hide_tactic_menu()
_hide_item_menu()
_hide_equip_menu()
_hide_chapter_menu()
_hide_roster_menu()
_load_current_battle()
_show_briefing()