update
This commit is contained in:
1 parent
f2c609a08b
commit
1d563b9eec
34 files changed
+5486
-643
No files matched your search
+190
-25
@@ -1,38 +1,139 @@
|
||||
class_name DeckScreen
|
||||
extends Control
|
||||
## Deck zusammenstellen: alle eigenen Karten im Raster, ein Klick nimmt eine Karte in den
|
||||
## Stapel oder wieder heraus. Karten außerhalb des Stapels sind abgedunkelt. Der Stapel hält
|
||||
## 40 bis 60 Karten; „Auffüllen“ nimmt die wertvollsten eigenen Karten des gewählten Filters.
|
||||
## „Stärke“ ist der mittlere Kartenwert (100 Grundkarten … 500 Ultimate Rare).
|
||||
## 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 := ""
|
||||
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()))
|
||||
Style.button(self, "Alle", Rect2(40, 86, 60, 34), set_filter.bind(""))
|
||||
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 := Style.button(self, e, Rect2(108 + i * 82, 86, 78, 34), set_filter.bind(e), Style.darker(Cards.ELEMENT_COLORS[e], 0.8))
|
||||
fb.add_theme_font_size_override("font_size", 14)
|
||||
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)
|
||||
var hint := Style.label(self, "", 15, Style.MUTED, Rect2(840, 188, 380, 66), "center")
|
||||
hint.autowrap_mode = TextServer.AUTOWRAP_WORD # vor dem Text, sonst wächst die Breite auf eine Zeile
|
||||
hint.text = "Klick nimmt eine Karte in den Stapel oder heraus. Auffüllen nimmt die wertvollsten Karten des Filters."
|
||||
Style.button(self, "Auffüllen", Rect2(840, 262, 380, 46), fill_up)
|
||||
Style.button(self, "Leeren", Rect2(840, 318, 380, 46), clear_deck, Color("5a3c28"))
|
||||
msg_label = Style.label(self, "", 15, Style.RED, Rect2(840, 382, 380, 48), "center")
|
||||
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()
|
||||
|
||||
|
||||
@@ -46,15 +147,34 @@ func set_filter(e: String) -> void:
|
||||
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)
|
||||
msg_label.text = err
|
||||
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
|
||||
@@ -72,30 +192,75 @@ func clear_deck() -> void:
|
||||
refresh()
|
||||
|
||||
|
||||
## Eigene Karten, die im Stapel zuerst, dann höchste Stufen vorn, dann nach Kosten.
|
||||
## 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)
|
||||
out.sort_custom(func(a, b):
|
||||
var ia: bool = a.id in p.deck
|
||||
var ib: bool = b.id in p.deck
|
||||
if ia != ib:
|
||||
return ia
|
||||
if p.level(a.id) != p.level(b.id):
|
||||
return p.level(a.id) > p.level(b.id)
|
||||
return a.cost < b.cost if a.cost != b.cost else a.id < b.id)
|
||||
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():
|
||||
views[i].dim = not (shown[i].id in p.deck)
|
||||
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])
|
||||
Reference in new issue
Block a user