Add generated UI panel frames
This commit is contained in:
@@ -37,7 +37,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
|||||||
- Pre-battle prep commands use compact icon buttons with active selection state and Korean hover labels for chapters, shop, talks, armory, roster, formation, and save records.
|
- Pre-battle prep commands use compact icon buttons with active selection state and Korean hover labels for chapters, shop, talks, armory, roster, formation, and save records.
|
||||||
- Battle, top-toolbar, and pre-battle command buttons now prefer generated high-resolution bronze/parchment icon art under `art/ui/icons`, with procedural icon drawing kept only as a fallback.
|
- Battle, top-toolbar, and pre-battle command buttons now prefer generated high-resolution bronze/parchment icon art under `art/ui/icons`, with procedural icon drawing kept only as a fallback.
|
||||||
- Battle maps blend generated terrain texture tiles for plains, forests, hills, wasteland, roads, water, villages, and castles over the high-resolution battlefield backdrop.
|
- Battle maps blend generated terrain texture tiles for plains, forests, hills, wasteland, roads, water, villages, and castles over the high-resolution battlefield backdrop.
|
||||||
- Core UI panels now use a calmer ink/wood/jade palette with softer rounded corners and lighter button frames instead of the earlier yellow, hard-edged frame treatment.
|
- Core UI panels now prefer generated high-resolution lacquer, scroll, jade, and command-seal frame textures under `art/ui/panels`, with the calmer ink/wood/jade flat styles kept as fallbacks instead of the earlier yellow, hard-edged frame treatment.
|
||||||
- Pre-battle Chapters overview shows story-arc progress with map thumbnail rows, compact status badges, hover details, and can open completed or current battles.
|
- Pre-battle Chapters overview shows story-arc progress with map thumbnail rows, compact status badges, hover details, and can open completed or current battles.
|
||||||
- Pre-battle Save menu can write and load one manual campaign checkpoint separate from the automatic save, using compact seal-style slot rows and hover checkpoint details.
|
- Pre-battle Save menu can write and load one manual campaign checkpoint separate from the automatic save, using compact seal-style slot rows and hover checkpoint details.
|
||||||
- The title screen exposes Start, Load, and Settings, and Load now lets the player choose between the automatic record and the manual checkpoint.
|
- The title screen exposes Start, Load, and Settings, and Load now lets the player choose between the automatic record and the manual checkpoint.
|
||||||
@@ -130,7 +130,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
|||||||
- 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.
|
||||||
- AI-generated scenario battlefield backgrounds, opening story panels, terrain textures, transparent class units, transparent generic-enemy units, item icons, and toolbar UI icons live under `art/backgrounds`, `art/story`, `art/terrain`, `art/units`, `art/items`, and `art/ui/icons`.
|
- AI-generated scenario battlefield backgrounds, opening story panels, terrain textures, transparent class units, transparent generic-enemy units, item icons, toolbar UI icons, and generated UI panel frames live under `art/backgrounds`, `art/story`, `art/terrain`, `art/units`, `art/items`, `art/ui/icons`, and `art/ui/panels`.
|
||||||
- Battle HUD portrait thumbnails show the selected unit, or a hovered unit when nothing is selected.
|
- Battle HUD portrait thumbnails show the selected unit, or a hovered unit when nothing is selected.
|
||||||
- Selected and hovered unit focus text shows combat role, movement type, current terrain move cost, terrain bonuses, core stats, and status effects.
|
- Selected and hovered unit focus text shows combat role, movement type, current terrain move cost, terrain bonuses, core stats, and status effects.
|
||||||
- Dialogue lines can mark speaking side for text alignment inside a fixed visual-novel-style dialogue panel with stable portrait, progress, and previous-line controls.
|
- Dialogue lines can mark speaking side for text alignment inside a fixed visual-novel-style dialogue panel with stable portrait, progress, and previous-line controls.
|
||||||
|
|||||||
BIN
art/ui/panels/panel_command_seal.png
Normal file
BIN
art/ui/panels/panel_command_seal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 896 KiB |
BIN
art/ui/panels/panel_hud_jade.png
Normal file
BIN
art/ui/panels/panel_hud_jade.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 821 KiB |
BIN
art/ui/panels/panel_lacquer.png
Normal file
BIN
art/ui/panels/panel_lacquer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 787 KiB |
BIN
art/ui/panels/panel_paper_scroll.png
Normal file
BIN
art/ui/panels/panel_paper_scroll.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 860 KiB |
File diff suppressed because one or more lines are too long
@@ -29,6 +29,7 @@ const BGM_BATTLE_PATH := "res://audio/bgm/battle_loop_placeholder.wav"
|
|||||||
const TITLE_BACKGROUND_PATH := "res://art/backgrounds/title_war_camp_dawn.png"
|
const TITLE_BACKGROUND_PATH := "res://art/backgrounds/title_war_camp_dawn.png"
|
||||||
const TITLE_PORTRAIT_PATH := "res://art/portraits/cao_cao.png"
|
const TITLE_PORTRAIT_PATH := "res://art/portraits/cao_cao.png"
|
||||||
const TOOLBAR_ICON_PATH_TEMPLATE := "res://art/ui/icons/toolbar_%s.png"
|
const TOOLBAR_ICON_PATH_TEMPLATE := "res://art/ui/icons/toolbar_%s.png"
|
||||||
|
const PANEL_TEXTURE_PATH_TEMPLATE := "res://art/ui/panels/panel_%s.png"
|
||||||
const TERRAIN_TEXTURE_PATH_TEMPLATE := "res://art/terrain/terrain_%s.png"
|
const TERRAIN_TEXTURE_PATH_TEMPLATE := "res://art/terrain/terrain_%s.png"
|
||||||
const OPENING_PROLOGUE_REBELLION_PATH := "res://art/story/opening/yellow_turban_rebellion.png"
|
const OPENING_PROLOGUE_REBELLION_PATH := "res://art/story/opening/yellow_turban_rebellion.png"
|
||||||
const OPENING_PROLOGUE_RESOLVE_PATH := "res://art/story/opening/cao_cao_resolves.png"
|
const OPENING_PROLOGUE_RESOLVE_PATH := "res://art/story/opening/cao_cao_resolves.png"
|
||||||
@@ -692,6 +693,46 @@ func _make_panel_style(fill: Color, border: Color, border_width: int, radius: in
|
|||||||
return style
|
return style
|
||||||
|
|
||||||
|
|
||||||
|
func _make_panel_texture_style(variant: String, content_margin: int) -> StyleBoxTexture:
|
||||||
|
var spec := _panel_texture_spec(variant)
|
||||||
|
if spec.is_empty():
|
||||||
|
return null
|
||||||
|
var texture_key := str(spec.get("texture", ""))
|
||||||
|
var texture := _load_art_texture(PANEL_TEXTURE_PATH_TEMPLATE % texture_key)
|
||||||
|
if texture == null:
|
||||||
|
return null
|
||||||
|
var slice_margin := int(spec.get("slice_margin", 88))
|
||||||
|
var style := StyleBoxTexture.new()
|
||||||
|
style.texture = texture
|
||||||
|
style.draw_center = true
|
||||||
|
style.texture_margin_left = slice_margin
|
||||||
|
style.texture_margin_right = slice_margin
|
||||||
|
style.texture_margin_top = slice_margin
|
||||||
|
style.texture_margin_bottom = slice_margin
|
||||||
|
var resolved_margin: int = maxi(content_margin, int(spec.get("content_margin", content_margin)))
|
||||||
|
style.content_margin_left = resolved_margin
|
||||||
|
style.content_margin_right = resolved_margin
|
||||||
|
style.content_margin_top = resolved_margin
|
||||||
|
style.content_margin_bottom = resolved_margin
|
||||||
|
return style
|
||||||
|
|
||||||
|
|
||||||
|
func _panel_texture_spec(variant: String) -> Dictionary:
|
||||||
|
match variant:
|
||||||
|
"paper", "edict", "notice_edict", "speech_scroll":
|
||||||
|
return {"texture": "paper_scroll", "slice_margin": 108, "content_margin": 22}
|
||||||
|
"silk_map", "bamboo_slips", "hud_info", "target_hint", "caption", "edict_compact":
|
||||||
|
return {"texture": "hud_jade", "slice_margin": 46, "content_margin": 9}
|
||||||
|
"dialogue", "compact", "result_tablet":
|
||||||
|
return {"texture": "lacquer", "slice_margin": 96, "content_margin": 14}
|
||||||
|
"command_seal", "notice", "result_defeat_tablet":
|
||||||
|
return {"texture": "command_seal", "slice_margin": 72, "content_margin": 12}
|
||||||
|
"result_victory_tablet":
|
||||||
|
return {"texture": "hud_jade", "slice_margin": 92, "content_margin": 22}
|
||||||
|
_:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
func _age_panel_style(style: StyleBoxFlat, variant: String, border_width: int) -> void:
|
func _age_panel_style(style: StyleBoxFlat, variant: String, border_width: int) -> void:
|
||||||
if style == null:
|
if style == null:
|
||||||
return
|
return
|
||||||
@@ -872,7 +913,13 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void:
|
|||||||
pass
|
pass
|
||||||
var style := _make_panel_style(fill, border, border_width, radius, margin, shadow_size)
|
var style := _make_panel_style(fill, border, border_width, radius, margin, shadow_size)
|
||||||
_age_panel_style(style, variant, border_width)
|
_age_panel_style(style, variant, border_width)
|
||||||
|
var texture_style := _make_panel_texture_style(variant, margin)
|
||||||
|
if texture_style != null:
|
||||||
|
panel.add_theme_stylebox_override("panel", texture_style)
|
||||||
|
panel.set_meta("generated_panel_texture", true)
|
||||||
|
return
|
||||||
panel.add_theme_stylebox_override("panel", style)
|
panel.add_theme_stylebox_override("panel", style)
|
||||||
|
panel.set_meta("generated_panel_texture", false)
|
||||||
|
|
||||||
|
|
||||||
func _make_button_style(fill: Color, border: Color, border_width: int) -> StyleBoxFlat:
|
func _make_button_style(fill: Color, border: Color, border_width: int) -> StyleBoxFlat:
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ func _init() -> void:
|
|||||||
var failures: Array[String] = []
|
var failures: Array[String] = []
|
||||||
_check_battle_visual_data(failures)
|
_check_battle_visual_data(failures)
|
||||||
_check_generated_toolbar_icons(failures)
|
_check_generated_toolbar_icons(failures)
|
||||||
|
_check_generated_panel_textures(failures)
|
||||||
_check_generated_terrain_textures(failures)
|
_check_generated_terrain_textures(failures)
|
||||||
_check_scene_texture_loading(failures)
|
_check_scene_texture_loading(failures)
|
||||||
_check_hud_focus_text(failures)
|
_check_hud_focus_text(failures)
|
||||||
@@ -2977,6 +2978,12 @@ func _check_generated_toolbar_icons(failures: Array[String]) -> void:
|
|||||||
_check_toolbar_icon_path(failures, "res://art/ui/icons/toolbar_%s.png" % icon_kind, "toolbar icon %s" % icon_kind)
|
_check_toolbar_icon_path(failures, "res://art/ui/icons/toolbar_%s.png" % icon_kind, "toolbar icon %s" % icon_kind)
|
||||||
|
|
||||||
|
|
||||||
|
func _check_generated_panel_textures(failures: Array[String]) -> void:
|
||||||
|
var panel_keys := ["lacquer", "paper_scroll", "hud_jade", "command_seal"]
|
||||||
|
for panel_key in panel_keys:
|
||||||
|
_check_panel_texture_path(failures, "res://art/ui/panels/panel_%s.png" % panel_key, "panel texture %s" % panel_key)
|
||||||
|
|
||||||
|
|
||||||
func _check_generated_terrain_textures(failures: Array[String]) -> void:
|
func _check_generated_terrain_textures(failures: Array[String]) -> void:
|
||||||
var terrain_keys := ["g", "f", "h", "d", "r", "w", "t", "c"]
|
var terrain_keys := ["g", "f", "h", "d", "r", "w", "t", "c"]
|
||||||
for terrain_key in terrain_keys:
|
for terrain_key in terrain_keys:
|
||||||
@@ -3327,6 +3334,12 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
_check_panel_style_fill(failures, scene.dialogue_portrait_panel, "dialogue portrait panel", Color(0.025, 0.023, 0.020, 1.0))
|
_check_panel_style_fill(failures, scene.dialogue_portrait_panel, "dialogue portrait panel", Color(0.025, 0.023, 0.020, 1.0))
|
||||||
_check_panel_style_fill(failures, scene.dialogue_speaker_panel, "dialogue speaker seal panel", Color(0.26, 0.035, 0.030, 0.995))
|
_check_panel_style_fill(failures, scene.dialogue_speaker_panel, "dialogue speaker seal panel", Color(0.26, 0.035, 0.030, 0.995))
|
||||||
_check_panel_style_fill(failures, scene.dialogue_text_panel, "dialogue text scroll panel", Color(0.58, 0.55, 0.48, 0.996))
|
_check_panel_style_fill(failures, scene.dialogue_text_panel, "dialogue text scroll panel", Color(0.58, 0.55, 0.48, 0.996))
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.briefing_panel, "briefing generated frame")
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.briefing_objective_panel, "briefing objective generated frame")
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.briefing_camp_overview_panel, "briefing map generated frame")
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.dialogue_panel, "dialogue generated frame")
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.dialogue_text_panel, "dialogue text generated frame")
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.auto_end_turn_prompt_panel, "auto end turn generated frame")
|
||||||
if scene.screen_backdrop == null:
|
if scene.screen_backdrop == null:
|
||||||
failures.append("ancient briefing screen should create an ink-wash backdrop")
|
failures.append("ancient briefing screen should create an ink-wash backdrop")
|
||||||
else:
|
else:
|
||||||
@@ -3356,6 +3369,9 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
_check_panel_style_frame(failures, scene.cell_info_panel, "HUD terrain info panel", Color(0.20, 0.24, 0.21, 0.96), 2)
|
_check_panel_style_frame(failures, scene.cell_info_panel, "HUD terrain info panel", Color(0.20, 0.24, 0.21, 0.96), 2)
|
||||||
_check_panel_style_fill(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.045, 0.050, 0.048, 0.90))
|
_check_panel_style_fill(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.045, 0.050, 0.048, 0.90))
|
||||||
_check_panel_style_frame(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.20, 0.24, 0.21, 0.96), 2)
|
_check_panel_style_frame(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.20, 0.24, 0.21, 0.96), 2)
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.mission_detail_panel, "mission detail generated frame")
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.cell_info_panel, "HUD terrain generated frame")
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.forecast_panel, "HUD forecast generated frame")
|
||||||
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:
|
||||||
@@ -3487,6 +3503,7 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
failures.append("briefing shop sell button should avoid modern sale wording")
|
failures.append("briefing shop sell button should avoid modern sale wording")
|
||||||
if scene.post_move_title_label == null or scene.post_move_title_label.text != "군령 선택":
|
if scene.post_move_title_label == null or scene.post_move_title_label.text != "군령 선택":
|
||||||
failures.append("post-move command title should use order wording")
|
failures.append("post-move command title should use order wording")
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.post_move_menu, "post-move command generated frame")
|
||||||
_check_local_command_icon_button(failures, scene.post_move_attack_button, "타격령", "attack")
|
_check_local_command_icon_button(failures, scene.post_move_attack_button, "타격령", "attack")
|
||||||
_check_local_command_icon_button(failures, scene.post_move_tactic_button, "책략첩", "tactic")
|
_check_local_command_icon_button(failures, scene.post_move_tactic_button, "책략첩", "tactic")
|
||||||
_check_local_command_icon_button(failures, scene.post_move_item_button, "보급첩", "item")
|
_check_local_command_icon_button(failures, scene.post_move_item_button, "보급첩", "item")
|
||||||
@@ -3506,8 +3523,10 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
failures.append("briefing map fallback should read as an old campaign map")
|
failures.append("briefing map fallback should read as an old campaign map")
|
||||||
scene._apply_panel_style(scene.result_panel, "result_victory_tablet")
|
scene._apply_panel_style(scene.result_panel, "result_victory_tablet")
|
||||||
_check_panel_style_fill(failures, scene.result_panel, "victory result panel", Color(0.27, 0.34, 0.28, 0.997))
|
_check_panel_style_fill(failures, scene.result_panel, "victory result panel", Color(0.27, 0.34, 0.28, 0.997))
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.result_panel, "victory result generated frame")
|
||||||
scene._apply_panel_style(scene.result_panel, "result_defeat_tablet")
|
scene._apply_panel_style(scene.result_panel, "result_defeat_tablet")
|
||||||
_check_panel_style_fill(failures, scene.result_panel, "defeat result panel", Color(0.10, 0.035, 0.028, 0.997))
|
_check_panel_style_fill(failures, scene.result_panel, "defeat result panel", Color(0.10, 0.035, 0.028, 0.997))
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.result_panel, "defeat result generated frame")
|
||||||
if not scene._format_victory_result_text().contains("승리"):
|
if not scene._format_victory_result_text().contains("승리"):
|
||||||
failures.append("victory result should use Korean title")
|
failures.append("victory result should use Korean title")
|
||||||
if not scene._format_defeat_result_text().contains("패배"):
|
if not scene._format_defeat_result_text().contains("패배"):
|
||||||
@@ -3628,9 +3647,13 @@ func _check_panel_style_fill(failures: Array[String], panel: PanelContainer, lab
|
|||||||
if panel == null:
|
if panel == null:
|
||||||
failures.append("%s missing" % label)
|
failures.append("%s missing" % label)
|
||||||
return
|
return
|
||||||
var style := panel.get_theme_stylebox("panel") as StyleBoxFlat
|
var stylebox := panel.get_theme_stylebox("panel")
|
||||||
|
if stylebox is StyleBoxTexture:
|
||||||
|
_check_panel_style_texture(failures, stylebox as StyleBoxTexture, label)
|
||||||
|
return
|
||||||
|
var style := stylebox as StyleBoxFlat
|
||||||
if style == null:
|
if style == null:
|
||||||
failures.append("%s should use a flat styled panel" % label)
|
failures.append("%s should use a styled panel" % label)
|
||||||
return
|
return
|
||||||
if not _colors_nearly_equal(style.bg_color, expected):
|
if not _colors_nearly_equal(style.bg_color, expected):
|
||||||
failures.append("%s fill mismatch: %s" % [label, str(style.bg_color)])
|
failures.append("%s fill mismatch: %s" % [label, str(style.bg_color)])
|
||||||
@@ -3640,9 +3663,13 @@ func _check_panel_style_frame(failures: Array[String], panel: PanelContainer, la
|
|||||||
if panel == null:
|
if panel == null:
|
||||||
failures.append("%s missing" % label)
|
failures.append("%s missing" % label)
|
||||||
return
|
return
|
||||||
var style := panel.get_theme_stylebox("panel") as StyleBoxFlat
|
var stylebox := panel.get_theme_stylebox("panel")
|
||||||
|
if stylebox is StyleBoxTexture:
|
||||||
|
_check_panel_style_texture(failures, stylebox as StyleBoxTexture, label)
|
||||||
|
return
|
||||||
|
var style := stylebox as StyleBoxFlat
|
||||||
if style == null:
|
if style == null:
|
||||||
failures.append("%s should use a flat styled panel" % label)
|
failures.append("%s should use a styled panel" % label)
|
||||||
return
|
return
|
||||||
if not _colors_nearly_equal(style.border_color, expected_border):
|
if not _colors_nearly_equal(style.border_color, expected_border):
|
||||||
failures.append("%s border mismatch: %s" % [label, str(style.border_color)])
|
failures.append("%s border mismatch: %s" % [label, str(style.border_color)])
|
||||||
@@ -3653,6 +3680,32 @@ func _check_panel_style_frame(failures: Array[String], panel: PanelContainer, la
|
|||||||
failures.append("%s should use softened rounded corners, got %d" % [label, radius])
|
failures.append("%s should use softened rounded corners, got %d" % [label, radius])
|
||||||
|
|
||||||
|
|
||||||
|
func _check_panel_style_texture(failures: Array[String], style: StyleBoxTexture, label: String) -> void:
|
||||||
|
if style == null:
|
||||||
|
failures.append("%s should use a generated texture style" % label)
|
||||||
|
return
|
||||||
|
if style.texture == null:
|
||||||
|
failures.append("%s generated texture style is missing its texture" % label)
|
||||||
|
return
|
||||||
|
if style.texture.get_width() < 512 or style.texture.get_height() < 512:
|
||||||
|
failures.append("%s generated panel texture should keep high-resolution source pixels: %dx%d" % [label, style.texture.get_width(), style.texture.get_height()])
|
||||||
|
if style.texture_margin_left < 24 or style.texture_margin_top < 24:
|
||||||
|
failures.append("%s generated panel texture should define a visible frame slice margin" % label)
|
||||||
|
if style.content_margin_left < 7 or style.content_margin_top < 7:
|
||||||
|
failures.append("%s generated panel texture should reserve readable content margins" % label)
|
||||||
|
|
||||||
|
|
||||||
|
func _check_panel_uses_generated_texture(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 StyleBoxTexture):
|
||||||
|
failures.append("%s should use a generated panel texture" % label)
|
||||||
|
return
|
||||||
|
_check_panel_style_texture(failures, stylebox as StyleBoxTexture, label)
|
||||||
|
|
||||||
|
|
||||||
func _colors_nearly_equal(left: Color, right: Color) -> bool:
|
func _colors_nearly_equal(left: Color, right: Color) -> bool:
|
||||||
return (
|
return (
|
||||||
absf(left.r - right.r) < 0.01
|
absf(left.r - right.r) < 0.01
|
||||||
@@ -3874,6 +3927,7 @@ func _check_battle_unit_list_panel(failures: Array[String]) -> void:
|
|||||||
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.93))
|
||||||
|
_check_panel_uses_generated_texture(failures, scene.battle_unit_list_panel, "battle unit list generated frame")
|
||||||
if scene.battle_unit_list_title_label == null or not scene.battle_unit_list_title_label.text.contains("아군"):
|
if scene.battle_unit_list_title_label == null or not scene.battle_unit_list_title_label.text.contains("아군"):
|
||||||
failures.append("battle unit list should open on the ally tab")
|
failures.append("battle unit list should open on the ally tab")
|
||||||
if scene.battle_unit_list_rows == null or scene.battle_unit_list_rows.get_child_count() < 2:
|
if scene.battle_unit_list_rows == null or scene.battle_unit_list_rows.get_child_count() < 2:
|
||||||
@@ -4843,6 +4897,58 @@ func _check_toolbar_icon_path(failures: Array[String], path: String, context: St
|
|||||||
failures.append("%s icon should not keep chroma-key green pixels: %s (%d sampled)" % [context, path, green_fringe_samples])
|
failures.append("%s icon should not keep chroma-key green pixels: %s (%d sampled)" % [context, path, green_fringe_samples])
|
||||||
|
|
||||||
|
|
||||||
|
func _check_panel_texture_path(failures: Array[String], path: String, context: String) -> void:
|
||||||
|
if path.is_empty():
|
||||||
|
failures.append("%s has an empty panel path" % context)
|
||||||
|
return
|
||||||
|
if not path.begins_with("res://"):
|
||||||
|
failures.append("%s must use a res:// path: %s" % [context, path])
|
||||||
|
return
|
||||||
|
if not FileAccess.file_exists(path):
|
||||||
|
failures.append("%s references missing panel texture: %s" % [context, path])
|
||||||
|
return
|
||||||
|
var image := Image.new()
|
||||||
|
var err := image.load(path)
|
||||||
|
if err != OK:
|
||||||
|
failures.append("%s references unreadable panel texture: %s" % [context, path])
|
||||||
|
return
|
||||||
|
if image.get_width() < 512 or image.get_height() < 512:
|
||||||
|
failures.append("%s should keep high-resolution source pixels: %s (%dx%d)" % [context, path, image.get_width(), image.get_height()])
|
||||||
|
return
|
||||||
|
for corner in [Vector2i(0, 0), Vector2i(image.get_width() - 1, 0), Vector2i(0, image.get_height() - 1), Vector2i(image.get_width() - 1, image.get_height() - 1)]:
|
||||||
|
if image.get_pixelv(corner).a > 0.01:
|
||||||
|
failures.append("%s should have transparent corners: %s" % [context, path])
|
||||||
|
return
|
||||||
|
var opaque_samples := 0
|
||||||
|
var transparent_samples := 0
|
||||||
|
var green_fringe_samples := 0
|
||||||
|
var luma_min := 999.0
|
||||||
|
var luma_max := -999.0
|
||||||
|
var total_samples := 0
|
||||||
|
for y in range(0, image.get_height(), 8):
|
||||||
|
for x in range(0, image.get_width(), 8):
|
||||||
|
total_samples += 1
|
||||||
|
var pixel := image.get_pixel(x, y)
|
||||||
|
if pixel.a <= 0.01:
|
||||||
|
transparent_samples += 1
|
||||||
|
continue
|
||||||
|
if pixel.a >= 0.80:
|
||||||
|
opaque_samples += 1
|
||||||
|
if pixel.g > 0.80 and pixel.r < 0.18 and pixel.b < 0.18:
|
||||||
|
green_fringe_samples += 1
|
||||||
|
var luma := pixel.r * 0.299 + pixel.g * 0.587 + pixel.b * 0.114
|
||||||
|
luma_min = minf(luma_min, luma)
|
||||||
|
luma_max = maxf(luma_max, luma)
|
||||||
|
if transparent_samples < int(total_samples * 0.08):
|
||||||
|
failures.append("%s should retain transparent frame padding: %s (%d/%d sampled pixels)" % [context, path, transparent_samples, total_samples])
|
||||||
|
if opaque_samples < int(total_samples * 0.36):
|
||||||
|
failures.append("%s should contain substantial generated frame artwork: %s (%d/%d sampled pixels)" % [context, path, opaque_samples, total_samples])
|
||||||
|
if green_fringe_samples > 0:
|
||||||
|
failures.append("%s should not keep chroma-key green pixels: %s (%d sampled)" % [context, path, green_fringe_samples])
|
||||||
|
if luma_max - luma_min < 0.10:
|
||||||
|
failures.append("%s should contain visible material contrast: %s" % [context, path])
|
||||||
|
|
||||||
|
|
||||||
func _check_terrain_texture_path(failures: Array[String], path: String, context: String) -> void:
|
func _check_terrain_texture_path(failures: Array[String], path: String, context: String) -> void:
|
||||||
if path.is_empty():
|
if path.is_empty():
|
||||||
failures.append("%s has an empty texture path" % context)
|
failures.append("%s has an empty texture path" % context)
|
||||||
|
|||||||
Reference in New Issue
Block a user