fix(m57-c): per-region readiness chrome and typography
Stop reusing one plaque for every region. The header is now a compartmented band with dividers, icons and left-aligned values; the bottom is a carved dock band with four independent command plates (icon above label, wider green primary) instead of buttons inside a long frame; the warning stack is three cards with severity icons; the summary is a parchment card with shape markers, values and pip rows; and typography uses explicit tracking and embolden so titles, values and captions read as different ranks. View contract names and z-order are unchanged.
This commit is contained in:
@@ -3,24 +3,27 @@ extends "res://presentation/page_view.gd"
|
||||
|
||||
## UI-only PTY-002 Readiness Review View.
|
||||
##
|
||||
## The authored page uses a normalized 16:9 canvas. The View owns the
|
||||
## parchment, semantic overlays, PartyLineTrack layers, focus and explicit
|
||||
## intents; all Company/Formation/Warning/Receipt facts come from the copied
|
||||
## ReadinessPageController snapshot.
|
||||
##
|
||||
## M57: the shell uses the shared asset nine-patch material (paper, wood,
|
||||
## teal command plates) instead of page-local flat rectangles, and the party is
|
||||
## grounded on one baseline with a contact shadow and a name plate.
|
||||
## M57: each region owns its own structure instead of one reused plaque.
|
||||
## The header is a compartmented band, the bottom is a carved dock with four
|
||||
## independent command plates, warnings are small cards and the summary is an
|
||||
## icon/value/pip grid. Text and values stay engine-rendered.
|
||||
|
||||
const InitialCompanyArt = preload("res://entry/initial_company_art.gd")
|
||||
const PartyLineTrack = preload("res://entry/party_line_track.gd")
|
||||
const AssetChrome = preload("res://ui/shared/asset_chrome.gd")
|
||||
const PageChrome = preload("res://ui/shared/page_chrome.gd")
|
||||
const UIIcon = preload("res://ui/shared/ui_icon.gd")
|
||||
const READINESS_STATIC_MASTER_PATH := "res://assets/backgrounds/guild_master_b_background.png"
|
||||
const M55_READINESS_HEADER = preload("res://ui/m55/readiness_header.gd")
|
||||
const M55_READINESS_SUMMARY = preload("res://ui/m55/readiness_formation_summary.gd")
|
||||
const M55_READINESS_ACTION_DOCK = preload("res://ui/m55/readiness_action_dock.gd")
|
||||
const M55_READINESS_SLOTS = preload("res://ui/m55/readiness_slots.gd")
|
||||
|
||||
const HEADER_BAND := Rect2(0.030, 0.018, 0.865, 0.115)
|
||||
const HEADER_SLOT_COUNT := 5
|
||||
const DOCK_BAND := Rect2(0.0, 0.838, 1.0, 0.162)
|
||||
const WARNING_STACK := Rect2(0.700, 0.420, 0.265, 0.222)
|
||||
const STATUS_CARD := Rect2(0.700, 0.672, 0.265, 0.156)
|
||||
|
||||
var design_canvas: Control
|
||||
|
||||
static var _shadow_texture_cache: Texture2D
|
||||
@@ -81,10 +84,6 @@ func _build_overlay(data: Dictionary) -> void:
|
||||
overlay.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
design_canvas.add_child(overlay)
|
||||
|
||||
var top_rail := AssetChrome.rail("ReadinessTopRail", Rect2(0.03, 0.018, 0.94, 0.112))
|
||||
top_rail.z_index = 10
|
||||
overlay.add_child(top_rail)
|
||||
|
||||
var view_formation: Dictionary = Dictionary(data.get("formation", {}))
|
||||
var view_order: Dictionary = Dictionary(data.get("order", {}))
|
||||
var view_contract: Dictionary = Dictionary(data.get("contract", {}))
|
||||
@@ -99,19 +98,16 @@ func _build_overlay(data: Dictionary) -> void:
|
||||
var contract_name := String(view_contract.get("contract_id", view_contract.get("name", "")))
|
||||
if contract_name.is_empty():
|
||||
contract_name = localize("委托", "CONTRACT", data)
|
||||
var header_values := [
|
||||
{"name":"ReadinessTitle", "text":localize("出发准备", "DEPARTURE", data), "slot":M55_READINESS_SLOTS.header_value(0)},
|
||||
{"name":"ReadinessSnapshot", "text":localize("已冻结", "FROZEN", data), "slot":M55_READINESS_SLOTS.header_value(1)},
|
||||
{"name":"ReadinessContract", "text":contract_name, "slot":M55_READINESS_SLOTS.header_value(2)},
|
||||
{"name":"ReadinessPressure", "text":_party_pressure_short(String(view_contract.get("difficulty", "standard")), data), "slot":M55_READINESS_SLOTS.header_value(3)},
|
||||
{"name":"ReadinessCompany", "text":localize("编制 %d/%d", "LINE %d/%d", data) % [int(view_company.get("owned_count", 0)), int(view_company.get("line_capacity", 0))], "slot":M55_READINESS_SLOTS.header_value(4)}
|
||||
]
|
||||
var readiness_header := M55_READINESS_HEADER.new().configure(header_values)
|
||||
readiness_header.name = "ReadinessHeader"
|
||||
_readiness_place(readiness_header, Rect2(0.0, 0.034, 1.0, 0.080))
|
||||
readiness_header.z_index = 40
|
||||
overlay.add_child(readiness_header)
|
||||
var settings_button := _readiness_action_button("ReadinessSettings", Rect2(0.895, 0.036, 0.066, 0.076), "settings", {}, localize("打开设置", "Open settings", data))
|
||||
|
||||
_build_header(overlay, [
|
||||
{"name":"ReadinessTitle", "text":localize("出发准备", "DEPARTURE", data), "icon":"", "role":"title", "size":11, "tracking":1.6, "embolden":0.38, "align":"left"},
|
||||
{"name":"ReadinessSnapshot", "text":localize("已冻结", "FROZEN", data), "icon":"pause", "role":"value", "size":9, "tracking":0.6, "embolden":0.0, "align":"left"},
|
||||
{"name":"ReadinessContract", "text":contract_name, "icon":"inspect", "role":"value", "size":9, "tracking":0.6, "embolden":0.0, "align":"left"},
|
||||
{"name":"ReadinessPressure", "text":_party_pressure_short(String(view_contract.get("difficulty", "standard")), data), "icon":"risk", "role":"value", "size":9, "tracking":0.6, "embolden":0.0, "align":"left"},
|
||||
{"name":"ReadinessCompany", "text":localize("编制 %d/%d", "LINE %d/%d", data) % [int(view_company.get("owned_count", 0)), int(view_company.get("line_capacity", 0))], "icon":"company", "role":"value", "size":9, "tracking":0.6, "embolden":0.0, "align":"left"}
|
||||
])
|
||||
|
||||
var settings_button := _readiness_action_button("ReadinessSettings", Rect2(0.903, 0.020, 0.062, 0.078), "settings", {}, localize("打开设置", "Open settings", data))
|
||||
overlay.add_child(settings_button)
|
||||
|
||||
var cord_track := PartyLineTrack.new()
|
||||
@@ -151,56 +147,35 @@ func _build_overlay(data: Dictionary) -> void:
|
||||
var summary := M55_READINESS_SUMMARY.new().configure(
|
||||
localize("首要目标:%s · 四人同时出战 · 无自动替补", "Primary exposure: %s · all four deploy · no automatic Reserve", data) % _recruit_name(front_recruit, data),
|
||||
[
|
||||
{"name":"ReadinessProtection", "text":localize("保护位 %d", "GUARD %d", data) % int(view_coverage.get("protection", 0)), "left":0.06, "right":0.22},
|
||||
{"name":"ReadinessReach", "text":localize("远射位 %d", "RANGED %d", data) % int(view_coverage.get("long_reach", 0)), "left":0.27, "right":0.43},
|
||||
{"name":"ReadinessPressureCoverage", "text":localize("控场位 %d", "CONTROL %d", data) % int(view_coverage.get("control", 0)), "left":0.47, "right":0.65},
|
||||
{"name":"ReadinessReserve", "text":localize("预备位 0", "RESERVE 0", data), "left":0.69, "right":0.84}
|
||||
{"name":"ReadinessProtection", "text":localize("保护", "GUARD", data), "count":int(view_coverage.get("protection", 0)), "left":0.09, "right":0.29},
|
||||
{"name":"ReadinessReach", "text":localize("远射", "RANGED", data), "count":int(view_coverage.get("long_reach", 0)), "left":0.31, "right":0.51},
|
||||
{"name":"ReadinessPressureCoverage", "text":localize("控场", "CONTROL", data), "count":int(view_coverage.get("control", 0)), "left":0.53, "right":0.73},
|
||||
{"name":"ReadinessReserve", "text":localize("预备", "RESERVE", data), "count":0, "left":0.75, "right":0.95}
|
||||
],
|
||||
localize("出发摘要", "DEPARTURE SUMMARY", data)
|
||||
)
|
||||
summary.name = "ReadinessFormationSummary"
|
||||
_readiness_place(summary, Rect2(0.04, 0.675, 0.64, 0.16))
|
||||
_readiness_place(summary, Rect2(0.040, 0.672, 0.620, 0.156))
|
||||
summary.z_index = 20
|
||||
overlay.add_child(summary)
|
||||
|
||||
var warning_surface := AssetChrome.paper("ReadinessWarningSurface", Rect2(0.69, 0.150, 0.28, 0.200), 0.5, 70.0, 55.0)
|
||||
warning_surface.z_index = 20
|
||||
overlay.add_child(warning_surface)
|
||||
var warning_button := _readiness_warning_button(warning_rows, data)
|
||||
_readiness_place(warning_button, Rect2(0.69, 0.150, 0.28, 0.200))
|
||||
warning_button.z_index = 45
|
||||
overlay.add_child(warning_button)
|
||||
|
||||
var status_surface := AssetChrome.paper("ReadinessStatusSurface", Rect2(0.69, 0.365, 0.28, 0.185), 0.5, 70.0, 55.0)
|
||||
status_surface.z_index = 20
|
||||
overlay.add_child(status_surface)
|
||||
var risk_hint := _readiness_label(
|
||||
overlay,
|
||||
"ReadinessRiskUnavailable",
|
||||
Rect2(0.705, 0.378, 0.25, 0.062),
|
||||
localize("接受准备后可比较风险", "ACCEPT PREPARATION TO COMPARE RISK", data),
|
||||
8,
|
||||
Color("#5e4a32") if not bool(command_data.get("risk_enabled", false)) else Color("#3f6b4f")
|
||||
)
|
||||
risk_hint.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
risk_hint.z_index = 21
|
||||
var knockout := _readiness_label(overlay, "ReadinessKnockoutRule", Rect2(0.705, 0.442, 0.25, 0.062), localize("击倒成员下一关归队", "A KNOCKED-OUT MEMBER RETURNS NEXT STAGE", data), 7, Color("#2f2417"))
|
||||
knockout.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
knockout.z_index = 21
|
||||
if bool(Dictionary(data.get("receipt", {})).get("present", false)):
|
||||
var receipt := _readiness_label(overlay, "ReadinessReceipt", Rect2(0.705, 0.500, 0.25, 0.042), localize("回执已保存", "RECEIPT CONFIRMED", data), 7, Color("#3f6b4f"))
|
||||
receipt.z_index = 21
|
||||
receipt.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
_build_status_card(overlay, command_data, data)
|
||||
|
||||
var action_dock := M55_READINESS_ACTION_DOCK.new().configure()
|
||||
action_dock.name = "ReadinessActionDock"
|
||||
_readiness_place(action_dock, Rect2(0.0, 0.0, 1.0, 1.0))
|
||||
action_dock.z_index = 50
|
||||
overlay.add_child(action_dock)
|
||||
var bottom_rail := AssetChrome.rail("ReadinessBottomRail", Rect2(0.03, 0.842, 0.94, 0.132))
|
||||
bottom_rail.z_index = 40
|
||||
action_dock.add_child(bottom_rail)
|
||||
var revise := _readiness_command_button("ReadinessRevise", localize("修改队列", "REVISE", data), "revise", M55_READINESS_SLOTS.action_hit(0), false, localize("返回队列并重新保存", "Return to the Party Line and save again", data), false)
|
||||
var dock_band := PageChrome.new().configure("dock_band")
|
||||
dock_band.name = "ReadinessDockBand"
|
||||
_readiness_place(dock_band, DOCK_BAND)
|
||||
dock_band.z_index = 40
|
||||
action_dock.add_child(dock_band)
|
||||
var revise := _readiness_command_button("ReadinessRevise", localize("修改队列", "REVISE", data), "revise", M55_READINESS_SLOTS.action_hit(0), false, localize("返回队列并重新调整顺序", "Return to the Party Line and adjust again", data), false)
|
||||
var back := _readiness_command_button("ReadinessBack", localize("返回公会", "BACK", data), "back", M55_READINESS_SLOTS.action_hit(1), false, localize("返回公会,不改变已保存顺序", "Return to Guild without changing the saved order", data), false)
|
||||
var accept := _readiness_command_button("ReadinessAccept", localize("接受准备", "ACCEPT", data), "accept", M55_READINESS_SLOTS.action_hit(2), not bool(command_data.get("accept_enabled", false)), localize("冻结四人顺序;不会选择风险或开始战斗", "Freeze the four-member order; no risk or battle starts", data), true)
|
||||
var risk := _readiness_command_button("ReadinessRisk", localize("比较风险", "RISK", data), "risk", M55_READINESS_SLOTS.action_hit(3), not bool(command_data.get("risk_enabled", false)), localize("接受准备后比较三个风险选项", "Compare three risk options", data), false)
|
||||
@@ -225,6 +200,89 @@ func _build_overlay(data: Dictionary) -> void:
|
||||
initial_focus.call_deferred("grab_focus")
|
||||
|
||||
|
||||
func _build_header(parent: Control, items: Array) -> void:
|
||||
var count := items.size()
|
||||
var divider_values: Array = []
|
||||
for index in range(1, count):
|
||||
divider_values.append(float(index) / float(count))
|
||||
var band := PageChrome.new().configure("header_band", {"dividers": divider_values})
|
||||
band.name = "ReadinessHeaderBand"
|
||||
_readiness_place(band, HEADER_BAND)
|
||||
band.z_index = 40
|
||||
parent.add_child(band)
|
||||
var slots_left := HEADER_BAND.position.x
|
||||
var width := HEADER_BAND.size.x / float(count)
|
||||
for index in range(count):
|
||||
var item: Dictionary = Dictionary(items[index])
|
||||
var x0 := slots_left + width * float(index)
|
||||
var icon_kind := String(item.get("icon", ""))
|
||||
var text_left := 0.06
|
||||
if not icon_kind.is_empty():
|
||||
var icon := UIIcon.new().configure(icon_kind, "light")
|
||||
icon.name = String(item.get("name", "ReadinessHeader%d" % index)) + "Icon"
|
||||
_readiness_place(icon, Rect2(x0 + width * 0.08, HEADER_BAND.position.y + HEADER_BAND.size.y * 0.30, width * 0.16, HEADER_BAND.size.y * 0.40))
|
||||
icon.z_index = 41
|
||||
parent.add_child(icon)
|
||||
text_left = 0.30
|
||||
var label := AssetChrome.styled_label(
|
||||
String(item.get("text", "")),
|
||||
_readiness_text_size(int(item.get("size", 9))),
|
||||
PageChrome.INK,
|
||||
float(item.get("tracking", 0.0)),
|
||||
float(item.get("embolden", 0.0))
|
||||
)
|
||||
label.name = String(item.get("name", "ReadinessHeaderValue%d" % index))
|
||||
label.anchor_left = x0 + width * text_left
|
||||
label.anchor_right = x0 + width * 0.94
|
||||
label.anchor_top = HEADER_BAND.position.y + HEADER_BAND.size.y * 0.08
|
||||
label.anchor_bottom = HEADER_BAND.position.y + HEADER_BAND.size.y * 0.92
|
||||
label.offset_left = 0.0
|
||||
label.offset_top = 0.0
|
||||
label.offset_right = 0.0
|
||||
label.offset_bottom = 0.0
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.clip_text = true
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
label.z_index = 41
|
||||
parent.add_child(label)
|
||||
|
||||
|
||||
func _build_status_card(parent: Control, command_data: Dictionary, data: Dictionary) -> void:
|
||||
var card := PageChrome.new().configure("card")
|
||||
card.name = "ReadinessStatusCard"
|
||||
_readiness_place(card, STATUS_CARD)
|
||||
card.z_index = 20
|
||||
parent.add_child(card)
|
||||
var rows := [
|
||||
{"name":"ReadinessRiskState", "text":localize("接受准备后可比较风险", "ACCEPT PREPARATION TO COMPARE RISK", data), "color":Color("#3f6b4f") if bool(command_data.get("risk_enabled", false)) else PageChrome.INK_SOFT},
|
||||
{"name":"ReadinessKnockoutRule", "text":localize("击倒成员下一关归队", "A KNOCKED-OUT MEMBER RETURNS NEXT STAGE", data), "color":PageChrome.INK}
|
||||
]
|
||||
for index in range(rows.size()):
|
||||
var item: Dictionary = Dictionary(rows[index])
|
||||
var top := STATUS_CARD.position.y + STATUS_CARD.size.y * (0.16 + float(index) * 0.46)
|
||||
var marker := PageChrome.new().configure("pip", {"shape": "diamond"})
|
||||
marker.name = String(item.get("name", "")) + "Marker"
|
||||
_readiness_place(marker, Rect2(STATUS_CARD.position.x + STATUS_CARD.size.x * 0.05, top + STATUS_CARD.size.y * 0.10, STATUS_CARD.size.x * 0.055, STATUS_CARD.size.y * 0.22))
|
||||
marker.z_index = 21
|
||||
parent.add_child(marker)
|
||||
var label := AssetChrome.styled_label(String(item.get("text", "")), _readiness_text_size(8), Color(item.get("color", PageChrome.INK)), 0.4, 0.0)
|
||||
label.name = String(item.get("name", "ReadinessStatusRow%d" % index))
|
||||
label.anchor_left = STATUS_CARD.position.x + STATUS_CARD.size.x * 0.14
|
||||
label.anchor_right = STATUS_CARD.position.x + STATUS_CARD.size.x * 0.96
|
||||
label.anchor_top = top
|
||||
label.anchor_bottom = top + STATUS_CARD.size.y * 0.42
|
||||
label.offset_left = 0.0
|
||||
label.offset_top = 0.0
|
||||
label.offset_right = 0.0
|
||||
label.offset_bottom = 0.0
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
label.z_index = 21
|
||||
parent.add_child(label)
|
||||
|
||||
|
||||
func _render_invalid(data: Dictionary) -> void:
|
||||
var overlay := Control.new()
|
||||
overlay.name = "ReadinessInvalidOverlay"
|
||||
@@ -258,12 +316,27 @@ func _add_member_name_plate(parent: Control, index: int, slot: Rect2, name_text:
|
||||
var width := slot.size.x * 1.02
|
||||
var cx := slot.position.x + slot.size.x * 0.5
|
||||
var area := Rect2(cx - width * 0.5, slot.position.y + slot.size.y * 1.005, width, 0.052)
|
||||
var plate := AssetChrome.market("ReadinessMemberPlate%d" % index, area, "compact_label")
|
||||
var plate := PageChrome.new().configure("card")
|
||||
plate.name = "ReadinessMemberPlate%d" % index
|
||||
_readiness_place(plate, area)
|
||||
plate.z_index = 31
|
||||
parent.add_child(plate)
|
||||
var label := _readiness_label(parent, "ReadinessMember%dIdentity" % index, Rect2(area.position.x, area.position.y, area.size.x, area.size.y), name_text, 8, Color("#2f2417"))
|
||||
label.z_index = 32
|
||||
var label := AssetChrome.styled_label(name_text, _readiness_text_size(8), PageChrome.INK, 0.4, 0.16)
|
||||
label.name = "ReadinessMember%dIdentity" % index
|
||||
label.anchor_left = area.position.x + 0.012
|
||||
label.anchor_right = area.end.x - 0.012
|
||||
label.anchor_top = area.position.y + area.size.y * 0.12
|
||||
label.anchor_bottom = area.end.y - area.size.y * 0.12
|
||||
label.offset_left = 0.0
|
||||
label.offset_top = 0.0
|
||||
label.offset_right = 0.0
|
||||
label.offset_bottom = 0.0
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
label.clip_text = true
|
||||
label.z_index = 32
|
||||
parent.add_child(label)
|
||||
|
||||
|
||||
func _ground_shadow_texture() -> Texture2D:
|
||||
@@ -300,8 +373,6 @@ func _readiness_label(parent: Control, node_name: String, area: Rect2, text_valu
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_NO_TRIMMING
|
||||
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
if node_name in ["ReadinessFrozenFacts", "ReadinessProtection", "ReadinessReach", "ReadinessPressureCoverage", "ReadinessReserve"]:
|
||||
label.z_index = 21
|
||||
_readiness_place(label, area)
|
||||
parent.add_child(label)
|
||||
return label
|
||||
@@ -311,7 +382,7 @@ func _readiness_text_size(design_size: int) -> int:
|
||||
var canvas_height := design_canvas.size.y if is_instance_valid(design_canvas) else size.y
|
||||
var canvas_scale := clampf(canvas_height / 360.0, 1.0, 2.0)
|
||||
var text_scale := float(presentation.get("text_scale", 1.0))
|
||||
return mini(20, maxi(13, int(round(float(design_size) * canvas_scale * text_scale))))
|
||||
return mini(30, maxi(12, int(round(float(design_size) * canvas_scale * text_scale))))
|
||||
|
||||
|
||||
func _readiness_action_button(node_name: String, area: Rect2, intent_id: String, payload: Dictionary, tooltip: String) -> Button:
|
||||
@@ -372,36 +443,52 @@ func _readiness_warning_button(warning_rows: Array, data: Dictionary) -> Button:
|
||||
button.focus_mode = Control.FOCUS_ALL
|
||||
button.tooltip_text = localize("查看允许的警告与归属", "Review allowed warnings and ownership", data)
|
||||
button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
## The warning parchment is an authored runtime surface; the control stays
|
||||
## transparent and owns only input and the localized rows.
|
||||
for style_name in ["normal", "hover", "pressed", "focus", "disabled", "hover_pressed"]:
|
||||
button.add_theme_stylebox_override(style_name, StyleBoxEmpty.new())
|
||||
_readiness_place(button, WARNING_STACK)
|
||||
button.pressed.connect(func(): emit_intent("review_warnings"), CONNECT_DEFERRED)
|
||||
var lines: Array[String] = []
|
||||
var severities: Array[String] = []
|
||||
for row in warning_rows:
|
||||
if row is Dictionary:
|
||||
lines.append(String(row.get("text", "")))
|
||||
severities.append(String(row.get("severity", "notice")))
|
||||
else:
|
||||
lines.append(String(row))
|
||||
severities.append("notice")
|
||||
while lines.size() < 3:
|
||||
lines.append("")
|
||||
severities.append("notice")
|
||||
if lines.size() > 3:
|
||||
lines = lines.slice(0, 3)
|
||||
severities = severities.slice(0, 3)
|
||||
for index in range(3):
|
||||
var row := Control.new()
|
||||
row.name = "ReadinessWarningRow%d" % index
|
||||
row.anchor_left = 0.08
|
||||
row.anchor_right = 0.92
|
||||
row.anchor_top = 0.20 + float(index) * 0.22
|
||||
row.anchor_bottom = 0.40 + float(index) * 0.22
|
||||
row.anchor_left = 0.0
|
||||
row.anchor_right = 1.0
|
||||
row.anchor_top = float(index) * 0.347
|
||||
row.anchor_bottom = float(index) * 0.347 + 0.290
|
||||
row.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
button.add_child(row)
|
||||
var card := PageChrome.new().configure("card")
|
||||
card.name = "WarningCard"
|
||||
card.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
row.add_child(card)
|
||||
var icon := PageChrome.new().configure("warning_icon", {"severity": severities[index]})
|
||||
icon.name = "WarningIcon"
|
||||
icon.anchor_left = 0.03
|
||||
icon.anchor_right = 0.20
|
||||
icon.anchor_top = 0.14
|
||||
icon.anchor_bottom = 0.86
|
||||
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
row.add_child(icon)
|
||||
var warning_text := _readiness_warning_text(lines[index], data)
|
||||
var warning_colors := [Color("#7a2f28"), Color("#6a4a1f"), Color("#2b5b60")]
|
||||
var label := make_label("• " + warning_text if not warning_text.is_empty() else "", _readiness_text_size(8), warning_colors[index])
|
||||
var label := AssetChrome.styled_label(warning_text, _readiness_text_size(8), PageChrome.INK, 0.4, 0.0)
|
||||
label.name = "WarningLabel"
|
||||
label.anchor_left = 0.02
|
||||
label.anchor_right = 0.95
|
||||
label.anchor_left = 0.24
|
||||
label.anchor_right = 0.97
|
||||
label.anchor_top = 0.0
|
||||
label.anchor_bottom = 1.0
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
@@ -435,36 +522,37 @@ func _readiness_command_button(node_name: String, text_value: String, intent_id:
|
||||
button.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN if disabled else Control.CURSOR_POINTING_HAND
|
||||
button.set_meta("ui_factory_control", true)
|
||||
button.set_meta("ui_semantic_variant", "primary" if primary else "secondary")
|
||||
button.set_meta("ui_shape", "asset")
|
||||
button.set_meta("ui_shape", "dock_plate")
|
||||
button.set_meta("ui_family", "company")
|
||||
for style_name in ["normal", "hover", "pressed", "focus", "disabled", "hover_pressed"]:
|
||||
button.add_theme_stylebox_override(style_name, StyleBoxEmpty.new())
|
||||
_readiness_place(button, area)
|
||||
var plate := TextureRect.new()
|
||||
var state := "disabled" if disabled else "primary" if primary else "secondary"
|
||||
var plate := PageChrome.new().configure("command_plate", {"state": state})
|
||||
plate.name = node_name + "Plate"
|
||||
plate.texture = AssetChrome.scaled(AssetChrome.ACTION_PLATE, 0.5)
|
||||
plate.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||
plate.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||
plate.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS
|
||||
plate.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
plate.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
if disabled:
|
||||
plate.modulate = Color(0.58, 0.58, 0.60, 0.72)
|
||||
elif primary:
|
||||
plate.modulate = Color(0.80, 1.16, 0.94, 1.0)
|
||||
else:
|
||||
plate.modulate = Color(0.86, 0.90, 1.0, 1.0)
|
||||
button.add_child(plate)
|
||||
var icon_kind := "party" if intent_id == "revise" else "back" if intent_id == "back" else "confirm" if intent_id == "accept" else "risk"
|
||||
UIFactory.attach_icon(button, icon_kind, "CommandIcon", "light", Rect2(0.20, 0.20, 0.16, 0.60))
|
||||
var label := make_label(text_value, _readiness_text_size(11 if primary else 10), Color("#f6ecd4"))
|
||||
var icon := UIIcon.new().configure(icon_kind, "dark" if (primary or disabled) else "light")
|
||||
icon.name = "CommandIcon"
|
||||
icon.anchor_left = 0.34
|
||||
icon.anchor_right = 0.66
|
||||
icon.anchor_top = 0.14
|
||||
icon.anchor_bottom = 0.54
|
||||
icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
button.add_child(icon)
|
||||
var label_color := Color("#e8e3d5") if disabled else PageChrome.PAPER_LIGHT if primary else PageChrome.INK
|
||||
var label := AssetChrome.styled_label(text_value, _readiness_text_size(10 if primary else 9), label_color, 0.6, 0.20)
|
||||
label.name = "ButtonLabel"
|
||||
label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
label.offset_left = 52.0
|
||||
label.offset_right = -16.0
|
||||
label.anchor_left = 0.06
|
||||
label.anchor_right = 0.94
|
||||
label.anchor_top = 0.52
|
||||
label.anchor_bottom = 0.92
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
label.clip_text = true
|
||||
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
button.add_child(label)
|
||||
button.pressed.connect(func(): emit_intent(intent_id), CONNECT_DEFERRED)
|
||||
|
||||
@@ -1,66 +1,101 @@
|
||||
class_name ABGM55ReadinessFormationSummary
|
||||
extends Control
|
||||
|
||||
const UIFactory = preload("res://ui/shared/ui_factory.gd")
|
||||
## Departure summary: one parchment card with a purpose title, the exposure
|
||||
## sentence and four aligned coverage columns. Each column is a shape marker,
|
||||
## a value and a pip row, so the numbers read as gauges rather than prose.
|
||||
|
||||
const AssetChrome = preload("res://ui/shared/asset_chrome.gd")
|
||||
const PageChrome = preload("res://ui/shared/page_chrome.gd")
|
||||
|
||||
const COLUMN_SHAPES := ["shield", "circle", "diamond", "circle"]
|
||||
|
||||
var facts := ""
|
||||
var metrics: Array = []
|
||||
var title := "准备摘要"
|
||||
|
||||
|
||||
func configure(next_facts: String, next_metrics: Array, next_title := "准备摘要") -> ABGM55ReadinessFormationSummary:
|
||||
facts = next_facts
|
||||
metrics = next_metrics
|
||||
title = next_title
|
||||
return self
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
clip_contents = true
|
||||
## The summary is the sole owner of its parchment surface; the shared asset
|
||||
## nine-patch replaces the previous page-local flat rectangle.
|
||||
var matte := AssetChrome.paper("ReadinessSummaryBand", Rect2(0.0, 0.0, 1.0, 1.0), 0.5, 70.0, 55.0)
|
||||
add_child(matte)
|
||||
move_child(matte, 0)
|
||||
var title_label := UIFactory.label(title, "label", 1.0, Color("#6a4a1f"))
|
||||
var card := PageChrome.new().configure("card")
|
||||
card.name = "ReadinessSummaryBand"
|
||||
card.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
add_child(card)
|
||||
move_child(card, 0)
|
||||
|
||||
var title_label := AssetChrome.styled_label(title, 15, PageChrome.INK_SOFT, 1.2, 0.34)
|
||||
title_label.name = "ReadinessSummaryTitle"
|
||||
title_label.anchor_left = 0.05
|
||||
title_label.anchor_right = 0.24
|
||||
title_label.anchor_top = 0.22
|
||||
title_label.anchor_bottom = 0.46
|
||||
title_label.anchor_left = 0.03
|
||||
title_label.anchor_right = 0.20
|
||||
title_label.anchor_top = 0.10
|
||||
title_label.anchor_bottom = 0.34
|
||||
title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
title_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
title_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
title_label.clip_text = true
|
||||
add_child(title_label)
|
||||
var divider := ColorRect.new()
|
||||
divider.name = "ReadinessSummaryDivider"
|
||||
divider.anchor_left = 0.05
|
||||
divider.anchor_right = 0.95
|
||||
divider.anchor_top = 0.50
|
||||
divider.anchor_bottom = 0.515
|
||||
divider.color = Color("#9f8252", 0.48)
|
||||
divider.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(divider)
|
||||
var facts_label := UIFactory.label(facts, "body", 1.0, Color("#2b241b"))
|
||||
|
||||
var facts_label := AssetChrome.styled_label(facts, 17, PageChrome.INK, 0.3, 0.0)
|
||||
facts_label.name = "ReadinessFrozenFacts"
|
||||
facts_label.add_theme_font_size_override("font_size", 18)
|
||||
facts_label.anchor_left = 0.24
|
||||
facts_label.anchor_right = 0.96
|
||||
facts_label.anchor_top = 0.16
|
||||
facts_label.anchor_bottom = 0.50
|
||||
facts_label.anchor_left = 0.21
|
||||
facts_label.anchor_right = 0.97
|
||||
facts_label.anchor_top = 0.08
|
||||
facts_label.anchor_bottom = 0.34
|
||||
facts_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
facts_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
facts_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
facts_label.clip_text = true
|
||||
add_child(facts_label)
|
||||
|
||||
var divider := ColorRect.new()
|
||||
divider.name = "ReadinessSummaryDivider"
|
||||
divider.anchor_left = 0.03
|
||||
divider.anchor_right = 0.97
|
||||
divider.anchor_top = 0.365
|
||||
divider.anchor_bottom = 0.378
|
||||
divider.color = Color(PageChrome.GOLD, 0.55)
|
||||
divider.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(divider)
|
||||
|
||||
for index in range(metrics.size()):
|
||||
var item: Dictionary = Dictionary(metrics[index])
|
||||
var label := UIFactory.label(String(item.get("text", "")), "label", 1.0, Color("#493a29"))
|
||||
var left := float(item.get("left", 0.0))
|
||||
var right := float(item.get("right", 0.2))
|
||||
var column_width := right - left
|
||||
var marker := PageChrome.new().configure("pip", {"shape": COLUMN_SHAPES[index % COLUMN_SHAPES.size()], "filled": true})
|
||||
marker.name = String(item.get("name", "ReadinessMetric%d" % index)) + "Marker"
|
||||
marker.anchor_left = left
|
||||
marker.anchor_right = left + column_width * 0.16
|
||||
marker.anchor_top = 0.44
|
||||
marker.anchor_bottom = 0.64
|
||||
marker.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(marker)
|
||||
var label := AssetChrome.styled_label(String(item.get("text", "")), 16, PageChrome.INK, 0.6, 0.20)
|
||||
label.name = String(item.get("name", "ReadinessMetric%d" % index))
|
||||
label.anchor_left = float(item.get("left", 0.0))
|
||||
label.anchor_top = 0.50
|
||||
label.anchor_right = float(item.get("right", 0.2))
|
||||
label.anchor_bottom = 0.78
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.anchor_left = left + column_width * 0.22
|
||||
label.anchor_right = right
|
||||
label.anchor_top = 0.42
|
||||
label.anchor_bottom = 0.66
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.clip_text = true
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
add_child(label)
|
||||
var count := int(item.get("count", 0))
|
||||
var pip_gap := column_width * 0.045
|
||||
var pip_width := (column_width - pip_gap * 3.0) / 4.0
|
||||
for pip_index in range(4):
|
||||
var pip := PageChrome.new().configure("pip", {"shape": COLUMN_SHAPES[index % COLUMN_SHAPES.size()], "filled": pip_index < count})
|
||||
pip.name = "ReadinessPip%d_%d" % [index, pip_index]
|
||||
pip.anchor_left = left + float(pip_index) * (pip_width + pip_gap)
|
||||
pip.anchor_right = pip.anchor_left + pip_width * 0.70
|
||||
pip.anchor_top = 0.72
|
||||
pip.anchor_bottom = 0.92
|
||||
pip.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(pip)
|
||||
|
||||
@@ -20,21 +20,20 @@ const HEADER_VALUE_SLOTS := [
|
||||
Rect2(0.810, 0.036, 0.120, 0.078),
|
||||
]
|
||||
|
||||
## Four equal runtime command slots on the bottom rail.
|
||||
## Four independent command plates on the carved dock band. The primary
|
||||
## (Accept) plate is wider than the three secondary plates.
|
||||
const ACTION_HIT_SLOTS := [
|
||||
Rect2(0.075, 0.855, 0.190, 0.100),
|
||||
Rect2(0.285, 0.855, 0.190, 0.100),
|
||||
Rect2(0.495, 0.855, 0.190, 0.100),
|
||||
Rect2(0.705, 0.855, 0.190, 0.100),
|
||||
Rect2(0.072, 0.852, 0.185, 0.120),
|
||||
Rect2(0.274, 0.852, 0.185, 0.120),
|
||||
Rect2(0.476, 0.852, 0.250, 0.120),
|
||||
Rect2(0.743, 0.852, 0.185, 0.120),
|
||||
]
|
||||
|
||||
## The cream plates are lower than the icon panels. Keep labels inside these
|
||||
## slots so localization cannot collide with the icon or bottom matte.
|
||||
const ACTION_LABEL_SLOTS := [
|
||||
Rect2(0.075, 0.855, 0.190, 0.100),
|
||||
Rect2(0.285, 0.855, 0.190, 0.100),
|
||||
Rect2(0.495, 0.855, 0.190, 0.100),
|
||||
Rect2(0.705, 0.855, 0.190, 0.100),
|
||||
Rect2(0.072, 0.852, 0.185, 0.120),
|
||||
Rect2(0.274, 0.852, 0.185, 0.120),
|
||||
Rect2(0.476, 0.852, 0.250, 0.120),
|
||||
Rect2(0.743, 0.852, 0.185, 0.120),
|
||||
]
|
||||
|
||||
## Member portraits and the right warning card are runtime-owned content
|
||||
|
||||
@@ -96,6 +96,27 @@ static func place(control: Control, area: Rect2) -> void:
|
||||
control.offset_bottom = 0.0
|
||||
|
||||
|
||||
## Typography: one base family with explicit tracking and weight so titles,
|
||||
## values and captions read as different ranks instead of one flat label size.
|
||||
static func styled_font(tracking := 0.0, embolden := 0.0) -> FontVariation:
|
||||
var variation := FontVariation.new()
|
||||
variation.base_font = preload("res://assets/fonts/NotoSansSC.ttf")
|
||||
variation.spacing_glyph = int(round(tracking))
|
||||
variation.variation_embolden = embolden
|
||||
return variation
|
||||
|
||||
|
||||
static func styled_label(value: String, actual_size: int, color := INK, tracking := 0.0, embolden := 0.0) -> Label:
|
||||
var label := Label.new()
|
||||
label.text = value
|
||||
label.add_theme_font_override("font", styled_font(tracking, embolden))
|
||||
label.add_theme_font_size_override("font_size", actual_size)
|
||||
label.add_theme_color_override("font_color", color)
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
return label
|
||||
|
||||
|
||||
static func ink_label(value: String, color := INK, design_size := 14.0) -> Label:
|
||||
var label := Label.new()
|
||||
label.text = value
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
class_name ABGPageChrome
|
||||
extends Control
|
||||
|
||||
## Per-region drawn chrome for first-play pages.
|
||||
##
|
||||
## One generic plaque was previously reused as the header, the command dock,
|
||||
## the warning stack and the summary, which produced large empty frames with
|
||||
## floating text. Each region now gets its own structure: a compartmented
|
||||
## header band, a carved command dock with independent command plates, small
|
||||
## warning cards and parchment cards. Everything scales cleanly at any size and
|
||||
## text stays engine-rendered.
|
||||
|
||||
const PAPER := Color("#f0e0bd")
|
||||
const PAPER_LIGHT := Color("#faf1d8")
|
||||
const PAPER_SHADE := Color("#d9c096")
|
||||
const WOOD := Color("#6f4a2e")
|
||||
const WOOD_LIGHT := Color("#9a6b40")
|
||||
const GOLD := Color("#c2a061")
|
||||
const GOLD_LIGHT := Color("#eed9a4")
|
||||
const INK := Color("#2f2417")
|
||||
const INK_SOFT := Color("#6a5236")
|
||||
const DOCK := Color("#1d3f4a")
|
||||
const DOCK_LIGHT := Color("#2d5b67")
|
||||
const DOCK_DARK := Color("#12272e")
|
||||
const GREEN := Color("#57813f")
|
||||
const GREEN_LIGHT := Color("#7aa85f")
|
||||
const STONE := Color("#4a4f52")
|
||||
const RED := Color("#a8503f")
|
||||
const AMBER := Color("#b98a3c")
|
||||
const TEAL := Color("#4f8a8f")
|
||||
const SHADOW := Color(0.05, 0.06, 0.07, 0.34)
|
||||
|
||||
var variant := "card"
|
||||
var params: Dictionary = {}
|
||||
|
||||
|
||||
func configure(next_variant: String, next_params: Dictionary = {}) -> ABGPageChrome:
|
||||
variant = next_variant
|
||||
params = next_params
|
||||
return self
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
resized.connect(queue_redraw)
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
if size.x < 2.0 or size.y < 2.0:
|
||||
return
|
||||
match variant:
|
||||
"header_band":
|
||||
_draw_header_band()
|
||||
"dock_band":
|
||||
_draw_dock_band()
|
||||
"command_plate":
|
||||
_draw_command_plate()
|
||||
"card":
|
||||
_draw_card()
|
||||
"warning_icon":
|
||||
_draw_warning_icon()
|
||||
"pip":
|
||||
_draw_pip()
|
||||
_:
|
||||
_draw_card()
|
||||
|
||||
|
||||
func _draw_header_band() -> void:
|
||||
var rect := Rect2(2.0, 3.0, size.x - 4.0, size.y - 6.0)
|
||||
_round(Rect2(rect.position + Vector2(0.0, 3.0), rect.size), 7.0, SHADOW)
|
||||
_round(rect, 7.0, PAPER, GOLD, 2.0)
|
||||
_round(rect.grow(-3.0), 5.0, Color(PAPER_LIGHT, 0.45), Color(PAPER_SHADE, 0.85), 1.0)
|
||||
var dividers: Array = params.get("dividers", [])
|
||||
for value in dividers:
|
||||
var x := rect.position.x + rect.size.x * float(value)
|
||||
draw_line(Vector2(x, rect.position.y + 8.0), Vector2(x, rect.end.y - 8.0), Color(GOLD, 0.75), 1.5, true)
|
||||
draw_line(Vector2(rect.position.x + 10.0, rect.position.y + 1.0), Vector2(rect.end.x - 10.0, rect.position.y + 1.0), Color(PAPER_LIGHT, 0.9), 1.5, true)
|
||||
|
||||
|
||||
func _draw_dock_band() -> void:
|
||||
var rect := Rect2(0.0, 0.0, size.x, size.y)
|
||||
draw_rect(Rect2(0.0, 0.0, size.x, size.y * 0.55), DOCK_LIGHT)
|
||||
draw_rect(Rect2(0.0, size.y * 0.55, size.x, size.y * 0.45), DOCK)
|
||||
draw_rect(Rect2(0.0, 0.0, size.x, 2.5), Color(GOLD, 0.85))
|
||||
draw_rect(Rect2(0.0, 2.5, size.x, 2.0), Color(DOCK_DARK, 0.6))
|
||||
draw_rect(Rect2(0.0, size.y - 3.0, size.x, 3.0), Color(DOCK_DARK, 0.7))
|
||||
draw_rect(Rect2(0.0, 0.0, size.x, 1.0), Color(GOLD_LIGHT, 0.9))
|
||||
|
||||
|
||||
func _draw_command_plate() -> void:
|
||||
var state := String(params.get("state", "secondary"))
|
||||
var face := PAPER
|
||||
var edge := GOLD
|
||||
var inner := PAPER_LIGHT
|
||||
var label_line := PAPER_SHADE
|
||||
match state:
|
||||
"primary":
|
||||
face = GREEN
|
||||
edge = GOLD_LIGHT
|
||||
inner = GREEN_LIGHT
|
||||
label_line = Color(0.12, 0.20, 0.10, 0.35)
|
||||
"disabled":
|
||||
face = STONE
|
||||
edge = Color("#6d7074")
|
||||
inner = Color("#5c6064")
|
||||
label_line = Color(0.0, 0.0, 0.0, 0.28)
|
||||
var rect := Rect2(2.0, 3.0, size.x - 4.0, size.y - 7.0)
|
||||
_round(Rect2(rect.position + Vector2(0.0, 4.0), rect.size), 9.0, SHADOW)
|
||||
_round(rect, 9.0, WOOD, Color(0.10, 0.07, 0.05, 0.55), 1.0)
|
||||
var face_rect := rect.grow(-5.0)
|
||||
_round(face_rect, 6.0, face, edge, 2.0)
|
||||
_round(face_rect.grow(-3.0), 4.0, Color(inner, 0.35), Color(edge, 0.55), 1.0)
|
||||
draw_line(Vector2(face_rect.position.x + 8.0, face_rect.position.y + 3.0), Vector2(face_rect.end.x - 8.0, face_rect.position.y + 3.0), Color(PAPER_LIGHT, 0.55) if state != "disabled" else Color(1, 1, 1, 0.12), 1.2, true)
|
||||
draw_line(Vector2(face_rect.position.x + 12.0, face_rect.end.y - 4.0), Vector2(face_rect.end.x - 12.0, face_rect.end.y - 4.0), label_line, 2.0, true)
|
||||
|
||||
|
||||
func _draw_card() -> void:
|
||||
var rect := Rect2(3.0, 3.0, size.x - 6.0, size.y - 6.0)
|
||||
_round(Rect2(rect.position + Vector2(0.0, 3.0), rect.size), 7.0, SHADOW)
|
||||
_round(rect, 7.0, PAPER, GOLD, 2.0)
|
||||
_round(rect.grow(-3.0), 5.0, Color(PAPER_LIGHT, 0.50), Color(PAPER_SHADE, 0.85), 1.0)
|
||||
draw_line(Vector2(rect.position.x + 8.0, rect.position.y + 2.0), Vector2(rect.end.x - 8.0, rect.position.y + 2.0), Color(PAPER_LIGHT, 0.85), 1.2, true)
|
||||
|
||||
|
||||
func _draw_warning_icon() -> void:
|
||||
var severity := String(params.get("severity", "warning"))
|
||||
var fill := RED if severity == "warning" else AMBER if severity == "notice" else TEAL
|
||||
var rect := Rect2(1.0, 1.0, size.x - 2.0, size.y - 2.0)
|
||||
_round(rect, 5.0, Color(0.08, 0.06, 0.05, 0.4))
|
||||
_round(rect.grow(-1.0), 4.0, fill, GOLD_LIGHT, 1.5)
|
||||
var center := rect.get_center()
|
||||
var bar_w := maxf(2.0, rect.size.x * 0.11)
|
||||
draw_rect(Rect2(center.x - bar_w * 0.5, rect.position.y + rect.size.y * 0.26, bar_w, rect.size.y * 0.36), Color(PAPER_LIGHT, 0.95))
|
||||
draw_circle(Vector2(center.x, rect.position.y + rect.size.y * 0.74), bar_w * 0.62, Color(PAPER_LIGHT, 0.95))
|
||||
|
||||
|
||||
func _draw_pip() -> void:
|
||||
var shape := String(params.get("shape", "circle"))
|
||||
var filled := bool(params.get("filled", false))
|
||||
var rect := Rect2(1.5, 1.5, size.x - 3.0, size.y - 3.0)
|
||||
var line := Color(GOLD, 0.95) if filled else Color(GOLD, 0.55)
|
||||
match shape:
|
||||
"shield":
|
||||
var points := PackedVector2Array([
|
||||
Vector2(rect.position.x + rect.size.x * 0.5, rect.position.y),
|
||||
Vector2(rect.end.x, rect.position.y + rect.size.y * 0.28),
|
||||
Vector2(rect.end.x - rect.size.x * 0.16, rect.end.y),
|
||||
Vector2(rect.position.x + rect.size.x * 0.16, rect.end.y),
|
||||
Vector2(rect.position.x, rect.position.y + rect.size.y * 0.28)
|
||||
])
|
||||
if filled:
|
||||
draw_colored_polygon(points, Color(GOLD, 0.35))
|
||||
draw_polyline(points + PackedVector2Array([points[0]]), line, 1.6, true)
|
||||
"diamond":
|
||||
var center := rect.get_center()
|
||||
var radius := minf(rect.size.x, rect.size.y) * 0.5
|
||||
var diamond := PackedVector2Array([center + Vector2(0, -radius), center + Vector2(radius, 0), center + Vector2(0, radius), center + Vector2(-radius, 0)])
|
||||
if filled:
|
||||
draw_colored_polygon(diamond, Color(GOLD, 0.35))
|
||||
draw_polyline(diamond + PackedVector2Array([diamond[0]]), line, 1.6, true)
|
||||
_:
|
||||
var center := rect.get_center()
|
||||
var radius := minf(rect.size.x, rect.size.y) * 0.42
|
||||
if filled:
|
||||
draw_circle(center, radius * 0.8, Color(GOLD, 0.35))
|
||||
draw_arc(center, radius, 0.0, TAU, 22, line, 1.6, true)
|
||||
if filled:
|
||||
draw_circle(rect.get_center(), 1.4, Color(GOLD, 0.95))
|
||||
|
||||
|
||||
func _round(rect: Rect2, radius: float, fill: Color, border := Color(0, 0, 0, 0), width := 0.0) -> void:
|
||||
var style := StyleBoxFlat.new()
|
||||
style.bg_color = fill
|
||||
style.corner_radius_top_left = int(radius)
|
||||
style.corner_radius_top_right = int(radius)
|
||||
style.corner_radius_bottom_left = int(radius)
|
||||
style.corner_radius_bottom_right = int(radius)
|
||||
if width > 0.0:
|
||||
style.border_color = border
|
||||
style.set_border_width_all(int(width))
|
||||
draw_style_box(style, rect)
|
||||
Reference in New Issue
Block a user