class_name DeckScreen extends Control ## Deck zusammenstellen: alle eigenen Karten im Raster (Filter nach Element und Mana, Stapelkarten ## zuerst, dann höchste Stufen vorn), ein Klick nimmt eine Karte in den Stapel oder wieder heraus. Stapelkarten tragen einen Haken, die anderen sind ## abgedunkelt. Rechts Stapelgröße, Stärke (mittlerer Kartenwert, 100 Grundkarten … 500 Ultimate ## Rare), Manakurve, Elementanteile und die Vorschau der gezeigten Karte. Der Stapel hält DECK_MIN ## bis DECK_MAX Karten; „Auffüllen“ nimmt die wertvollsten eigenen Karten des gewählten Filters. const PREVIEW := Rect2(940, 362, 180, 252) const MAX_COST := 10 var app var filter := "" # Element oder "" var cost_filter := -1 # Kosten 0 bis 9, MAX_COST für 10 und mehr, oder -1 var grid: CardGrid var all_button: Button var element_buttons := {} # Element -> Knopf mit Symbol var cost_buttons: Array = [] # je Kosten ein Knopf var count_label: Label var strength_label: Label var curve: CurveView var share: ElementShare var preview_holder: Control var shown_card: Cards.Card = null # Karte in der Vorschau var msg_label: Label ## Filterknopf mit dem Symbol eines Elements. class ElementButton extends Button: var element: String func _init(e: String) -> void: element = e tooltip_text = tr(e) func _draw() -> void: Style.element_icon(self, element, size / 2, size.y * 0.6, Style.lighter(Cards.ELEMENT_COLORS[element], 0.4)) ## Manakurve: ein Balken je Kosten 0 bis 9 und einer für 10 und mehr, mit der Zahl der Stapelkarten. class CurveView extends Control: var counts: Array = [] func _init(rect: Rect2) -> void: position = rect.position size = rect.size mouse_filter = Control.MOUSE_FILTER_IGNORE func set_counts(c: Array) -> void: counts = c queue_redraw() func _draw() -> void: var n := 11 var gap := 4.0 var bw := (size.x - gap * (n - 1)) / n var top := 16.0 var base := size.y - 16.0 var mx := 1 for v in counts: mx = maxi(mx, int(v)) for i in n: var v: int = int(counts[i]) if i < counts.size() else 0 var x := i * (bw + gap) var h := (base - top) * v / mx draw_rect(Rect2(x, base - 2, bw, 2), Style.PANEL_LIGHT) if v > 0: draw_rect(Rect2(x, base - h, bw, h), Style.BLUE) Style.text(self, str(v), 11, Style.TEXT, Vector2(x + bw / 2, base - h - 8), "center", true) Style.text(self, str(i) if i < n - 1 else str(i) + "+", 11, Style.MUTED, Vector2(x + bw / 2, base + 8), "center") ## Elementanteile des Stapels: je Element eine farbige Kachel mit der Kartenzahl. class ElementShare extends Control: var counts := {} func _init(rect: Rect2) -> void: position = rect.position size = rect.size mouse_filter = Control.MOUSE_FILTER_IGNORE func set_counts(c: Dictionary) -> void: counts = c queue_redraw() func shown() -> Array: return ElementView.all().filter(func(e): return int(counts.get(e, 0)) > 0) func _draw() -> void: var items := shown() if items.is_empty(): return var w := size.x / items.size() for i in items.size(): var e: String = items[i] var col: Color = Cards.ELEMENT_COLORS[e] var r := Rect2(i * w + 2, 0, w - 4, size.y) draw_style_box(Style.box(Color(col, 0.25), 6, Color(col, 0.7), 1), r) Style.text(self, "%s %d" % [tr(e), int(counts[e])], 12, Style.TEXT, r.get_center(), "center", true) func _ready() -> void: Style.header(self, "Deck", func(): app.show_screen(MenuScreen.new())) all_button = Style.button(self, "Alle", Rect2(40, 86, 60, 34), set_filter.bind("")) var filters: Array = Cards.ELEMENTS + [Cards.NEUTRAL] for i in filters.size(): var e: String = filters[i] var fb := ElementButton.new(e) fb.position = Vector2(108 + i * 40, 86) fb.size = Vector2(34, 34) fb.pressed.connect(set_filter.bind(e)) Style.juice(fb) add_child(fb) element_buttons[e] = fb for c in MAX_COST + 1: var cb := Style.button(self, str(c) if c < MAX_COST else str(c) + "+", Rect2(330 + c * 40, 86, 36, 34), set_cost.bind(c)) cb.add_theme_font_size_override("font_size", 14) cost_buttons.append(cb) grid = CardGrid.new(Rect2(40, 132, CardGrid.COLS * (CardGrid.CW + CardGrid.GAP) + 10, 650)) grid.card_clicked.connect(toggle) add_child(grid) Style.panel(self, Rect2(820, 86, 420, 696)) count_label = Style.chip(self, "", Rect2(860, 100, 340, 36), Style.BLUE) strength_label = Style.chip(self, "", Rect2(860, 144, 340, 36), Style.GOLD) Style.label(self, "Manakurve", 13, Style.MUTED, Rect2(850, 190, 360, 20), "center") curve = CurveView.new(Rect2(850, 212, 360, 100)) add_child(curve) share = ElementShare.new(Rect2(850, 322, 360, 30)) add_child(share) preview_holder = Control.new() preview_holder.mouse_filter = Control.MOUSE_FILTER_IGNORE add_child(preview_holder) Style.button(self, "Auffüllen", Rect2(840, 626, 380, 44), fill_up, Style.PRIMARY) Style.button(self, "Leeren", Rect2(840, 678, 380, 40), clear_deck) msg_label = Style.label(self, "", 14, Style.MUTED, Rect2(840, 726, 380, 48), "center") msg_label.autowrap_mode = TextServer.AUTOWRAP_WORD refresh() func _input(event: InputEvent) -> void: if event.is_action_pressed("ui_cancel"): app.show_screen(MenuScreen.new()) func set_filter(e: String) -> void: filter = e refresh() ## Manafilter: derselbe Knopf noch einmal hebt ihn auf. func set_cost(c: int) -> void: cost_filter = -1 if cost_filter == c else c refresh() ## Hebt den aktiven Filterknopf hervor. func _mark(b: Button, active: bool, base: Color) -> void: b.add_theme_stylebox_override("normal", Style.box(Style.lighter(base) if active else base, 8, Style.GOLD if active else Style.MUTED, 2 if active else 1)) b.add_theme_stylebox_override("hover", Style.box(Style.lighter(base), 8, Style.GOLD if active else Style.MUTED, 2)) b.add_theme_stylebox_override("pressed", Style.box(Style.darker(base), 8, Style.GOLD if active else Style.MUTED, 2)) func toggle(card: Cards.Card) -> void: var err: String = app.profile.toggle_deck(card.id) say(err) if err == "": app.sfx.play("click") app.save() refresh() ## Fehler rot, sonst der Hinweis. func say(err: String) -> void: msg_label.text = tr(err) if err != "" else tr("Klick auf eine Karte: rein oder raus.") msg_label.add_theme_color_override("font_color", Style.RED if err != "" else Style.MUTED) ## Füllt mit den wertvollsten eigenen Karten des gewählten Filters bis zum Höchstwert auf. func fill_up() -> void: var p: Profile = app.profile var pool: Array = p.collection.keys() if filter != "": pool = pool.filter(func(id): return filter in Cards.by_id(id).elements) p.deck = DeckGen.fill(p.deck, pool, Game.DECK_MAX, p.collection) app.save() refresh() func clear_deck() -> void: app.profile.deck = [] app.save() refresh() ## Sortierschlüssel einer Karte: im Stapel zuerst, dann höchste Stufen vorn, dann Kosten, dann ID. func sort_key(c: Cards.Card) -> Array: var p: Profile = app.profile return [0 if c.id in p.deck else 1, -p.level(c.id), c.cost, c.id] ## Eigene Karten nach Element- und Manafilter, sortiert (sort_key). func cards() -> Array: var p: Profile = app.profile var out: Array = p.collection.keys().map(func(id): return Cards.by_id(id)) if filter != "": out = out.filter(func(c): return filter in c.elements) if cost_filter >= 0: out = out.filter(func(c): return c.cost == cost_filter or (cost_filter == MAX_COST and c.cost > MAX_COST)) out.sort_custom(func(a, b): return sort_key(a) < sort_key(b)) return out ## Vorschau der gezeigten Karte in ihrer Stufe. func show_card(card: Cards.Card) -> void: var p: Profile = app.profile shown_card = card Style.clear(preview_holder) var v := CardView.new(card.leveled(p.level(card.id)), PREVIEW.size, false, p.rarity(card.id)) v.position = PREVIEW.position v.mouse_filter = Control.MOUSE_FILTER_IGNORE preview_holder.add_child(v) func refresh() -> void: var p: Profile = app.profile count_label.text = tr("Stapel: %d Karten (%d bis %d)") % [p.deck.size(), Game.DECK_MIN, Game.DECK_MAX] count_label.add_theme_color_override("font_color", Style.RED if p.deck.size() < Game.DECK_MIN else Style.BLUE) strength_label.text = tr("Stärke %d") % DeckGen.strength(p.deck, p.collection) _mark(all_button, filter == "", Style.PANEL_LIGHT) var open: Array = p.unlocked_elements() # nur Elemente offener Kapitel for e in element_buttons: var eb: Button = element_buttons[e] eb.visible = e in open if eb.visible: eb.position = Vector2(108 + open.find(e) * 40, 86) _mark(eb, filter == e, Style.darker(Cards.ELEMENT_COLORS[e], 0.8)) for c in cost_buttons.size(): _mark(cost_buttons[c], cost_filter == c, Style.PANEL_LIGHT) var counts := [] counts.resize(11) counts.fill(0) var per_element := {} for id in p.deck: var c := Cards.by_id(id) counts[mini(c.cost, MAX_COST)] += 1 per_element[c.elements[0]] = int(per_element.get(c.elements[0], 0)) + 1 curve.set_counts(counts) share.set_counts(per_element) if msg_label.text == "": say("") var shown := cards() grid.fill(shown.map(func(c): return c.leveled(p.level(c.id))), func(c): return Cards.RARITIES[c.look]) var views := grid.views() for i in views.size(): var in_deck: bool = shown[i].id in p.deck views[i].dim = not in_deck views[i].marked = in_deck views[i].hover.connect(func(c, entered): if entered: show_card(c)) if shown_card != null and p.owns(shown_card.id): show_card(shown_card) elif shown.is_empty(): Style.clear(preview_holder) else: show_card(shown[0])