147 lines
6.8 KiB
GDScript
147 lines
6.8 KiB
GDScript
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)
|