267 lines
9.9 KiB
GDScript
267 lines
9.9 KiB
GDScript
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 10 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 10 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), 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), 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)
|
|
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[clampi(c.cost, 0, 10)] += 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])
|