Age battle UI toward ancient campaign style

This commit is contained in:
2026-06-19 02:47:18 +09:00
parent f26952e39e
commit 9d31448573
3 changed files with 146 additions and 47 deletions

View File

@@ -83,6 +83,9 @@ const UI_BAMBOO_DARK := Color(0.20, 0.11, 0.045, 1.0)
const UI_MUTED_JADE := Color(0.16, 0.30, 0.23, 1.0)
const UI_PARCHMENT_TEXT := Color(0.95, 0.82, 0.55, 1.0)
const UI_DISABLED_TEXT := Color(0.48, 0.40, 0.29, 1.0)
const UI_SILK_WASH := Color(0.80, 0.64, 0.37, 1.0)
const UI_SILK_FIBER := Color(0.18, 0.10, 0.045, 1.0)
const MAP_SCROLL_FRAME_PADDING := 12.0
var state: BattleState = BattleStateScript.new()
var campaign_state: CampaignState = CampaignStateScript.new()
@@ -431,6 +434,27 @@ func _make_bamboo_divider(width: float = 0.0, height: float = 4.0) -> HBoxContai
return row
func _make_scroll_rod(width: float = 0.0, height: float = 4.0) -> HBoxContainer:
var row := HBoxContainer.new()
row.custom_minimum_size = Vector2(width, height)
row.size_flags_horizontal = Control.SIZE_EXPAND_FILL
row.add_theme_constant_override("separation", 2)
var cap_width: float = maxf(8.0, height * 3.0)
for index in range(5):
var strip := ColorRect.new()
strip.mouse_filter = Control.MOUSE_FILTER_IGNORE
strip.custom_minimum_size = Vector2(cap_width if index != 2 else width, height)
strip.size_flags_horizontal = Control.SIZE_EXPAND_FILL if index == 2 else Control.SIZE_SHRINK_CENTER
if index == 0 or index == 4:
strip.color = UI_CINNABAR_DARK
elif index == 1 or index == 3:
strip.color = UI_OLD_BRONZE
else:
strip.color = UI_BAMBOO
row.add_child(strip)
return row
func _make_section_caption(text: String, width: float = 0.0) -> Label:
var label := Label.new()
label.text = text
@@ -558,7 +582,7 @@ func _create_hud() -> void:
selected_row.add_child(selected_label)
mission_title_label = Label.new()
mission_title_label.text = "Battle Mandate"
mission_title_label.text = "Imperial Mandate"
mission_title_label.add_theme_font_size_override("font_size", 14)
_apply_label_style(mission_title_label, UI_OLD_BRONZE)
side_column.add_child(mission_title_label)
@@ -846,8 +870,8 @@ func _create_hud() -> void:
_apply_label_style(briefing_location_label, UI_MUTED_JADE, 14)
briefing_column.add_child(briefing_location_label)
briefing_column.add_child(_make_bamboo_divider(680, 4))
briefing_column.add_child(_make_section_caption("Bronze War Edict", 680))
briefing_column.add_child(_make_scroll_rod(680, 4))
briefing_column.add_child(_make_section_caption("Imperial Bronze War Edict", 680))
briefing_objective_panel = PanelContainer.new()
briefing_objective_panel.custom_minimum_size = Vector2(680, 92)
@@ -867,7 +891,7 @@ func _create_hud() -> void:
_apply_label_style(briefing_objective_label, UI_AGED_INK, 15)
briefing_objective_row.add_child(briefing_objective_label)
briefing_column.add_child(_make_section_caption("Bamboo Map and Camp Ledger", 680))
briefing_column.add_child(_make_section_caption("Silk Map and Camp Ledger", 680))
briefing_camp_overview_row = HBoxContainer.new()
briefing_camp_overview_row.custom_minimum_size = Vector2(680, 84)
@@ -905,7 +929,7 @@ func _create_hud() -> void:
_apply_label_style(briefing_camp_overview_label, UI_AGED_INK, 14)
briefing_camp_overview_row.add_child(briefing_camp_overview_label)
briefing_column.add_child(_make_bamboo_divider(680, 3))
briefing_column.add_child(_make_scroll_rod(680, 3))
var briefing_scroll := ScrollContainer.new()
briefing_scroll.custom_minimum_size = Vector2(680, 66)
@@ -1116,7 +1140,7 @@ func _create_hud() -> void:
save_action_row.add_child(manual_load_button)
var begin_button := Button.new()
begin_button.text = "Enter Field"
begin_button.text = "Unroll War Map"
begin_button.pressed.connect(_on_begin_battle_pressed)
briefing_column.add_child(begin_button)
@@ -1161,7 +1185,7 @@ func _create_hud() -> void:
dialogue_column.add_theme_constant_override("separation", 8)
dialogue_row.add_child(dialogue_column)
dialogue_column.add_child(_make_bamboo_divider(DIALOGUE_TEXT_SIZE.x, 3))
dialogue_column.add_child(_make_scroll_rod(DIALOGUE_TEXT_SIZE.x, 3))
dialogue_speaker_panel = PanelContainer.new()
dialogue_speaker_panel.custom_minimum_size = Vector2(DIALOGUE_TEXT_SIZE.x, 32)
@@ -1193,7 +1217,7 @@ func _create_hud() -> void:
_apply_label_style(dialogue_text_label, UI_AGED_INK, 17)
dialogue_text_row.add_child(dialogue_text_label)
dialogue_column.add_child(_make_bamboo_divider(DIALOGUE_TEXT_SIZE.x, 3))
dialogue_column.add_child(_make_scroll_rod(DIALOGUE_TEXT_SIZE.x, 3))
var dialogue_control_row := HBoxContainer.new()
dialogue_control_row.add_theme_constant_override("separation", 8)
@@ -1230,6 +1254,8 @@ func _create_hud() -> void:
result_column.alignment = BoxContainer.ALIGNMENT_CENTER
result_panel.add_child(result_column)
result_column.add_child(_make_scroll_rod(520, 4))
result_label = Label.new()
result_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
result_label.vertical_alignment = VERTICAL_ALIGNMENT_TOP
@@ -1238,6 +1264,8 @@ func _create_hud() -> void:
_apply_label_style(result_label, UI_AGED_INK, 16)
result_column.add_child(result_label)
result_column.add_child(_make_scroll_rod(520, 3))
result_choice_list = VBoxContainer.new()
result_choice_list.add_theme_constant_override("separation", 6)
result_column.add_child(result_choice_list)
@@ -1759,7 +1787,58 @@ func _draw_map() -> void:
_draw_terrain_edge_blend(cell, rect, terrain_key)
draw_rect(rect, _map_grid_color(has_background), false, 1.0)
draw_rect(board_rect, Color(0.02, 0.025, 0.03), false, 3.0)
_draw_silk_map_patina(board_rect, has_background)
_draw_silk_map_frame(board_rect)
func _draw_silk_map_patina(board_rect: Rect2, has_background: bool) -> void:
var wash := UI_SILK_WASH
wash.a = 0.08 if has_background else 0.14
draw_rect(board_rect, wash)
var fiber := UI_SILK_FIBER
fiber.a = 0.12 if has_background else 0.18
for index in range(7):
var y := board_rect.position.y + 18.0 + float(index) * (board_rect.size.y - 36.0) / 6.0
var drift := 4.0 + _cell_noise(Vector2i(index, state.map_size.y), 211) * 10.0
draw_line(
Vector2(board_rect.position.x + 12.0, y),
Vector2(board_rect.end.x - 12.0, y + drift),
fiber,
1.0
)
for index in range(5):
var x := board_rect.position.x + 20.0 + float(index) * (board_rect.size.x - 40.0) / 4.0
var drift := 5.0 + _cell_noise(Vector2i(state.map_size.x, index), 227) * 9.0
draw_line(
Vector2(x, board_rect.position.y + 12.0),
Vector2(x + drift, board_rect.end.y - 12.0),
Color(fiber.r, fiber.g, fiber.b, fiber.a * 0.65),
1.0
)
var corner_stain := Color(0.12, 0.06, 0.025, 0.12 if has_background else 0.18)
draw_circle(board_rect.position + Vector2(20, 18), 26.0, corner_stain)
draw_circle(board_rect.end - Vector2(24, 22), 30.0, corner_stain)
func _draw_silk_map_frame(board_rect: Rect2) -> void:
var padding := MAP_SCROLL_FRAME_PADDING
var top_rod := Rect2(board_rect.position + Vector2(-padding, -padding), Vector2(board_rect.size.x + padding * 2.0, 7.0))
var bottom_rod := Rect2(Vector2(board_rect.position.x - padding, board_rect.end.y + padding - 7.0), top_rod.size)
var left_rod := Rect2(board_rect.position + Vector2(-padding, -padding), Vector2(7.0, board_rect.size.y + padding * 2.0))
var right_rod := Rect2(Vector2(board_rect.end.x + padding - 7.0, board_rect.position.y - padding), left_rod.size)
for rod in [top_rod, bottom_rod, left_rod, right_rod]:
draw_rect(rod, UI_BAMBOO_DARK)
draw_rect(rod, Color(0.62, 0.43, 0.18, 0.80), false, 1.0)
for corner in [
top_rod.position - Vector2(3, 3),
Vector2(top_rod.end.x - 11.0, top_rod.position.y - 3.0),
Vector2(bottom_rod.position.x - 3.0, bottom_rod.end.y - 11.0),
bottom_rod.end - Vector2(11, 11)
]:
draw_rect(Rect2(corner, Vector2(14, 14)), UI_CINNABAR_DARK)
draw_rect(Rect2(corner + Vector2(2, 2), Vector2(10, 10)), UI_OLD_BRONZE, false, 1.0)
draw_rect(board_rect.grow(4.0), UI_LACQUER_EDGE, false, 5.0)
draw_rect(board_rect.grow(1.0), UI_OLD_BRONZE, false, 2.0)
func _current_battle_background_texture() -> Texture2D:
@@ -2964,8 +3043,8 @@ func _update_hud() -> void:
func _format_objective_hud_text() -> String:
var text := String(state.objectives.get("victory", "Defeat all enemies."))
if text.is_empty():
return "Battle Mandate"
return "Battle Mandate: %s" % text
return "Imperial Mandate"
return "Imperial Mandate: %s" % text
func _format_unit_focus_text(unit: Dictionary, prefix: String) -> String:
@@ -3135,19 +3214,19 @@ func _format_mission_panel_text() -> String:
var lines := []
var victory := str(state.objectives.get("victory", ""))
if not victory.is_empty():
lines.append("Edict: %s" % victory)
lines.append("Imperial Edict: %s" % victory)
var progress_lines := state.get_objective_progress_lines(false, false)
if not progress_lines.is_empty():
lines.append("Battle Register:")
lines.append("Bamboo Register:")
for progress_line in progress_lines:
lines.append(" %s" % progress_line)
var risk_lines := state.get_defeat_progress_lines()
if not risk_lines.is_empty():
lines.append("Dire Portents:")
lines.append("Ominous Signs:")
for risk_line in risk_lines:
lines.append(" %s" % risk_line)
if lines.is_empty():
return "No battle mandate."
return "No edict has been sealed."
return _join_strings(lines, "\n")
@@ -3542,10 +3621,7 @@ func _update_result_panel() -> void:
if result_restart_button != null:
result_restart_button.visible = true
_update_result_next_button_visibility()
result_label.text = "Victory Proclamation\n%s\n%s" % [
state.objectives.get("victory", ""),
_format_battle_result_summary()
]
result_label.text = _format_victory_result_text()
elif state.battle_status == BattleState.STATUS_DEFEAT:
_clear_dialogue()
_clear_result_choices()
@@ -3554,7 +3630,7 @@ func _update_result_panel() -> void:
result_restart_button.visible = true
if next_battle_button != null:
next_battle_button.visible = false
result_label.text = "Defeat Memorial\n%s" % state.objectives.get("defeat", "")
result_label.text = _format_defeat_result_text()
else:
_clear_result_choices()
result_panel.visible = false
@@ -3564,6 +3640,17 @@ func _update_result_panel() -> void:
next_battle_button.visible = false
func _format_victory_result_text() -> String:
return "Bronze Victory Proclamation\nMandate Fulfilled: %s\n%s" % [
state.objectives.get("victory", ""),
_format_battle_result_summary()
]
func _format_defeat_result_text() -> String:
return "Defeat Memorial Tablet\nOmen Fulfilled: %s" % state.objectives.get("defeat", "")
func _on_state_changed() -> void:
if state.get_selected_unit().is_empty():
selected_skill_id = ""
@@ -3584,18 +3671,18 @@ func _on_log_added(message: String) -> void:
func _format_log_entry_text(message: String) -> String:
if message.begins_with("Objective updated:"):
return "Mandate revised:%s" % message.substr("Objective updated:".length())
return "Edict amended:%s" % message.substr("Objective updated:".length())
if message.begins_with("Defeat condition updated:"):
return "Omen revised:%s" % message.substr("Defeat condition updated:".length())
return "Omen amended:%s" % message.substr("Defeat condition updated:".length())
return message
func _on_objective_updated(victory: String, defeat: String) -> void:
var notice_lines := ["New Battle Mandate"]
var notice_lines := ["New Sealed Edict"]
if not victory.is_empty():
notice_lines.append("Mandate: %s" % victory)
notice_lines.append("Imperial Mandate: %s" % victory)
if not defeat.is_empty():
notice_lines.append("Omen: %s" % defeat)
notice_lines.append("Defeat Omen: %s" % defeat)
if objective_notice_label != null:
objective_notice_label.text = "\n".join(notice_lines)
if objective_notice_panel != null:
@@ -4317,27 +4404,27 @@ func _format_briefing_objectives() -> String:
var text := ""
var victory := str(state.objectives.get("victory", ""))
if not victory.is_empty():
text = "Mandate of Victory: %s" % victory
text = "Imperial Victory Edict: %s" % victory
var defeat := str(state.objectives.get("defeat", ""))
if not defeat.is_empty():
if not text.is_empty():
text += "\n"
text += "Omen of Ruin: %s" % defeat
text += "Defeat Omen: %s" % defeat
var progress_lines := state.get_objective_progress_lines(false, false)
if not progress_lines.is_empty():
if not text.is_empty():
text += "\n"
text += "Battle Register: %s" % _join_strings(progress_lines, ", ")
text += "Bamboo Battle Register: %s" % _join_strings(progress_lines, ", ")
var risk_lines := state.get_defeat_progress_lines()
if not risk_lines.is_empty():
if not text.is_empty():
text += "\n"
text += "Dire Portents: %s" % _join_strings(risk_lines, ", ")
text += "Ominous Signs: %s" % _join_strings(risk_lines, ", ")
var rewards_text := _format_briefing_rewards()
if not rewards_text.is_empty():
if not text.is_empty():
text += "\n"
text += "Spoils Ledger: %s" % rewards_text
text += "Spoils Seal: %s" % rewards_text
return text
@@ -6022,10 +6109,7 @@ func _on_post_battle_choice_pressed(choice: Dictionary) -> void:
_rebuild_result_choices()
_update_result_next_button_visibility()
if result_label != null and state.battle_status == BattleState.STATUS_VICTORY:
result_label.text = "Victory Proclamation\n%s\n%s" % [
state.objectives.get("victory", ""),
_format_battle_result_summary()
]
result_label.text = _format_victory_result_text()
_update_hud()
@@ -6899,8 +6983,8 @@ func _action_phase_block_reason() -> Dictionary:
}
if not battle_started:
return {
"label": "Enter Field",
"tooltip": "Begin the battle before using combat actions."
"label": "Unroll Map",
"tooltip": "Unroll the war map before issuing combat orders."
}
if state.battle_status != BattleState.STATUS_ACTIVE:
return {
@@ -6954,7 +7038,7 @@ func _update_threat_button() -> void:
_set_action_button_blocked(threat_button, "Enemy Lines", "Chronicle Sealed", "The campaign is complete.")
return
if not battle_started:
_set_action_button_blocked(threat_button, "Enemy Lines", "Enter Field", "Begin the battle before reading enemy lines.")
_set_action_button_blocked(threat_button, "Enemy Lines", "Unroll Map", "Unroll the war map before reading enemy lines.")
return
if state.battle_status != BattleState.STATUS_ACTIVE:
_set_action_button_blocked(threat_button, "Enemy Lines", "Field Settled", "Enemy lines are only available during active battles.")