Add selectable camp conversations

This commit is contained in:
2026-06-18 17:10:27 +09:00
parent d723d34a32
commit 26741b97dd
9 changed files with 315 additions and 21 deletions

View File

@@ -243,10 +243,42 @@ func _normalized_briefing(source) -> Dictionary:
for line in block.get("lines", []):
lines.append(str(line))
result["lines"] = lines
result["camp_dialogue"] = _normalized_dialogue_lines(result.get("camp_dialogue", []))
result["camp_conversations"] = _normalized_camp_conversations(result.get("camp_conversations", []))
result.erase("conditional_lines")
return result
func _normalized_camp_conversations(source) -> Array:
var result := []
if typeof(source) != TYPE_ARRAY:
return result
var seen := {}
for entry in source:
if typeof(entry) != TYPE_DICTIONARY:
continue
var conversation_id := str(entry.get("id", "")).strip_edges()
if conversation_id.is_empty() or seen.has(conversation_id):
continue
var lines := _normalized_dialogue_lines(entry.get("lines", []))
if lines.is_empty():
continue
seen[conversation_id] = true
var label := str(entry.get("label", conversation_id.capitalize())).strip_edges()
if label.is_empty():
label = conversation_id.capitalize()
result.append({
"id": conversation_id,
"group": str(entry.get("group", "topic")).strip_edges(),
"officer_id": str(entry.get("officer_id", "")).strip_edges(),
"label": label,
"speaker": str(entry.get("speaker", "")).strip_edges(),
"summary": str(entry.get("summary", "")).strip_edges(),
"lines": lines
})
return result
func _is_condition_shape_valid(condition) -> bool:
return typeof(condition) == TYPE_DICTIONARY or typeof(condition) == TYPE_ARRAY

View File

@@ -98,6 +98,9 @@ var chapter_list: VBoxContainer
var chapter_status_label: Label
var shop_button: Button
var talk_button: Button
var talk_menu: VBoxContainer
var talk_list: VBoxContainer
var talk_status_label: Label
var shop_menu: VBoxContainer
var shop_buy_button: Button
var shop_sell_button: Button
@@ -552,6 +555,25 @@ func _create_hud() -> void:
shop_list.add_theme_constant_override("separation", 4)
shop_scroll.add_child(shop_list)
talk_menu = VBoxContainer.new()
talk_menu.visible = false
talk_menu.add_theme_constant_override("separation", 6)
briefing_column.add_child(talk_menu)
talk_status_label = Label.new()
talk_status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
talk_status_label.custom_minimum_size = Vector2(600, 22)
talk_menu.add_child(talk_status_label)
var talk_scroll := ScrollContainer.new()
talk_scroll.custom_minimum_size = Vector2(600, 150)
talk_menu.add_child(talk_scroll)
talk_list = VBoxContainer.new()
talk_list.custom_minimum_size = Vector2(580, 0)
talk_list.add_theme_constant_override("separation", 4)
talk_scroll.add_child(talk_list)
armory_menu = VBoxContainer.new()
armory_menu.visible = false
armory_menu.add_theme_constant_override("separation", 6)
@@ -2681,6 +2703,7 @@ func _load_scenario(scenario_id: String) -> void:
_hide_equip_menu()
_hide_chapter_menu()
_hide_shop_menu()
_hide_talk_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
@@ -2812,6 +2835,7 @@ func _show_briefing() -> void:
battle_started = false
_hide_chapter_menu()
_hide_shop_menu()
_hide_talk_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
@@ -2822,7 +2846,7 @@ func _show_briefing() -> void:
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 talk_button != null:
talk_button.disabled = prep_locked or _camp_dialogue_lines().is_empty()
talk_button.disabled = prep_locked or _camp_conversation_entries().is_empty()
if armory_button != null:
armory_button.disabled = prep_locked or state.get_controllable_player_units().is_empty()
if roster_button != null:
@@ -2845,6 +2869,7 @@ func _start_battle_from_briefing() -> void:
briefing_panel.visible = false
_hide_chapter_menu()
_hide_shop_menu()
_hide_talk_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
@@ -2862,6 +2887,7 @@ func _on_chapter_pressed() -> void:
_hide_chapter_menu()
else:
_hide_shop_menu()
_hide_talk_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
@@ -2969,6 +2995,7 @@ func _on_shop_pressed() -> void:
else:
shop_sell_mode = state.get_shop_item_ids().is_empty() and not _get_sellable_item_ids().is_empty()
_hide_chapter_menu()
_hide_talk_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
@@ -2987,27 +3014,104 @@ func _hide_shop_menu() -> void:
func _on_talk_pressed() -> void:
if battle_started or _is_prebattle_prep_locked() or briefing_panel == null or not briefing_panel.visible:
return
var lines := _camp_dialogue_lines()
if lines.is_empty():
if _camp_conversation_entries().is_empty():
_play_ui_cancel()
return
_play_ui_confirm()
_hide_chapter_menu()
_hide_shop_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
_hide_save_menu()
_show_camp_dialogue(lines)
_play_ui_click()
if talk_menu != null and talk_menu.visible:
_hide_talk_menu()
else:
_hide_chapter_menu()
_hide_shop_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
_hide_save_menu()
_rebuild_talk_menu()
talk_menu.visible = true
_update_hud()
func _camp_dialogue_lines() -> Array:
func _hide_talk_menu() -> void:
if talk_menu == null:
return
talk_menu.visible = false
func _rebuild_talk_menu() -> void:
if talk_list == null:
return
_clear_talk_list()
var conversations := _camp_conversation_entries()
if talk_status_label != null:
talk_status_label.text = "Camp conversations | %d available" % conversations.size()
if conversations.is_empty():
var empty_label := Label.new()
empty_label.text = "No camp conversations"
talk_list.add_child(empty_label)
return
for conversation in conversations:
var conversation_id := str(conversation.get("id", ""))
var button := Button.new()
button.text = _format_camp_conversation_button_text(conversation)
button.pressed.connect(_on_camp_conversation_pressed.bind(conversation_id))
talk_list.add_child(button)
func _clear_talk_list() -> void:
for child in talk_list.get_children():
talk_list.remove_child(child)
child.queue_free()
func _camp_conversation_entries() -> Array:
var briefing := state.get_briefing()
var conversations = briefing.get("camp_conversations", [])
if typeof(conversations) == TYPE_ARRAY and not conversations.is_empty():
return conversations
var lines = briefing.get("camp_dialogue", [])
if typeof(lines) != TYPE_ARRAY:
return []
return lines
if typeof(lines) == TYPE_ARRAY and not lines.is_empty():
return [{
"id": "camp_dialogue",
"group": "topic",
"officer_id": "",
"label": "War Council",
"speaker": "",
"summary": "Review the immediate situation.",
"lines": lines
}]
return []
func _format_camp_conversation_button_text(conversation: Dictionary) -> String:
var label := str(conversation.get("label", "Camp Talk"))
var speaker := str(conversation.get("speaker", ""))
var summary := str(conversation.get("summary", ""))
var group := str(conversation.get("group", "topic")).capitalize()
var parts := [group, label]
if not speaker.is_empty():
parts.append(speaker)
if not summary.is_empty():
parts.append(summary)
return _join_strings(parts, " - ")
func _on_camp_conversation_pressed(conversation_id: String) -> void:
var conversation := _camp_conversation_by_id(conversation_id)
if conversation.is_empty():
_play_ui_cancel()
return
_play_ui_confirm()
_hide_talk_menu()
_show_camp_dialogue(conversation.get("lines", []))
_update_hud()
func _camp_conversation_by_id(conversation_id: String) -> Dictionary:
for conversation in _camp_conversation_entries():
if str(conversation.get("id", "")) == conversation_id:
return conversation
return {}
func _show_camp_dialogue(lines: Array) -> void:
@@ -3029,6 +3133,7 @@ func _on_armory_pressed() -> void:
else:
_hide_chapter_menu()
_hide_shop_menu()
_hide_talk_menu()
_hide_roster_menu()
_hide_formation_menu()
_hide_save_menu()
@@ -3052,6 +3157,7 @@ func _on_roster_pressed() -> void:
else:
_hide_chapter_menu()
_hide_shop_menu()
_hide_talk_menu()
_hide_armory_menu()
_hide_formation_menu()
_hide_save_menu()
@@ -3075,6 +3181,7 @@ func _on_formation_pressed() -> void:
else:
_hide_chapter_menu()
_hide_shop_menu()
_hide_talk_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_save_menu()
@@ -3099,6 +3206,7 @@ func _on_save_pressed() -> void:
else:
_hide_chapter_menu()
_hide_shop_menu()
_hide_talk_menu()
_hide_armory_menu()
_hide_roster_menu()
_hide_formation_menu()
@@ -3215,6 +3323,7 @@ func _is_prep_menu_visible() -> bool:
return (
(chapter_menu != null and chapter_menu.visible)
or (shop_menu != null and shop_menu.visible)
or (talk_menu != null and talk_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)