Files
cards/scripts/ui/creature_view.gd
team3 f2c609a08b
Some checks are pending
Tests / headless (push) Waiting to run
Tests / fenster-schnell (push) Waiting to run
Tests / fenster-voll (push) Waiting to run
Tests / balance (push) Waiting to run
update
2026-10-13 15:39:55 +02:00

147 lines
6.8 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
class_name CreatureView
extends Control
## Zeichnet ein Wesen auf dem Feld. Bleibt über Zustandsänderungen bestehen (für Animationen).
signal pressed(view: CreatureView, pos: Vector2) # linke Maustaste gedrückt; der Kampf macht daraus Klick oder Ziehen
signal double_clicked(view: CreatureView) # Doppelklick: eigenes Wesen greift das Leben des Gegners an
signal hover(card: Cards.Card, entered: bool)
var creature: Game.Creature
var border = null
var width := 2
var flash := false # kurz rot: wurde gerade angegriffen
var slot: Vector2 # Zielposition im Raster
var busy := false # wird gerade animiert (kein Neupositionieren)
var matchup := 0 # Elementvorteil des gewählten Angreifers gegen dieses fremde Wesen (Game.matchup_bonus); die Werte zeigen dann, was ihn erwartet
var attack_shift := 0 # eigener Angreifer: Elementvorteil gegen das Ziel unter der Maus
var doomed := false # stirbt bei dem Angriff, der gerade unter der Maus liegt
var secret := false # verdecktes fremdes Wesen: nur der Rücken ist zu sehen
var drop := false # Ziel der gerade gezogenen Karte
func _init(cr: Game.Creature, sz: Vector2, b = null, w: int = 2) -> void:
creature = cr
border = b
width = w
custom_minimum_size = sz
size = sz
pivot_offset = sz / 2
set_process(Style.rarity_animated(rarity())) # bewegter Glanz
mouse_filter = Control.MOUSE_FILTER_STOP
mouse_entered.connect(func(): hover.emit(creature.card, true))
mouse_exited.connect(func(): hover.emit(creature.card, false))
func _process(_delta: float) -> void:
queue_redraw()
func _gui_input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
accept_event()
if event.double_click:
double_clicked.emit(self)
else:
pressed.emit(self, event.global_position)
func rarity() -> String:
var owner = creature.owner()
return creature.card.rarity_in(owner.looks if owner != null else {})
func _draw() -> void:
var rect := Rect2(Vector2.ZERO, size)
if secret:
_draw_back(rect)
_draw_drop()
return
var small := size.x < 100
var rar := rarity()
var seed := hash(creature.card.id) + creature.uid
if rar != "Common":
Style.glow(self, rect, Style.rarity_color(rar, seed), 6, 6)
Style.fill_elements(self, rect, creature.card.elements, 6)
var bcol: Color = border if border != null else Style.rarity_frame(rar, seed)
if flash:
bcol = Style.RED
var shiny := Style.rarity_stats_shiny(rar)
# Oben: Kosten links, daneben der Name; das Namensfeld ist genau so hoch wie der Kristall
var fs := 12 if small else 13
var r_cost := 10.0 if small else 11.0
var top_h := r_cost * 2 + 4
draw_rect(Rect2(2, 2, size.x - 4, top_h), Color(0.05, 0.05, 0.08, 0.6))
Style.card_name(self, creature.card.title(), fs, rar, Rect2(r_cost * 2 + 10, 2, size.x - r_cost * 2 - 16, top_h), seed)
# Mitte: das Bild im Kreis; unten Angriff links, Leben rechts
var r_stat := 12.0 if small else 14.0
var pic_top := top_h + 4
var pic_bottom := size.y - r_stat - 4
var r_pic := minf(size.x * 0.42, (pic_bottom - pic_top) / 2)
Style.picture(self, creature.card, Vector2(size.x / 2, (pic_top + pic_bottom) / 2), r_pic, rar, seed)
# Unter dem Namen links die Eigenschaften (in der Farbe des Elements, das sie gibt), rechts der Elementbonus
var icon := 6.0 if small else 7.0
var y := top_h + 6 + icon
for k in creature.keywords():
var col := Color("f2f2f6")
for e in creature.card.elements:
if Cards.ELEMENT_KEYWORD.get(e, "") == k:
col = Style.lighter(Cards.ELEMENT_COLORS[e], 0.45)
Style.keyword_icon(self, k, Vector2(6 + icon, y), icon, col)
y += icon * 2 + 6
if creature.aura != 0:
var badge := "+%d" % creature.aura if creature.aura > 0 else "−%d" % -creature.aura
var bw := Style.FONT_BOLD.get_string_size(badge, HORIZONTAL_ALIGNMENT_LEFT, -1, Style.fs(10)).x + 8
draw_rect(Rect2(size.x - bw - 4, top_h + 5, bw, 16), Color(0.05, 0.05, 0.08, 0.75))
Style.text(self, badge, 10, Style.GREEN if creature.aura > 0 else Style.RED, Vector2(size.x - 4 - bw / 2, top_h + 13), "center", true)
_draw_stats(15 if small else 17, r_stat, shiny)
Style.card_sheen(self, rect, rar, seed)
Style.border(self, rect, bcol, 4 if flash else width, 6)
Style.cost_crystal(self, Vector2(r_cost + 4, r_cost + 4), r_cost, creature.card.cost, 13, shiny)
if creature.hidden:
draw_rect(rect, Color(0.05, 0.05, 0.1, 0.55))
Style.text_outlined(self, tr("verdeckt"), 12, Style.TEXT, Vector2(size.x / 2, size.y / 2), "center", 3)
if doomed:
draw_rect(Rect2(0, size.y * 0.5 - 12, size.x, 24), Color(0.05, 0.02, 0.02, 0.8))
Style.text_outlined(self, tr("stirbt"), 14, Style.RED, Vector2(size.x / 2, size.y * 0.5), "center", 3)
Style.rarity_sparkle(self, rect, rar, seed)
_draw_drop()
## Goldener Rahmen, solange eine Karte zum Verschmelzen darüber schwebt.
func _draw_drop() -> void:
if drop:
Style.border(self, Rect2(Vector2.ZERO, size), Style.GOLD, 5, 6)
## Verdecktes fremdes Wesen: ein Kartenrücken.
func _draw_back(rect: Rect2) -> void:
var bcol: Color = border if border != null else Style.GOLD
if flash:
bcol = Style.RED
draw_style_box(Style.box(Color("3c285a"), 6, bcol, 4 if flash else maxi(width, 2)), rect)
Style.border(self, rect.grow(-10), Color("5a3c82"), 2, 6)
Style.text(self, "?", 36, Style.GOLD, size / 2, "center", true)
## Angriff und Leben für den Angriff, der gerade vorbereitet wird. Fremdes Wesen bei gewähltem
## Angreifer: sein Konter gegen ihn (im Nachteil 1 weniger, im Vorteil 1 mehr, auch bei 0 Angriff),
## das Leben bleibt echt. Eigener Angreifer über einem Ziel: sein Angriff mit Elementvorteil.
func shown_stats() -> Array:
if matchup != 0:
var atk := maxi(0, creature.power() - matchup)
return [atk, creature.health]
if attack_shift != 0:
return [maxi(0, creature.power() + attack_shift), creature.health]
return [creature.power(), creature.health]
## Angriff gelb, Leben blau; die Zahl wird grün, wenn sie für den aktuellen Angriff steigt, rot, wenn sie sinkt.
func _draw_stats(f: int, r: float, shiny: bool) -> void:
var shown := shown_stats()
var atk: int = shown[0]
var hp: int = shown[1]
var atk_text := Style.GREEN if atk > creature.power() else (Style.RED if atk < creature.power() else Color.WHITE)
var hp_text := Style.GREEN if hp > creature.health else (Style.RED if hp < creature.health else Color.WHITE)
Style.stat_circle(self, Vector2(r - 1, size.y - r + 1), r, str(atk), Style.AMBER, f, atk_text, shiny)
Style.stat_circle(self, Vector2(size.x - r + 1, size.y - r + 1), r, str(hp), Style.LIFE, f, hp_text, shiny)