update
Some checks failed
Tests / headless (push) Has been cancelled
Tests / fenster-schnell (push) Has been cancelled
Tests / fenster-voll (push) Has been cancelled
Tests / balance (push) Has been cancelled

This commit is contained in:
team3
2026-10-03 01:17:27 +02:00
parent 24a03ed1ee
commit 5f9442497a
27 changed files with 1040 additions and 871 deletions

View File

@@ -1,23 +1,27 @@
class_name CardView
extends Control
## Zeichnet eine Karte (Laden, Hand, Sammlung, Booster, Vorschau).
## Aufbau wie bei Hearthstone: Kostenkristall oben links, Bild, Namensband, Schlüsselwörter als
## Symbole, Effekttext, Angriff und Leben als Kreise unten. Das Element steht im Hintergrund,
## die Seltenheit in Rahmen, Glanz und Edelstein. Sehr flache Karten (Hand) zeigen nur das Nötigste.
## Zeichnet eine Karte (Hand, Sammlung, Booster, Vorschau).
## Aufbau wie bei Hearthstone: Kostenkristall oben links, Bild, Namensband, Eigenschaften als
## Symbole, Angriff und Leben als Kreise unten. Das erste Element färbt den Hintergrund, weitere
## Elemente zeigt ein Medaillon als Tortenstücke hinter dem Bild; die Seltenheit steckt in Name,
## Rahmen, Glanz und Edelstein (siehe Style). Sehr flache Karten zeigen nur das Nötigste.
signal clicked(view: CardView)
signal pressed(view: CardView, pos: Vector2) # nur mit drag_enabled: der Kampf macht daraus Klick oder Ziehen
signal hover(card: Cards.Card, entered: bool)
var card: Cards.Card
var border = null # Color oder null (dann Rahmen der Seltenheitsstufe)
var rarity := "gewöhnlich" # nur Optik
var rarity := "Common" # nur Optik
var dim := false
var bonus := 0 # Vorschau im Kampf: Elementbonus, den die Karte offen gelegt bekäme (+1 oder -1)
var lift := false # hebt sich beim Zeigen an (Hand und Laden)
var bonus := 0 # Vorschau im Kampf: Elementbonus, den die Karte offen gelegt bekäme
var lift := false # hebt sich beim Zeigen an (Hand)
var drag_enabled := false # Hand im Kampf: Drücken meldet pressed statt clicked
var hold := 0.0 # Fortschritt des Verschmelzens, wenn eine Karte darüber gehalten wird (0..1)
## focusable: per Gamepad oder Tab anwählbar (Sammlung, Booster); A bzw. Enter wirkt wie ein Klick.
func _init(c: Cards.Card, sz: Vector2, focusable: bool = false, rar: String = "gewöhnlich") -> void:
func _init(c: Cards.Card, sz: Vector2, focusable: bool = false, rar: String = "Common") -> void:
card = c
rarity = rar
set_process(Style.rarity_animated(rarity)) # bewegter Glanz
@@ -55,7 +59,10 @@ func _process(_delta: float) -> void:
func _gui_input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
accept_event()
clicked.emit(self)
if drag_enabled:
pressed.emit(self, event.global_position)
else:
clicked.emit(self)
elif has_focus() and event.is_action_pressed("ui_accept"):
accept_event()
clicked.emit(self)
@@ -65,7 +72,7 @@ func _draw() -> void:
var rect := Rect2(Vector2.ZERO, size)
var seed := hash(card.id)
var frame: Color = border if border != null else Style.rarity_frame(rarity, seed)
if rarity != "gewöhnlich":
if rarity != "Common":
Style.glow(self, rect, Style.rarity_color(rarity, seed), 8, 8 if size.x >= 180 else 6)
Style.fill_elements(self, rect, card.elements, 8)
if size.y < 90:
@@ -76,9 +83,14 @@ func _draw() -> void:
draw_rect(rect, Color(0, 0, 0, 0.47))
if has_focus():
Style.border(self, rect, Style.GOLD, 4, 8)
if hold > 0.0:
var c := size / 2
var r := minf(size.x, size.y) * 0.3
draw_circle(c, r + 6, Color(0, 0, 0, 0.55))
draw_arc(c, r, -PI / 2, -PI / 2 + TAU * hold, 40, Style.GOLD, 5.0, true)
## Volle Karte: Laden, Sammlung, Booster, Vorschau.
## Volle Karte: Sammlung, Booster, Vorschau.
func _draw_full(rect: Rect2, frame: Color, seed: int) -> void:
var big := size.x >= 180
var mid := size.x >= 140
@@ -90,14 +102,18 @@ func _draw_full(rect: Rect2, frame: Color, seed: int) -> void:
var icon := 11.0 if big else (9.0 if mid else 7.0)
# Bild: das Elementsymbol füllt den oberen Teil
var band_top: float = size.y * 0.36
Style.glyph(self, card, Vector2(size.x / 2, band_top * 0.56), minf(size.x * 0.62, band_top * 0.95), 0.4)
Style.rarity_sheen(self, rect, rarity, seed)
var gc := Vector2(size.x / 2, band_top * 0.56)
var gs := minf(size.x * 0.62, band_top * 0.95)
if card.is_combo():
Style.element_medallion(self, gc, gs * 0.6, card.elements)
Style.glyph(self, card, gc, gs, 0.4, Style.rarity_relief(rarity))
Style.rarity_sheen(self, rect, rarity, seed, Rect2(0, 0, size.x, band_top))
# Namensband über die volle Breite
var band_h: float = Style.fs(f_name) + 10
var band := Rect2(0, band_top, size.x, band_h)
draw_rect(band, Color(0.05, 0.05, 0.08, 0.82))
var nf := Style.fit_size(card.title(), true, f_name, size.x - 12)
Style.text(self, Style.fit(card.title(), true, Style.fs(nf), size.x - 12), nf, Style.TEXT, Vector2(size.x / 2, band.get_center().y), "center", true)
Style.text(self, Style.fit(card.title(), true, Style.fs(nf), size.x - 12), nf, Style.rarity_name_color(rarity, seed), Vector2(size.x / 2, band.get_center().y), "center", true)
var y := band.end.y + 4
# Kombo eines verschmolzenen Wesens
if card.is_combo():
@@ -146,9 +162,8 @@ func _draw_full(rect: Rect2, frame: Color, seed: int) -> void:
# Werte springen aus den unteren Ecken, dazwischen der Edelstein
Style.stat_circle(self, Vector2(r_stat - 2, size.y - r_stat + 2), r_stat, str(card.attack), Style.AMBER, f_stat)
Style.stat_circle(self, Vector2(size.x - r_stat + 2, size.y - r_stat + 2), r_stat, str(card.health), Style.CRIMSON, f_stat)
if bonus != 0:
Style.text_outlined(self, ("+%d/+%d" % [bonus, bonus]) if bonus > 0 else ("%d/%d" % [bonus, bonus]), f_text,
Style.GREEN if bonus > 0 else Style.RED, Vector2(r_stat * 2 + 4, size.y - r_stat + 2), "midleft", 3)
if bonus > 0:
Style.text_outlined(self, "+%d/+%d" % [bonus, bonus], f_text, Style.GREEN, Vector2(r_stat * 2 + 4, size.y - r_stat + 2), "midleft", 3)
Style.rarity_gem(self, rarity, Vector2(size.x / 2, size.y - r_stat + 1), 8 if big else 6, seed)
Style.border(self, rect, frame, 3 if big else 2, 8)
Style.cost_crystal(self, Vector2(r_cost + 2, r_cost + 2), r_cost, card.cost, f_stat)
@@ -160,7 +175,7 @@ func _draw_compact(rect: Rect2, frame: Color, seed: int) -> void:
Style.rarity_sheen(self, rect, rarity, seed)
Style.border(self, rect, frame, 2, 8)
Style.cost_crystal(self, Vector2(13, 13), 11, card.cost, 13)
var name_w: float = size.x - 30 - (12 if rarity != "gewöhnlich" else 0)
var name_w: float = size.x - 30 - (12 if rarity != "Common" else 0)
var nf := Style.fit_size(card.title(), true, 12, name_w)
Style.text(self, Style.fit(card.title(), true, Style.fs(nf), name_w), nf, Style.TEXT, Vector2(28, 13), "midleft", true)
Style.rarity_gem(self, rarity, Vector2(size.x - 9, 13), 5, seed)
@@ -174,6 +189,6 @@ func _draw_compact(rect: Rect2, frame: Color, seed: int) -> void:
x += 18
Style.stat_circle(self, Vector2(11, size.y - 11), 10, str(card.attack), Style.AMBER, 12)
Style.stat_circle(self, Vector2(size.x - 11, size.y - 11), 10, str(card.health), Style.CRIMSON, 12)
if bonus != 0:
Style.text_outlined(self, ("+%d" % bonus) if bonus > 0 else str(bonus), 10, Style.GREEN if bonus > 0 else Style.RED, Vector2(4, size.y - 29), "midleft", 3)
if bonus > 0:
Style.text_outlined(self, "+%d" % bonus, 10, Style.GREEN, Vector2(4, size.y - 29), "midleft", 3)
Style.rarity_sparkle(self, rect, rarity, seed)