Improve readable text backings
This commit is contained in:
@@ -132,7 +132,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
|||||||
- Campaign completion screen.
|
- Campaign completion screen.
|
||||||
- New campaign save reset.
|
- New campaign save reset.
|
||||||
- Manual campaign checkpoint save/load.
|
- Manual campaign checkpoint save/load.
|
||||||
- Cinematic title screen uses a dedicated ancient-war-camp dawn backdrop, Cao Cao portrait art, and a compact left-side command board with `처음부터`, `로드하기`, and `환경설정`; its generated command board and title buttons keep safer source-art proportions, linear filtering, tile-fit 9-slice surfaces, and aspect-locked canvas scaling for QHD fullscreen, while story/briefing action buttons use crisp flat readable surfaces. New campaigns then move into a generated four-panel Yellow Turban rebellion, Cao Cao resolve, Xiahou Dun meeting, and Yingchuan contact prologue with full-screen story artwork, a chapter seal, a text-only chapter-side story ledger instead of exposed numeric cards or tick marks, `다음 기록`/`군막으로`/`전투 준비` story controls, and a bottom scroll caption panel. Briefing, dialogue, notice, auto-turn, and right-side battle information panels now favor high-contrast flat paper/ink text surfaces, larger Korean type, and explicit line spacing over busy decorative frames. Title and in-game settings expose clearer background-music, combat-SFX, edge-scroll, and fullscreen display controls, show the active music/SFX mix beside the preview button, include one-click audio recovery, and persist preferences in `user://heros_settings.json`.
|
- Cinematic title screen uses a dedicated ancient-war-camp dawn backdrop, Cao Cao portrait art, and a compact left-side command board with `처음부터`, `로드하기`, and `환경설정`; its generated command board and title buttons keep safer source-art proportions, linear filtering, tile-fit 9-slice surfaces, and aspect-locked canvas scaling for QHD fullscreen, while story/briefing action buttons use crisp flat readable surfaces. New campaigns then move into a generated four-panel Yellow Turban rebellion, Cao Cao resolve, Xiahou Dun meeting, and Yingchuan contact prologue with full-screen story artwork, a chapter seal, a text-only chapter-side story ledger instead of exposed numeric cards or tick marks, `다음 기록`/`군막으로`/`전투 준비` story controls, and a bottom scroll caption panel. Briefing, dialogue, notice, auto-turn, command-hint, title settings/load, battle log, and right-side battle information panels now favor high-contrast flat paper/ink text surfaces, larger Korean type, and explicit line spacing over busy decorative frames. Title and in-game settings expose clearer background-music, combat-SFX, edge-scroll, and fullscreen display controls, show the active music/SFX mix beside the preview button, include one-click audio recovery, and persist preferences in `user://heros_settings.json`.
|
||||||
- Victory and defeat result overlay.
|
- Victory and defeat result overlay.
|
||||||
- Dialogue portrait slot stays fixed across speaker and narration lines, with officer default image paths, optional per-line overrides, and speaker-initial or record fallback.
|
- Dialogue portrait slot stays fixed across speaker and narration lines, with officer default image paths, optional per-line overrides, and speaker-initial or record fallback.
|
||||||
- Initial AI-generated photorealistic officer portraits for Cao Cao, Xiahou Dun, Xiahou Yuan, Cao Ren, Dian Wei, Guo Jia, and Zhang He.
|
- Initial AI-generated photorealistic officer portraits for Cao Cao, Xiahou Dun, Xiahou Yuan, Cao Ren, Dian Wei, Guo Jia, and Zhang He.
|
||||||
|
|||||||
@@ -852,9 +852,11 @@ func _panel_texture_spec(variant: String) -> Dictionary:
|
|||||||
|
|
||||||
func _panel_variant_prefers_flat_text_surface(variant: String) -> bool:
|
func _panel_variant_prefers_flat_text_surface(variant: String) -> bool:
|
||||||
return variant in [
|
return variant in [
|
||||||
|
"compact",
|
||||||
"dialogue",
|
"dialogue",
|
||||||
"speech_scroll",
|
"speech_scroll",
|
||||||
"notice_edict",
|
"notice_edict",
|
||||||
|
"target_hint",
|
||||||
"cinematic_caption",
|
"cinematic_caption",
|
||||||
"story_caption"
|
"story_caption"
|
||||||
]
|
]
|
||||||
@@ -983,14 +985,14 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void:
|
|||||||
margin = 18
|
margin = 18
|
||||||
shadow_size = 6
|
shadow_size = 6
|
||||||
"hud_info":
|
"hud_info":
|
||||||
fill = Color(0.045, 0.050, 0.048, 0.90)
|
fill = Color(0.045, 0.050, 0.048, 0.988)
|
||||||
border = Color(0.20, 0.24, 0.21, 0.96)
|
border = Color(0.20, 0.24, 0.21, 1.0)
|
||||||
border_width = 2
|
border_width = 2
|
||||||
radius = 9
|
radius = 9
|
||||||
margin = 7
|
margin = 7
|
||||||
shadow_size = 3
|
shadow_size = 3
|
||||||
"target_hint":
|
"target_hint":
|
||||||
fill = Color(0.050, 0.070, 0.062, 0.94)
|
fill = Color(0.050, 0.070, 0.062, 0.992)
|
||||||
border = UI_JADE_EDGE
|
border = UI_JADE_EDGE
|
||||||
border_width = 3
|
border_width = 3
|
||||||
radius = 10
|
radius = 10
|
||||||
@@ -1016,8 +1018,8 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void:
|
|||||||
margin = 10
|
margin = 10
|
||||||
shadow_size = 10
|
shadow_size = 10
|
||||||
"compact":
|
"compact":
|
||||||
fill = Color(0.045, 0.048, 0.044, 0.93)
|
fill = Color(0.045, 0.048, 0.044, 0.988)
|
||||||
border = UI_TARNISHED_BRONZE
|
border = Color(UI_TARNISHED_BRONZE.r, UI_TARNISHED_BRONZE.g, UI_TARNISHED_BRONZE.b, 1.0)
|
||||||
border_width = 3
|
border_width = 3
|
||||||
radius = 10
|
radius = 10
|
||||||
margin = 8
|
margin = 8
|
||||||
@@ -2329,6 +2331,32 @@ func _apply_label_style(label: Label, font_color: Color, font_size: int = 0) ->
|
|||||||
_apply_text_legibility(label, font_color, font_size)
|
_apply_text_legibility(label, font_color, font_size)
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_rich_text_style(label: RichTextLabel, font_color: Color, font_size: int = 13) -> void:
|
||||||
|
if label == null:
|
||||||
|
return
|
||||||
|
label.add_theme_color_override("default_color", font_color)
|
||||||
|
label.add_theme_color_override("font_outline_color", _text_outline_color_for(font_color))
|
||||||
|
label.add_theme_color_override("font_shadow_color", UI_TEXT_SHADOW)
|
||||||
|
label.add_theme_constant_override("outline_size", 2 if font_size < 16 else 3)
|
||||||
|
label.add_theme_constant_override("shadow_offset_x", 1)
|
||||||
|
label.add_theme_constant_override("shadow_offset_y", 1)
|
||||||
|
label.add_theme_constant_override("line_separation", 4)
|
||||||
|
var regular := _ui_font(false)
|
||||||
|
if regular != null:
|
||||||
|
label.add_theme_font_override("normal_font", regular)
|
||||||
|
label.add_theme_font_override("italics_font", regular)
|
||||||
|
label.add_theme_font_override("mono_font", regular)
|
||||||
|
var bold := _ui_font(true)
|
||||||
|
if bold != null:
|
||||||
|
label.add_theme_font_override("bold_font", bold)
|
||||||
|
label.add_theme_font_override("bold_italics_font", bold)
|
||||||
|
label.add_theme_font_size_override("normal_font_size", font_size)
|
||||||
|
label.add_theme_font_size_override("bold_font_size", font_size)
|
||||||
|
label.add_theme_font_size_override("italics_font_size", font_size)
|
||||||
|
label.add_theme_font_size_override("bold_italics_font_size", font_size)
|
||||||
|
label.add_theme_font_size_override("mono_font_size", font_size)
|
||||||
|
|
||||||
|
|
||||||
func _apply_opening_caption_text_style(label: Label, font_color: Color, font_size: int = 0) -> void:
|
func _apply_opening_caption_text_style(label: Label, font_color: Color, font_size: int = 0) -> void:
|
||||||
if label == null:
|
if label == null:
|
||||||
return
|
return
|
||||||
@@ -3920,8 +3948,8 @@ func _create_hud() -> void:
|
|||||||
log_box = RichTextLabel.new()
|
log_box = RichTextLabel.new()
|
||||||
log_box.custom_minimum_size = Vector2(420, 70)
|
log_box.custom_minimum_size = Vector2(420, 70)
|
||||||
log_box.fit_content = false
|
log_box.fit_content = false
|
||||||
log_box.add_theme_color_override("default_color", UI_PARCHMENT_TEXT)
|
_apply_rich_text_style(log_box, UI_PARCHMENT_TEXT, 13)
|
||||||
log_box.add_theme_stylebox_override("normal", _make_panel_style(Color(0.11, 0.07, 0.045, 0.78), UI_RICE_PAPER_DARK, 1, 6, 6, 0))
|
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)
|
side_column.add_child(log_box)
|
||||||
|
|
||||||
_create_command_hint_panel(root)
|
_create_command_hint_panel(root)
|
||||||
@@ -4802,7 +4830,7 @@ func _create_hud() -> void:
|
|||||||
title_load_panel = PanelContainer.new()
|
title_load_panel = PanelContainer.new()
|
||||||
title_load_panel.visible = false
|
title_load_panel.visible = false
|
||||||
title_load_panel.custom_minimum_size = Vector2(390, 188)
|
title_load_panel.custom_minimum_size = Vector2(390, 188)
|
||||||
_apply_panel_style(title_load_panel, "hud_info")
|
_apply_panel_style(title_load_panel, "hud_readable")
|
||||||
title_menu_column.add_child(title_load_panel)
|
title_menu_column.add_child(title_load_panel)
|
||||||
|
|
||||||
var title_load_column := VBoxContainer.new()
|
var title_load_column := VBoxContainer.new()
|
||||||
@@ -4856,7 +4884,7 @@ func _create_hud() -> void:
|
|||||||
title_settings_panel = PanelContainer.new()
|
title_settings_panel = PanelContainer.new()
|
||||||
title_settings_panel.visible = false
|
title_settings_panel.visible = false
|
||||||
title_settings_panel.custom_minimum_size = Vector2(390, 362)
|
title_settings_panel.custom_minimum_size = Vector2(390, 362)
|
||||||
_apply_panel_style(title_settings_panel, "hud_info")
|
_apply_panel_style(title_settings_panel, "hud_readable")
|
||||||
title_menu_column.add_child(title_settings_panel)
|
title_menu_column.add_child(title_settings_panel)
|
||||||
|
|
||||||
var title_settings_column := VBoxContainer.new()
|
var title_settings_column := VBoxContainer.new()
|
||||||
|
|||||||
@@ -3692,6 +3692,7 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
_check_top_hud_chip(failures, scene, "TopStatusChip", scene.status_label, "군령")
|
_check_top_hud_chip(failures, scene, "TopStatusChip", scene.status_label, "군령")
|
||||||
_check_top_hud_chip(failures, scene, "TopObjectiveChip", scene.objective_label, "승리:")
|
_check_top_hud_chip(failures, scene, "TopObjectiveChip", scene.objective_label, "승리:")
|
||||||
_check_top_hud_chip(failures, scene, "TopCampaignChip", scene.campaign_status_label, "군자금")
|
_check_top_hud_chip(failures, scene, "TopCampaignChip", scene.campaign_status_label, "군자금")
|
||||||
|
_check_panel_uses_flat_readable_backing(failures, _find_descendant_by_name(scene.ui_root_control, "TopHudBar") as PanelContainer, "top HUD compact text backing")
|
||||||
var mission_text := scene._format_mission_panel_text()
|
var mission_text := scene._format_mission_panel_text()
|
||||||
for expected in ["승리:", "패배:", "진행:", "주의:"]:
|
for expected in ["승리:", "패배:", "진행:", "주의:"]:
|
||||||
if not mission_text.contains(expected):
|
if not mission_text.contains(expected):
|
||||||
@@ -3741,6 +3742,10 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
_check_panel_style_frame(failures, scene.cell_info_panel, "HUD terrain info panel", Color(0.42, 0.35, 0.24, 0.98), 2)
|
_check_panel_style_frame(failures, scene.cell_info_panel, "HUD terrain info panel", Color(0.42, 0.35, 0.24, 0.98), 2)
|
||||||
_check_panel_style_fill(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.060, 0.056, 0.048, 0.982))
|
_check_panel_style_fill(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.060, 0.056, 0.048, 0.982))
|
||||||
_check_panel_style_frame(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.42, 0.35, 0.24, 0.98), 2)
|
_check_panel_style_frame(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.42, 0.35, 0.24, 0.98), 2)
|
||||||
|
_check_panel_uses_flat_readable_backing(failures, scene.title_load_panel, "title load readable panel")
|
||||||
|
_check_panel_uses_flat_readable_backing(failures, scene.title_settings_panel, "title settings readable panel")
|
||||||
|
_check_panel_uses_flat_readable_backing(failures, _find_descendant_by_name(scene.ui_root_control, "SettingsPanel") as PanelContainer, "battle settings readable panel")
|
||||||
|
_check_rich_text_readability(failures, scene.log_box, "battle log text")
|
||||||
if scene.cell_info_icon == null:
|
if scene.cell_info_icon == null:
|
||||||
failures.append("HUD terrain info panel should expose an icon chip")
|
failures.append("HUD terrain info panel should expose an icon chip")
|
||||||
if scene.forecast_icon == null:
|
if scene.forecast_icon == null:
|
||||||
@@ -4331,6 +4336,47 @@ func _check_panel_uses_flat_text_surface(failures: Array[String], panel: PanelCo
|
|||||||
failures.append("%s should reserve breathing room around readable text" % label)
|
failures.append("%s should reserve breathing room around readable text" % label)
|
||||||
|
|
||||||
|
|
||||||
|
func _check_panel_uses_flat_readable_backing(failures: Array[String], panel: PanelContainer, label: String) -> void:
|
||||||
|
if panel == null:
|
||||||
|
failures.append("%s missing" % label)
|
||||||
|
return
|
||||||
|
var stylebox := panel.get_theme_stylebox("panel")
|
||||||
|
if not (stylebox is StyleBoxFlat):
|
||||||
|
failures.append("%s should use a flat readable backing behind Korean text" % label)
|
||||||
|
return
|
||||||
|
if bool(panel.get_meta("generated_panel_texture", false)):
|
||||||
|
failures.append("%s should not mark a busy generated texture behind text" % label)
|
||||||
|
var style := stylebox as StyleBoxFlat
|
||||||
|
if style.bg_color.a < 0.98:
|
||||||
|
failures.append("%s should keep an opaque backing: %s" % [label, str(style.bg_color)])
|
||||||
|
if style.bg_color.get_luminance() > 0.18 and style.bg_color.a < 0.995:
|
||||||
|
failures.append("%s bright text backing should be almost opaque: %s" % [label, str(style.bg_color)])
|
||||||
|
if style.content_margin_left < 6 or style.content_margin_top < 6:
|
||||||
|
failures.append("%s should keep at least compact text padding" % label)
|
||||||
|
|
||||||
|
|
||||||
|
func _check_rich_text_readability(failures: Array[String], label: RichTextLabel, context: String) -> void:
|
||||||
|
if label == null:
|
||||||
|
failures.append("%s missing" % context)
|
||||||
|
return
|
||||||
|
var stylebox := label.get_theme_stylebox("normal")
|
||||||
|
if not (stylebox is StyleBoxFlat):
|
||||||
|
failures.append("%s should use a flat text log backing" % context)
|
||||||
|
else:
|
||||||
|
var style := stylebox as StyleBoxFlat
|
||||||
|
if style.bg_color.a < 0.98:
|
||||||
|
failures.append("%s backing should be opaque enough: %s" % [context, str(style.bg_color)])
|
||||||
|
var text_color := label.get_theme_color("default_color")
|
||||||
|
if text_color.a < 0.95 or text_color.get_luminance() < 0.70:
|
||||||
|
failures.append("%s should use bright readable default text: %s" % [context, str(text_color)])
|
||||||
|
if label.get_theme_font_size("normal_font_size") < 13:
|
||||||
|
failures.append("%s should keep readable Korean log size: %d" % [context, label.get_theme_font_size("normal_font_size")])
|
||||||
|
if label.get_theme_constant("outline_size") < 2:
|
||||||
|
failures.append("%s should keep an outline for text over dark UI" % context)
|
||||||
|
if label.get_theme_font("normal_font") == null:
|
||||||
|
failures.append("%s should use the project serif font" % context)
|
||||||
|
|
||||||
|
|
||||||
func _check_panel_uses_generated_ornament_texture(failures: Array[String], panel: PanelContainer, label: String) -> void:
|
func _check_panel_uses_generated_ornament_texture(failures: Array[String], panel: PanelContainer, label: String) -> void:
|
||||||
if panel == null:
|
if panel == null:
|
||||||
failures.append("%s missing" % label)
|
failures.append("%s missing" % label)
|
||||||
@@ -4676,8 +4722,9 @@ func _check_hud_command_grid_layout(failures: Array[String]) -> void:
|
|||||||
failures.append("unit command hint should show command title: %s" % ("" if scene.command_hint_title_label == null else scene.command_hint_title_label.text))
|
failures.append("unit command hint should show command title: %s" % ("" if scene.command_hint_title_label == null else scene.command_hint_title_label.text))
|
||||||
if scene.command_hint_detail_label == null or not scene.command_hint_detail_label.text.contains("부대 미지정"):
|
if scene.command_hint_detail_label == null or not scene.command_hint_detail_label.text.contains("부대 미지정"):
|
||||||
failures.append("disabled unit command hint should show the blocked reason: %s" % ("" if scene.command_hint_detail_label == null else scene.command_hint_detail_label.text))
|
failures.append("disabled unit command hint should show the blocked reason: %s" % ("" if scene.command_hint_detail_label == null else scene.command_hint_detail_label.text))
|
||||||
if scene.command_hint_icon == null or scene.command_hint_icon.texture == null:
|
if scene.command_hint_icon == null or scene.command_hint_icon.texture == null:
|
||||||
failures.append("unit command hint should show generated command icon artwork")
|
failures.append("unit command hint should show generated command icon artwork")
|
||||||
|
_check_panel_uses_flat_readable_backing(failures, scene.command_hint_panel, "unit command hint readable panel")
|
||||||
scene._on_command_hint_button_mouse_exited()
|
scene._on_command_hint_button_mouse_exited()
|
||||||
if scene.command_hint_panel != null and scene.command_hint_panel.visible:
|
if scene.command_hint_panel != null and scene.command_hint_panel.visible:
|
||||||
failures.append("unit command hint panel should hide when leaving the icon")
|
failures.append("unit command hint panel should hide when leaving the icon")
|
||||||
@@ -4714,6 +4761,7 @@ func _check_hud_command_grid_layout(failures: Array[String]) -> void:
|
|||||||
failures.append("top command hint should keep command detail: %s" % ("" if scene.command_hint_detail_label == null else scene.command_hint_detail_label.text))
|
failures.append("top command hint should keep command detail: %s" % ("" if scene.command_hint_detail_label == null else scene.command_hint_detail_label.text))
|
||||||
if scene.command_hint_icon == null or scene.command_hint_icon.texture == null or str(scene.end_turn_button.get_meta("command_icon", "")) != "end_turn":
|
if scene.command_hint_icon == null or scene.command_hint_icon.texture == null or str(scene.end_turn_button.get_meta("command_icon", "")) != "end_turn":
|
||||||
failures.append("top command hint should use generated top command icon artwork")
|
failures.append("top command hint should use generated top command icon artwork")
|
||||||
|
_check_panel_uses_flat_readable_backing(failures, scene.command_hint_panel, "top command hint readable panel")
|
||||||
scene.free()
|
scene.free()
|
||||||
|
|
||||||
|
|
||||||
@@ -4743,8 +4791,8 @@ func _check_battle_unit_list_panel(failures: Array[String]) -> void:
|
|||||||
if scene.battle_unit_list_panel == null or not scene.battle_unit_list_panel.visible:
|
if scene.battle_unit_list_panel == null or not scene.battle_unit_list_panel.visible:
|
||||||
failures.append("battle unit list panel should open from the top toolbar")
|
failures.append("battle unit list panel should open from the top toolbar")
|
||||||
else:
|
else:
|
||||||
_check_panel_style_fill(failures, scene.battle_unit_list_panel, "battle unit list panel", Color(0.045, 0.048, 0.044, 0.93))
|
_check_panel_style_fill(failures, scene.battle_unit_list_panel, "battle unit list panel", Color(0.045, 0.048, 0.044, 0.988))
|
||||||
_check_panel_uses_generated_texture(failures, scene.battle_unit_list_panel, "battle unit list generated frame")
|
_check_panel_uses_flat_readable_backing(failures, scene.battle_unit_list_panel, "battle unit list readable panel")
|
||||||
var header_icon := _find_descendant_by_name(scene.battle_unit_list_panel, "BattleUnitListHeaderIcon")
|
var header_icon := _find_descendant_by_name(scene.battle_unit_list_panel, "BattleUnitListHeaderIcon")
|
||||||
if header_icon == null or not header_icon is TextureRect or (header_icon as TextureRect).texture == null:
|
if header_icon == null or not header_icon is TextureRect or (header_icon as TextureRect).texture == null:
|
||||||
failures.append("battle unit list header should carry generated unit-list icon artwork")
|
failures.append("battle unit list header should carry generated unit-list icon artwork")
|
||||||
|
|||||||
Reference in New Issue
Block a user