diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 4600fd5..51f7c62 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -224,6 +224,9 @@ const COMMAND_HINT_PANEL_SIZE := Vector2(306, 92) const COMMAND_HINT_ICON_SIZE := Vector2(34, 34) const COMMAND_HINT_TITLE_SIZE := Vector2(242, 24) const COMMAND_HINT_DETAIL_SIZE := Vector2(242, 42) +const BATTLE_LOG_VISIBLE_LINES := 2 +const BATTLE_LOG_HISTORY_LIMIT := 18 +const BATTLE_LOG_VISIBLE_CHARS := 34 const PREP_MENU_BUTTON_SIZE := Vector2(48, 38) const PANEL_CLOSE_BUTTON_SIZE := Vector2(36, 28) const BATTLE_UNIT_LIST_PANEL_SIZE := Vector2(420, 360) @@ -679,6 +682,7 @@ var title_fullscreen_enabled := false var title_bgm_volume_percent := DEFAULT_AUDIO_VOLUME_PERCENT var title_sfx_volume_percent := DEFAULT_AUDIO_VOLUME_PERCENT var log_box: RichTextLabel +var battle_log_recent_lines: Array[String] = [] var dialogue_queue: Array = [] var active_dialogue_lines: Array = [] var active_dialogue_index := 0 @@ -4138,8 +4142,10 @@ func _create_hud() -> void: targeting_button_row.add_child(targeting_hint_cancel_move_button) log_box = RichTextLabel.new() - log_box.custom_minimum_size = Vector2(420, 70) + log_box.custom_minimum_size = Vector2(420, 82) log_box.fit_content = false + log_box.scroll_active = false + log_box.scroll_following = false _apply_rich_text_style(log_box, UI_PARCHMENT_TEXT, 13) log_box.add_theme_stylebox_override("normal", _make_panel_style(Color(0.030, 0.028, 0.024, 0.996), Color(0.22, 0.18, 0.12, 1.0), 1, 6, 8, 0)) side_column.add_child(log_box) @@ -11733,10 +11739,37 @@ func _on_state_changed() -> void: func _on_log_added(message: String) -> void: _play_log_sfx(message) _show_turn_notice_for_log(message) + _append_battle_log_entry(_format_log_entry_text(message)) + + +func _append_battle_log_entry(entry_text: String) -> void: + var normalized := entry_text.strip_edges() + if normalized.is_empty(): + return + battle_log_recent_lines.append(normalized) + while battle_log_recent_lines.size() > BATTLE_LOG_HISTORY_LIMIT: + battle_log_recent_lines.pop_front() + _refresh_battle_log_box() + + +func _clear_battle_log() -> void: + battle_log_recent_lines.clear() + _refresh_battle_log_box() + + +func _refresh_battle_log_box() -> void: if log_box == null: return - log_box.append_text(_format_log_entry_text(message) + "\n") - log_box.scroll_to_line(max(0, log_box.get_line_count() - 1)) + log_box.clear() + if battle_log_recent_lines.is_empty(): + log_box.tooltip_text = "전투 기록\n군령과 전장 변화가 여기에 짧게 남습니다." + return + var visible_lines: Array[String] = [] + var start_index := maxi(0, battle_log_recent_lines.size() - BATTLE_LOG_VISIBLE_LINES) + for index in range(start_index, battle_log_recent_lines.size()): + visible_lines.append("· %s" % _short_preview_text(battle_log_recent_lines[index], BATTLE_LOG_VISIBLE_CHARS)) + log_box.append_text(_join_strings(visible_lines, "\n")) + log_box.tooltip_text = "전투 기록\n%s" % _join_strings(battle_log_recent_lines, "\n") func _format_log_entry_text(message: String) -> String: @@ -12985,8 +13018,7 @@ func _on_restart_pressed() -> void: if campaign_complete_screen: return _play_ui_confirm() - if log_box != null: - log_box.clear() + _clear_battle_log() battle_started = false battle_start_notice_pending = false battle_result_applied = false @@ -13024,8 +13056,7 @@ func _load_pending_post_battle_choice() -> void: _play_bgm("menu") if briefing_panel != null: briefing_panel.visible = false - if log_box != null: - log_box.append_text("군의 논의가 이어지고 있다.\n") + _append_battle_log_entry("군의 논의가 이어지고 있다.") func _load_scenario(scenario_id: String) -> void: @@ -14132,8 +14163,7 @@ func _on_chapter_scenario_pressed(scenario_id: String) -> void: _play_ui_cancel() return _play_ui_confirm() - if log_box != null: - log_box.clear() + _clear_battle_log() _load_scenario(scenario_id) _show_briefing() _update_hud() @@ -15451,8 +15481,7 @@ func _load_title_slot(manual: bool) -> void: func _enter_campaign_from_title() -> void: _hide_opening_prologue() _hide_title_menu() - if log_box != null: - log_box.clear() + _clear_battle_log() if campaign_state.has_pending_post_battle_choice(): _load_pending_post_battle_choice() _update_result_panel() @@ -15849,9 +15878,8 @@ func _on_manual_load_pressed() -> void: _rebuild_save_menu() _update_top_archive_buttons() return - if log_box != null: - log_box.clear() - log_box.append_text("수동 봉인을 열었다.\n") + _clear_battle_log() + _append_battle_log_entry("수동 봉인을 열었다.") _reload_from_campaign_state() @@ -17575,8 +17603,7 @@ func _on_next_battle_pressed() -> void: if next_scenario_id.is_empty(): return _play_ui_confirm() - if log_box != null: - log_box.clear() + _clear_battle_log() _load_scenario(next_scenario_id) _show_briefing() _update_hud() @@ -17614,14 +17641,12 @@ func _show_campaign_complete() -> void: active_scenario_id = campaign_state.current_scenario_id if briefing_panel != null: briefing_panel.visible = false - if log_box != null: - log_box.append_text("전기가 여기서 매듭지어졌다.\n") + _append_battle_log_entry("전기가 여기서 매듭지어졌다.") func _on_new_campaign_pressed() -> void: _play_ui_confirm() - if log_box != null: - log_box.clear() + _clear_battle_log() if not campaign_state.reset_save(campaign_state.get_start_scenario_id()): result_panel.visible = true result_label.text = "전기 봉인을 새로 열지 못했습니다.\n저장 권한을 살펴본 뒤 다시 명하십시오." diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 52f37fc..ee7089e 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3852,6 +3852,18 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: scene._on_log_added("조조군이 동쪽 성채에 발을 들였다.") if scene.objective_notice_label == null or not scene.objective_notice_label.text.contains("거점 변화") or not scene.objective_notice_label.text.contains("성채"): failures.append("castle event log should become a battlefield notice: %s" % ("" if scene.objective_notice_label == null else scene.objective_notice_label.text)) + if scene.log_box == null: + failures.append("battle log summary missing") + else: + var visible_log_text: String = scene.log_box.get_parsed_text() + if not visible_log_text.contains("북쪽 숲 보급고") or not visible_log_text.contains("동쪽 성채"): + failures.append("battle log should show the latest two readable entries: %s" % visible_log_text) + if visible_log_text.contains("적군 차례") or visible_log_text.contains("아군 차례"): + failures.append("battle log visible text should hide older turn churn: %s" % visible_log_text) + if not scene.log_box.tooltip_text.contains("적군 차례") or not scene.log_box.tooltip_text.contains("마을 앞 유인선"): + failures.append("battle log tooltip should preserve earlier history: %s" % scene.log_box.tooltip_text) + if scene.log_box.scroll_active: + failures.append("battle log should avoid a visible scrollbar in the compact side panel") var localized_lines := scene._normalized_dialogue_lines([{"speaker": "Cao Cao", "display_speaker": "조조", "text": "군령을 전하라."}]) if localized_lines.is_empty() or str((localized_lines[0] as Dictionary).get("speaker", "")) != "조조": failures.append("dialogue normalization should preserve display_speaker for localized names")