saumwacht-godot/scripts/ui/theme/forged_style.gd
Martin Stickelbroeck c2b447610c Baseline: Saumwacht 0.15.0 (Phase 20A) vor dem Umbau zu Champions of Luneraria
Unveränderter Ausgangsstand inklusive Masterprompt und Konzeptbildern
unter assets/. .godot/ und docs/screenshots/ bleiben laut .gitignore
unversioniert; letztere sind unter ../backups gesichert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:00:36 +02:00

69 lines
3.5 KiB
GDScript

@tool
class_name ForgedStyle
extends StyleBox
## Vector-drawn plate for the Saumwacht theme: chamfered corners, a bronze edge,
## an inset line and an optional top bevel. Drawn per frame, so it stays crisp at any UI scale.
@export var fill_top: Color = Color("1b2027")
@export var fill_bottom: Color = Color("12161b")
@export var edge: Color = Color("8a6a3c")
@export var edge_width: float = 1.5
@export var inner: Color = Color(1, 1, 1, .05)
@export var inner_inset: float = 4.0
@export var chamfer: float = 7.0
@export var highlight: Color = Color(1, 1, 1, 0)
@export var shadow: Color = Color(0, 0, 0, 0)
@export var shadow_size: float = 0.0
@export var ornaments: bool = false
@export var ornament_color: Color = Color("c99a58")
## Grows the drawing outside the control; used for focus rings that must not cover content.
@export var grow: float = 0.0
@export var draw_fill: bool = true
static func plate_points(rect: Rect2, cut: float) -> PackedVector2Array:
var c: float = minf(cut, minf(rect.size.x, rect.size.y) * .5)
var p: Vector2 = rect.position
var e: Vector2 = rect.end
return PackedVector2Array([Vector2(p.x + c, p.y), Vector2(e.x - c, p.y), Vector2(e.x, p.y + c), Vector2(e.x, e.y - c),
Vector2(e.x - c, e.y), Vector2(p.x + c, e.y), Vector2(p.x, e.y - c), Vector2(p.x, p.y + c)])
func _draw(item: RID, rect: Rect2) -> void:
var r: Rect2 = rect.grow(grow)
if r.size.x < 2.0 or r.size.y < 2.0:
return
if shadow.a > 0.0 and shadow_size > 0.0:
for layer: int in range(3):
var spread: float = shadow_size * float(layer + 1) / 3.0
var soft := Color(shadow.r, shadow.g, shadow.b, shadow.a / 3.0)
if draw_fill:
var points := plate_points(Rect2(r.position + Vector2(0, spread * .5), r.size).grow(spread), chamfer + spread)
RenderingServer.canvas_item_add_polygon(item, points, PackedColorArray([soft]))
else:
# Rings only glow outwards; the control underneath stays untinted.
var halo := plate_points(r.grow(spread), chamfer + spread)
halo.append(halo[0])
RenderingServer.canvas_item_add_polyline(item, halo, PackedColorArray([soft]), shadow_size / 3.0 + 1.0, true)
if draw_fill:
var points := plate_points(r, chamfer)
var colors := PackedColorArray()
for point: Vector2 in points:
colors.append(fill_top.lerp(fill_bottom, clampf((point.y - r.position.y) / r.size.y, 0.0, 1.0)))
RenderingServer.canvas_item_add_polygon(item, points, colors)
if highlight.a > 0.0:
var y: float = r.position.y + edge_width + 1.0
RenderingServer.canvas_item_add_line(item, Vector2(r.position.x + chamfer, y), Vector2(r.end.x - chamfer, y), highlight, 1.0, true)
if inner.a > 0.0 and inner_inset > 0.0:
var inset := plate_points(r.grow(-inner_inset), maxf(1.0, chamfer - inner_inset * .45))
inset.append(inset[0])
RenderingServer.canvas_item_add_polyline(item, inset, PackedColorArray([inner]), 1.0, true)
if edge.a > 0.0 and edge_width > 0.0:
var outline := plate_points(r.grow(-edge_width * .5), chamfer)
outline.append(outline[0])
RenderingServer.canvas_item_add_polyline(item, outline, PackedColorArray([edge]), edge_width, true)
if ornaments:
# Small rivet diamonds on the long edges mark forged panels without adding frames.
for x: float in [r.position.x + r.size.x * .5]:
for y: float in [r.position.y, r.end.y]:
var size: float = 3.5
var diamond := PackedVector2Array([Vector2(x, y - size), Vector2(x + size, y), Vector2(x, y + size), Vector2(x - size, y)])
RenderingServer.canvas_item_add_polygon(item, diamond, PackedColorArray([ornament_color]))