308 lines
12 KiB
GDScript
308 lines
12 KiB
GDScript
extends Control
|
|
## Szenen-Mockup des Kampfbildschirms: Umgebung (Material, Licht, Schatten) plus Anordnung.
|
|
## Hintergrundebenen sind Kinder mit show_behind_parent; die Oberfläche zeichnet _draw().
|
|
|
|
const FONT_DIR := "/home/arbeit/projects/cards/assets/fonts/"
|
|
const ELEM := {"Feuer": Color("d8502a"), "Wasser": Color("3a78d8"), "Erde": Color("8a6a3a"), "Luft": Color("8ec0dc")}
|
|
const KW := {"Feuer": "Doppelschlag", "Wasser": "Regeneration", "Erde": "Spott", "Luft": "Flink"}
|
|
|
|
var font: FontFile
|
|
var bold: FontFile
|
|
var light_dir := Vector2(3, 6) # Schattenversatz = Lichtrichtung
|
|
var P := {} # Palette der Szene
|
|
|
|
|
|
func _init() -> void:
|
|
font = FontFile.new()
|
|
font.load_dynamic_font(FONT_DIR + "DejaVuSans.ttf")
|
|
bold = FontFile.new()
|
|
bold.load_dynamic_font(FONT_DIR + "DejaVuSans-Bold.ttf")
|
|
|
|
|
|
func _ready() -> void:
|
|
build_env()
|
|
|
|
|
|
# --- Umgebung: Texturen und Licht als Ebenen hinter der Oberfläche -------------------
|
|
|
|
func layer(tex: Texture2D, rect: Rect2, mod := Color.WHITE, additive := false, behind := true) -> TextureRect:
|
|
var t := TextureRect.new()
|
|
t.texture = tex
|
|
t.position = rect.position
|
|
t.size = rect.size
|
|
t.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
t.stretch_mode = TextureRect.STRETCH_SCALE
|
|
t.modulate = mod
|
|
t.show_behind_parent = behind
|
|
t.mouse_filter = MOUSE_FILTER_IGNORE
|
|
if additive:
|
|
var m := CanvasItemMaterial.new()
|
|
m.blend_mode = CanvasItemMaterial.BLEND_MODE_ADD
|
|
t.material = m
|
|
add_child(t)
|
|
return t
|
|
|
|
|
|
func noise_tex(w: int, h: int, kind: String, freq: float, seed := 1) -> ImageTexture:
|
|
var n := FastNoiseLite.new()
|
|
n.seed = seed
|
|
n.frequency = freq
|
|
match kind:
|
|
"cell":
|
|
n.noise_type = FastNoiseLite.TYPE_CELLULAR
|
|
n.cellular_return_type = FastNoiseLite.RETURN_DISTANCE2_SUB
|
|
"cellfill":
|
|
n.noise_type = FastNoiseLite.TYPE_CELLULAR
|
|
n.cellular_return_type = FastNoiseLite.RETURN_CELL_VALUE
|
|
_:
|
|
n.noise_type = FastNoiseLite.TYPE_SIMPLEX_SMOOTH
|
|
n.fractal_octaves = 4
|
|
return ImageTexture.create_from_image(n.get_image(w, h))
|
|
|
|
|
|
func radial_tex(inner: Color, outer: Color, size := 256) -> GradientTexture2D:
|
|
var g := Gradient.new()
|
|
g.set_color(0, inner)
|
|
g.set_color(1, outer)
|
|
var t := GradientTexture2D.new()
|
|
t.gradient = g
|
|
t.fill = GradientTexture2D.FILL_RADIAL
|
|
t.fill_from = Vector2(0.5, 0.5)
|
|
t.fill_to = Vector2(1.0, 0.5)
|
|
t.width = size
|
|
t.height = size
|
|
return t
|
|
|
|
|
|
func linear_tex(a: Color, b: Color, vertical := true) -> GradientTexture2D:
|
|
var g := Gradient.new()
|
|
g.set_color(0, a)
|
|
g.set_color(1, b)
|
|
var t := GradientTexture2D.new()
|
|
t.gradient = g
|
|
t.fill_to = Vector2(0, 1) if vertical else Vector2(1, 0)
|
|
return t
|
|
|
|
|
|
## Lichtfleck (additiv) an pos mit Radius r.
|
|
func light(pos: Vector2, r: float, col: Color, strength := 0.5) -> void:
|
|
layer(radial_tex(Color(col, strength), Color(col, 0)), Rect2(pos - Vector2(r, r), Vector2(r * 2, r * 2)), Color.WHITE, true)
|
|
|
|
|
|
func vignette_layer(strength := 0.7) -> void:
|
|
layer(radial_tex(Color(0, 0, 0, 0), Color(0, 0, 0, strength)), Rect2(Vector2(-200, -250), Vector2(1680, 1300)))
|
|
|
|
|
|
func build_env() -> void:
|
|
pass
|
|
|
|
|
|
# --- Grundformen -------------------------------------------------------------------
|
|
|
|
static func lighter(c: Color, d: float) -> Color:
|
|
return Color(minf(1, c.r + d), minf(1, c.g + d), minf(1, c.b + d), c.a)
|
|
|
|
static func darker(c: Color, f: float) -> Color:
|
|
return Color(c.r * f, c.g * f, c.b * f, c.a)
|
|
|
|
|
|
func rr(rect: Rect2, radius: int, col: Color) -> void:
|
|
var sb := StyleBoxFlat.new()
|
|
sb.bg_color = col
|
|
sb.set_corner_radius_all(radius)
|
|
draw_style_box(sb, rect)
|
|
|
|
|
|
func border(rect: Rect2, radius: int, col: Color, w: int) -> void:
|
|
var sb := StyleBoxFlat.new()
|
|
sb.draw_center = false
|
|
sb.border_color = col
|
|
sb.set_border_width_all(w)
|
|
sb.set_corner_radius_all(radius)
|
|
draw_style_box(sb, rect)
|
|
|
|
|
|
func shadow(rect: Rect2, radius: int, size: int, alpha := 0.5, offset := Vector2(9, 9)) -> void:
|
|
var sb := StyleBoxFlat.new()
|
|
sb.bg_color = Color(0, 0, 0, 0)
|
|
sb.set_corner_radius_all(radius)
|
|
sb.shadow_color = Color(0, 0, 0, alpha)
|
|
sb.shadow_size = size
|
|
sb.shadow_offset = light_dir if offset == Vector2(9, 9) else offset
|
|
draw_style_box(sb, rect)
|
|
|
|
|
|
func glow(rect: Rect2, radius: int, col: Color, size: int, alpha := 0.55) -> void:
|
|
var sb := StyleBoxFlat.new()
|
|
sb.bg_color = Color(0, 0, 0, 0)
|
|
sb.set_corner_radius_all(radius)
|
|
sb.shadow_color = Color(col, alpha)
|
|
sb.shadow_size = size
|
|
draw_style_box(sb, rect)
|
|
|
|
|
|
## Senkrechter Verlauf innerhalb einer Rundung: Streifen, Rahmen deckt die Kanten.
|
|
func grad(rect: Rect2, radius: int, top: Color, bottom: Color, steps := 24) -> void:
|
|
rr(rect, radius, top.lerp(bottom, 0.5))
|
|
var inner := rect.grow(-radius * 0.3)
|
|
var h := inner.size.y / steps
|
|
for i in steps:
|
|
var t := float(i) / (steps - 1)
|
|
var y := inner.position.y + i * h
|
|
var r := Rect2(inner.position.x, y, inner.size.x, h + 1)
|
|
if i == 0 or i == steps - 1:
|
|
r = r.grow_individual(-radius * 0.6, 0, -radius * 0.6, 0)
|
|
draw_rect(r, top.lerp(bottom, t))
|
|
|
|
|
|
## Innerer Schatten (Vertiefung).
|
|
func inset(rect: Rect2, radius: int, strength := 0.5) -> void:
|
|
for i in 5:
|
|
border(rect.grow(-i), maxi(0, radius - i), Color(0, 0, 0, strength * (0.22 - i * 0.04)), 1)
|
|
|
|
|
|
## Gravur: dunkle Linie mit heller Kante darunter.
|
|
func engrave(rect: Rect2, radius: int, dark: Color, lightc: Color) -> void:
|
|
border(Rect2(rect.position + Vector2(1, 1), rect.size), radius, lightc, 1)
|
|
border(rect, radius, dark, 1)
|
|
|
|
|
|
func circle(c: Vector2, r: float, col: Color) -> void:
|
|
draw_circle(c, r, col, true, -1, true)
|
|
|
|
|
|
func ring(c: Vector2, r: float, col: Color, w: float) -> void:
|
|
draw_arc(c, r, 0, TAU, 64, col, w, true)
|
|
|
|
|
|
## Metallring: dunkel außen, hell innen, Lichtkante oben links.
|
|
func metal_ring(c: Vector2, r: float, w: float, col: Color) -> void:
|
|
ring(c, r, darker(col, 0.45), w + 2)
|
|
ring(c, r, col, w)
|
|
draw_arc(c, r, PI * 1.05, PI * 1.75, 24, lighter(col, 0.3), w * 0.5, true)
|
|
draw_arc(c, r, PI * 0.1, PI * 0.7, 24, darker(col, 0.6), w * 0.5, true)
|
|
|
|
|
|
func txt(s: String, size: int, col: Color, pos: Vector2, anchor := "center", b := false) -> void:
|
|
var f := bold if b else font
|
|
var dim := f.get_string_size(s, HORIZONTAL_ALIGNMENT_LEFT, -1, size)
|
|
var p := pos
|
|
match anchor:
|
|
"center": p -= dim / 2
|
|
"midtop": p.x -= dim.x / 2
|
|
"midleft": p.y -= dim.y / 2
|
|
"midright": p -= Vector2(dim.x, dim.y / 2)
|
|
draw_string(f, Vector2(p.x, p.y + f.get_ascent(size)), s, HORIZONTAL_ALIGNMENT_LEFT, -1, size, col)
|
|
|
|
|
|
func txt_shadow(s: String, size: int, col: Color, pos: Vector2, anchor := "center", b := true) -> void:
|
|
txt(s, size, Color(0, 0, 0, 0.6), pos + Vector2(1, 2), anchor, b)
|
|
txt(s, size, col, pos, anchor, b)
|
|
|
|
|
|
func txt_outlined(s: String, size: int, col: Color, pos: Vector2, ow := 4, ocol := Color(0.05, 0.04, 0.03, 0.95)) -> void:
|
|
var dim := bold.get_string_size(s, HORIZONTAL_ALIGNMENT_LEFT, -1, size)
|
|
var at := pos - dim / 2 + Vector2(0, bold.get_ascent(size))
|
|
draw_string_outline(bold, at, s, HORIZONTAL_ALIGNMENT_LEFT, -1, size, ow, ocol)
|
|
draw_string(bold, at, s, HORIZONTAL_ALIGNMENT_LEFT, -1, size, col)
|
|
|
|
|
|
func fit(s: String, size: int, max_w: float) -> String:
|
|
while bold.get_string_size(s, HORIZONTAL_ALIGNMENT_LEFT, -1, size).x > max_w and s.length() > 3:
|
|
s = s.substr(0, s.length() - 2) + "…"
|
|
return s
|
|
|
|
|
|
# --- Symbole -----------------------------------------------------------------------
|
|
|
|
func glyph(elem: String, c: Vector2, s: float, col: Color, outline_col = null) -> void:
|
|
if outline_col != null:
|
|
for o in [Vector2(-2, 0), Vector2(2, 0), Vector2(0, -2), Vector2(0, 2)]:
|
|
_glyph(elem, c + o, s, outline_col)
|
|
_glyph(elem, c, s, col)
|
|
|
|
|
|
func _glyph(elem: String, c: Vector2, s: float, col: Color) -> void:
|
|
var h := s / 2
|
|
match elem:
|
|
"Feuer":
|
|
for i in 3:
|
|
var off := Vector2((i - 1) * s * 0.3, (0 if i == 1 else s * 0.14))
|
|
var hh := h * (1.0 if i == 1 else 0.62)
|
|
var w := hh * 0.7
|
|
draw_colored_polygon(PackedVector2Array([c + off + Vector2(0, -hh), c + off + Vector2(w * 0.6, -hh * 0.2), c + off + Vector2(w * 0.42, hh * 0.5),
|
|
c + off + Vector2(0, hh), c + off + Vector2(-w * 0.42, hh * 0.5), c + off + Vector2(-w * 0.6, -hh * 0.2)]), col)
|
|
draw_colored_polygon(PackedVector2Array([c + Vector2(0, -h * 0.3), c + Vector2(h * 0.18, h * 0.4), c + Vector2(0, h * 0.8), c + Vector2(-h * 0.18, h * 0.4)]), lighter(col, 0.35))
|
|
"Wasser":
|
|
for i in 2:
|
|
var y := c.y + (i - 0.5) * s * 0.32
|
|
for k in 3:
|
|
var x := c.x - s * 0.38 + k * s * 0.38
|
|
draw_arc(Vector2(x, y), s * 0.19, PI, TAU, 16, col, maxf(2.5, s * 0.1), true)
|
|
"Erde":
|
|
for i in 3:
|
|
var off := (i - 1) * s * 0.38
|
|
var hs := h * (1.0 if i == 1 else 0.6)
|
|
draw_colored_polygon(PackedVector2Array([c + Vector2(off - hs, h * 0.7), c + Vector2(off, -hs), c + Vector2(off + hs, h * 0.7)]), col)
|
|
draw_colored_polygon(PackedVector2Array([c + Vector2(0, -h), c + Vector2(h * 0.3, -h * 0.45), c + Vector2(-h * 0.08, -h * 0.4)]), lighter(col, 0.3))
|
|
"Luft":
|
|
for i in 3:
|
|
var r := h * (1.0 - i * 0.28)
|
|
draw_arc(c + Vector2(i * s * 0.06, 0), r, PI * 0.2 + i * 0.4, PI * 1.5 + i * 0.4, 24, col, maxf(2.5, s * 0.1), true)
|
|
|
|
|
|
func keyword_icon(c: Vector2, r: float, kind: String, col: Color, bg := Color(0.05, 0.04, 0.03, 0.8)) -> void:
|
|
circle(c, r + 3, bg)
|
|
var w := maxf(1.5, r * 0.22)
|
|
match kind:
|
|
"Doppelschlag":
|
|
draw_line(c + Vector2(-r * 0.65, r * 0.65), c + Vector2(r * 0.6, -r * 0.6), col, w, true)
|
|
draw_line(c + Vector2(r * 0.65, r * 0.65), c + Vector2(-r * 0.6, -r * 0.6), col, w, true)
|
|
"Regeneration":
|
|
draw_line(c + Vector2(0, -r * 0.7), c + Vector2(0, r * 0.7), col, w * 1.5, true)
|
|
draw_line(c + Vector2(-r * 0.7, 0), c + Vector2(r * 0.7, 0), col, w * 1.5, true)
|
|
"Spott":
|
|
draw_line(c + Vector2(0, -r * 0.7), c + Vector2(0, r * 0.2), col, w * 1.6, true)
|
|
circle(c + Vector2(0, r * 0.6), w * 0.95, col)
|
|
"Flink":
|
|
for i in 2:
|
|
var x := c.x - r * 0.55 + i * r * 0.55
|
|
draw_polyline(PackedVector2Array([Vector2(x, c.y - r * 0.6), Vector2(x + r * 0.5, c.y), Vector2(x, c.y + r * 0.6)]), col, w, true)
|
|
|
|
|
|
## Wertmarke: Kreis mit Metallring und Zahl.
|
|
func badge(c: Vector2, r: float, value: String, col: Color, fsize: int, ring_col: Color) -> void:
|
|
circle(c + Vector2(1, 2), r + 2, Color(0, 0, 0, 0.45))
|
|
circle(c, r, col)
|
|
circle(c + Vector2(-r * 0.25, -r * 0.3), r * 0.5, Color(1, 1, 1, 0.18))
|
|
metal_ring(c, r, 2.5, ring_col)
|
|
txt_outlined(value, fsize, Color.WHITE, c, 3)
|
|
|
|
|
|
## Kostenkristall: Sechseck mit Facette.
|
|
func crystal(c: Vector2, r: float, cost: String, fsize: int, col: Color, ring_col: Color) -> void:
|
|
var pts := PackedVector2Array()
|
|
for i in 6:
|
|
var a := -PI / 2 + i * PI / 3
|
|
pts.append(c + Vector2(cos(a), sin(a)) * r)
|
|
var closed := pts.duplicate()
|
|
closed.append(pts[0])
|
|
draw_colored_polygon(pts, col)
|
|
draw_colored_polygon(PackedVector2Array([pts[4], pts[5], pts[0], c + Vector2(0, -r * 0.1), c + Vector2(-r * 0.55, r * 0.15)]), Color(1, 1, 1, 0.22))
|
|
draw_polyline(closed, darker(ring_col, 0.5), 4, true)
|
|
draw_polyline(closed, ring_col, 2, true)
|
|
txt_outlined(cost, fsize, Color.WHITE, c, 3)
|
|
|
|
|
|
## Namensband mit eingekerbten Enden.
|
|
func ribbon(rect: Rect2, col: Color, edge: Color) -> void:
|
|
var p := rect.position
|
|
var e := rect.end
|
|
var n := rect.size.y * 0.35
|
|
var pts := PackedVector2Array([Vector2(p.x, p.y), Vector2(e.x, p.y), Vector2(e.x - n, rect.get_center().y), Vector2(e.x, e.y), Vector2(p.x, e.y), Vector2(p.x + n, rect.get_center().y)])
|
|
draw_colored_polygon(PackedVector2Array([Vector2(p.x + 1, p.y + 2), Vector2(e.x + 1, p.y + 2), Vector2(e.x - n + 1, rect.get_center().y + 2), Vector2(e.x + 1, e.y + 2), Vector2(p.x + 1, e.y + 2), Vector2(p.x + n + 1, rect.get_center().y + 2)]), Color(0, 0, 0, 0.4))
|
|
draw_colored_polygon(pts, col)
|
|
var closed := pts.duplicate()
|
|
closed.append(pts[0])
|
|
draw_polyline(closed, edge, 1.5, true)
|