update
This commit is contained in:
1 parent
f2c609a08b
commit
1d563b9eec
34 files changed
+5486
-643
No files matched your search
+165
-36
@@ -1,19 +1,32 @@
|
||||
class_name BoosterScreen
|
||||
extends Control
|
||||
## Booster kaufen und öffnen: unten die Pack-Kacheln der offenen Kapitel (Neutral und die Elemente in
|
||||
## Kapitelreihenfolge, gesperrte sind unsichtbar) mit ×1, ×5, ×10; die Karten fliegen aus dem Pack in ein Raster
|
||||
## (ein Pack: fünf große, mehrere: zehn je Reihe, beste Stufe zuerst) und liegen verdeckt. Klick
|
||||
## oder „Alle aufdecken“ dreht sie gestaffelt um, seltene blitzen auf; danach steht das Fazit.
|
||||
|
||||
const CARD_W := 200
|
||||
const CARD_H := 280
|
||||
const SMALL_W := 108
|
||||
const SMALL_H := 151
|
||||
const AREA := Rect2(40, 100, 1200, 460)
|
||||
const QUANTITIES := [1, 5, 10]
|
||||
|
||||
var app
|
||||
var cards: Array = [] # {"card", "rarity", "upgraded", "dust"} je Karte
|
||||
var cards: Array = [] # {"card", "rarity", "new", "upgraded", "dust"} je Karte
|
||||
var revealed := {}
|
||||
var coins_label: Label
|
||||
var msg_label: Label
|
||||
var b_buy: Button
|
||||
var summary_label: Label
|
||||
var b_buy: Button # Neutral ×1 (Tests)
|
||||
var b_all: Button
|
||||
var scroll: ScrollContainer
|
||||
var slots: Control
|
||||
var fx_layer: Control
|
||||
var fresh := {} # gerade aufgedeckt: wird umgedreht
|
||||
var pack_tiles := {} # Element -> PackTile (Kapitelreihenfolge)
|
||||
var pack_buttons := {} # Element -> der ×1-Knopf
|
||||
var fresh := {} # gerade aufgedeckt: Index -> Verzögerung fürs Umdrehen
|
||||
var fly_from := Vector2.ZERO # woher die Karten einfliegen (Mitte der gekauften Kachel)
|
||||
var fly := false # beim nächsten refresh einfliegen
|
||||
var last_pack := "" # zuletzt gekauftes Pack (pulsiert)
|
||||
|
||||
|
||||
class CardBack extends Control:
|
||||
@@ -39,28 +52,56 @@ class CardBack extends Control:
|
||||
var rect := Rect2(Vector2.ZERO, size)
|
||||
draw_style_box(Style.box(Color("3c285a"), 8, Style.GOLD, 5 if has_focus() else 3), rect)
|
||||
Style.border(self, rect.grow(-15), Color("5a3c82"), 2, 6)
|
||||
Style.text(self, "?", 48, Style.GOLD, size / 2, "center", true)
|
||||
Style.text(self, "?", 48 if size.x >= 180 else 30, Style.GOLD, size / 2, "center", true)
|
||||
|
||||
|
||||
func slot_rect(i: int) -> Rect2:
|
||||
var gap := 20
|
||||
var x0 := (1280 - Profile.BOOSTER_SIZE * CARD_W - (Profile.BOOSTER_SIZE - 1) * gap) / 2
|
||||
return Rect2(x0 + i * (CARD_W + gap), 230, CARD_W, CARD_H)
|
||||
## Pack-Kachel eines Elements: Symbol, Name, Preis; die Kaufknöpfe hängt der Bildschirm an.
|
||||
class PackTile extends Control:
|
||||
var element: String
|
||||
var locked := false
|
||||
var hint := ""
|
||||
var pulse := false
|
||||
var buy_buttons: Array = []
|
||||
|
||||
func _init(e: String, rect: Rect2) -> void:
|
||||
element = e
|
||||
position = rect.position
|
||||
size = rect.size
|
||||
pivot_offset = rect.size / 2
|
||||
|
||||
func _draw() -> void:
|
||||
var col: Color = Cards.ELEMENT_COLORS[element]
|
||||
draw_style_box(Style.box(Style.darker(col, 0.5), 10, Style.GOLD if pulse else Color(col, 0.7), 2), Rect2(Vector2.ZERO, size))
|
||||
Style.element_icon(self, element, Vector2(32, 40), 34, Style.lighter(col, 0.4))
|
||||
Style.text(self, tr(element), 16, Style.TEXT, Vector2(60, 30), "midleft", true)
|
||||
Style.text(self, hint, 13, Style.MUTED, Vector2(60, 52), "midleft")
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
Style.header(self, "Booster", func(): app.show_screen(MenuScreen.new()))
|
||||
coins_label = Style.chip(self, "", Rect2(860, 26, 240, 38))
|
||||
fx_layer = Control.new()
|
||||
fx_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(fx_layer)
|
||||
scroll = ScrollContainer.new()
|
||||
scroll.position = AREA.position
|
||||
scroll.size = AREA.size
|
||||
scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
add_child(scroll)
|
||||
slots = Control.new()
|
||||
slots.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add_child(slots)
|
||||
msg_label = Style.label(self, "", 17, Style.RED, Rect2(0, 640, 1280, 22), "center")
|
||||
b_buy = Style.button(self, "Booster kaufen (%d Münzen)" % Profile.BOOSTER_COST, Rect2(370, 690, 330, 52), buy, Color("5a4628"))
|
||||
b_buy.add_theme_font_size_override("font_size", 20)
|
||||
b_all = Style.button(self, "Alle aufdecken", Rect2(720, 690, 200, 52), reveal_all)
|
||||
slots.mouse_filter = Control.MOUSE_FILTER_PASS
|
||||
slots.custom_minimum_size = AREA.size
|
||||
scroll.add_child(slots)
|
||||
summary_label = Style.label(self, "", 16, Style.GOLD, Rect2(0, 566, 1280, 24), "center", true)
|
||||
b_all = Style.button(self, "Alle aufdecken", Rect2(540, 594, 200, 40), reveal_all)
|
||||
for i in Campaign.CHAPTER_ELEMENTS.size():
|
||||
var e: String = Campaign.CHAPTER_ELEMENTS[i]
|
||||
var tile := PackTile.new(e, Rect2(70 + i * 230, 644, 220, 136))
|
||||
add_child(tile)
|
||||
for k in QUANTITIES.size():
|
||||
var b := Style.button(tile, "×%d" % QUANTITIES[k], Rect2(10 + k * 68, 88, 64, 36), buy.bind(e, QUANTITIES[k]))
|
||||
b.add_theme_font_size_override("font_size", 14)
|
||||
tile.buy_buttons.append(b)
|
||||
pack_tiles[e] = tile
|
||||
pack_buttons[e] = tile.buy_buttons[0]
|
||||
b_buy = pack_buttons[Cards.NEUTRAL]
|
||||
refresh()
|
||||
|
||||
|
||||
@@ -69,19 +110,34 @@ func _input(event: InputEvent) -> void:
|
||||
app.show_screen(MenuScreen.new())
|
||||
|
||||
|
||||
func buy() -> void:
|
||||
## Kauft n Packs eines Elements; die Karten liegen verdeckt, mehrere Packs nach Stufe sortiert.
|
||||
func buy(element: String, n: int) -> void:
|
||||
var p: Profile = app.profile
|
||||
var got: Array = p.open_booster()
|
||||
var before: int = p.coins
|
||||
var got: Array = p.open_boosters(element, n)
|
||||
if got.is_empty():
|
||||
msg_label.text = "Nicht genug Münzen"
|
||||
msg_label.add_theme_color_override("font_color", Style.RED)
|
||||
return
|
||||
app.save()
|
||||
app.sfx.play("play")
|
||||
if n > 1:
|
||||
got.sort_custom(func(a, b): return Cards.RARITIES.find(a.rarity) > Cards.RARITIES.find(b.rarity))
|
||||
cards = got
|
||||
revealed = {}
|
||||
msg_label.text = ""
|
||||
fresh = {}
|
||||
fly = true
|
||||
var tile: PackTile = pack_tiles[element]
|
||||
fly_from = tile.position + tile.size / 2
|
||||
last_pack = element
|
||||
scroll.scroll_vertical = 0
|
||||
refresh()
|
||||
_count_coins(before, p.coins)
|
||||
|
||||
|
||||
## Die Münzen zählen sichtbar herunter.
|
||||
func _count_coins(from: int, to: int) -> void:
|
||||
if not Style.animations or from == to:
|
||||
return
|
||||
create_tween().tween_method(func(v: float): coins_label.text = "Münzen: %d" % int(round(v)), float(from), float(to), 0.5)
|
||||
|
||||
|
||||
func reveal(i: int) -> void:
|
||||
@@ -91,13 +147,17 @@ func reveal(i: int) -> void:
|
||||
refresh()
|
||||
|
||||
|
||||
## Deckt alles auf, von vorn nach hinten gestaffelt; ab Ultra Rare bleibt die Karte kurz allein.
|
||||
func reveal_all() -> void:
|
||||
fresh = {}
|
||||
var k := 0
|
||||
for i in Profile.BOOSTER_SIZE:
|
||||
var delay := 0.0
|
||||
var step := 0.12 if cards.size() <= Profile.BOOSTER_SIZE else 0.06
|
||||
for i in cards.size():
|
||||
if not revealed.has(i):
|
||||
fresh[i] = k * 0.12
|
||||
k += 1
|
||||
fresh[i] = delay
|
||||
delay += step
|
||||
if Cards.RARITIES.find(cards[i].rarity) >= 3:
|
||||
delay += 0.35
|
||||
revealed[i] = true
|
||||
app.sfx.play("play")
|
||||
refresh()
|
||||
@@ -113,7 +173,7 @@ func flip(v: CardView, delay: float, rarity: String) -> void:
|
||||
tw.tween_property(v, "scale:x", 1.0, 0.18).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
||||
if rarity == "Common":
|
||||
return
|
||||
var burst := Style.panel(fx_layer, Rect2(v.position, v.size).grow(6), Color(Cards.RARITY_COLORS[rarity], 0.6), 12)
|
||||
var burst := Style.panel(slots, Rect2(v.position, v.size).grow(6), Color(Cards.RARITY_COLORS[rarity], 0.6), 12)
|
||||
burst.pivot_offset = burst.size / 2
|
||||
burst.modulate.a = 0.0
|
||||
var bt := create_tween()
|
||||
@@ -124,13 +184,71 @@ func flip(v: CardView, delay: float, rarity: String) -> void:
|
||||
bt.tween_callback(burst.queue_free)
|
||||
|
||||
|
||||
## Einflug einer verdeckten Karte aus dem Pack auf ihren Platz, reihenweise gestaffelt.
|
||||
func fly_in(back: Control, target: Vector2, i: int, per_row: int) -> void:
|
||||
if not Style.animations:
|
||||
return
|
||||
back.position = fly_from - AREA.position - back.size / 2
|
||||
back.scale = Vector2(0.4, 0.4)
|
||||
var tw := create_tween().set_parallel()
|
||||
var delay := (i / per_row) * 0.1 + (i % per_row) * 0.03
|
||||
tw.tween_property(back, "position", target, 0.3).set_delay(delay).set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||
tw.tween_property(back, "scale", Vector2.ONE, 0.3).set_delay(delay).set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
|
||||
|
||||
|
||||
## Platz der i-ten Karte: ein Pack fünf große in einer Reihe, sonst zehn kleine je Reihe.
|
||||
func slot_rect(i: int) -> Rect2:
|
||||
var single := cards.size() <= Profile.BOOSTER_SIZE
|
||||
var w := CARD_W if single else SMALL_W
|
||||
var h := CARD_H if single else SMALL_H
|
||||
var per_row := 5 if single else 10
|
||||
var gap := 20 if single else 12
|
||||
var x0 := (AREA.size.x - per_row * w - (per_row - 1) * gap) / 2
|
||||
var row_h := h + (60 if single else 44)
|
||||
return Rect2(x0 + (i % per_row) * (w + gap), 10 + (i / per_row) * row_h, w, h)
|
||||
|
||||
|
||||
## Fazit nach dem Aufdecken: neue Karten, Aufwertungen, Staub.
|
||||
func summary() -> String:
|
||||
if cards.is_empty() or revealed.size() < cards.size():
|
||||
return ""
|
||||
var fresh_n := 0
|
||||
var up := 0
|
||||
var dust := 0
|
||||
for res in cards:
|
||||
if res.get("new", false):
|
||||
fresh_n += 1
|
||||
elif res.upgraded:
|
||||
up += 1
|
||||
dust += int(res.dust)
|
||||
return tr("%d neu · %d aufgewertet · +%d Staub") % [fresh_n, up, dust]
|
||||
|
||||
|
||||
func refresh() -> void:
|
||||
var p: Profile = app.profile
|
||||
coins_label.text = "Münzen: %d" % p.coins
|
||||
b_buy.disabled = p.coins < Profile.BOOSTER_COST
|
||||
b_all.disabled = cards.is_empty() or revealed.size() >= Profile.BOOSTER_SIZE
|
||||
var open: Array = p.unlocked_elements()
|
||||
var x0 := (1280 - open.size() * 220 - (open.size() - 1) * 10) / 2
|
||||
for e in pack_tiles:
|
||||
var tile: PackTile = pack_tiles[e]
|
||||
tile.locked = not p.booster_unlocked(e)
|
||||
tile.visible = not tile.locked
|
||||
if not tile.locked:
|
||||
tile.position = Vector2(x0 + open.find(e) * 230, 644)
|
||||
tile.hint = tr("%d Münzen") % p.pack_price(e)
|
||||
for k in QUANTITIES.size():
|
||||
tile.buy_buttons[k].disabled = tile.locked or p.coins < p.pack_price(e) * QUANTITIES[k]
|
||||
tile.pulse = e == last_pack and not cards.is_empty() and revealed.size() >= cards.size()
|
||||
tile.queue_redraw()
|
||||
b_all.disabled = cards.is_empty() or revealed.size() >= cards.size()
|
||||
summary_label.text = summary()
|
||||
Style.clear(slots)
|
||||
for i in Profile.BOOSTER_SIZE:
|
||||
var single := cards.size() <= Profile.BOOSTER_SIZE
|
||||
var per_row := 5 if single else 10
|
||||
var n := maxi(cards.size(), Profile.BOOSTER_SIZE)
|
||||
var rows := (n + per_row - 1) / per_row
|
||||
slots.custom_minimum_size = Vector2(AREA.size.x, maxf(AREA.size.y, slot_rect(n - 1).end.y + 60))
|
||||
for i in n:
|
||||
var r := slot_rect(i)
|
||||
if cards.is_empty():
|
||||
Style.panel(slots, r, Style.PANEL, 8, Style.PANEL_LIGHT, 2)
|
||||
@@ -142,16 +260,27 @@ func refresh() -> void:
|
||||
slots.add_child(v)
|
||||
if fresh.has(i):
|
||||
flip(v, fresh[i], res.rarity)
|
||||
Style.label(slots, tr(res.rarity), 16, Cards.RARITY_COLORS[res.rarity], Rect2(r.position.x, r.end.y + 8, r.size.x, 24), "center", true)
|
||||
var fs := 16 if single else 12
|
||||
Style.label(slots, tr(res.rarity), fs, Cards.RARITY_COLORS[res.rarity], Rect2(r.position.x - 10, r.end.y + 6, r.size.x + 20, 20), "center", true)
|
||||
var y2 := r.end.y + (30 if single else 24)
|
||||
if res.get("new", false):
|
||||
Style.label(slots, "Neu!", 16, Style.GOLD, Rect2(r.position.x, r.end.y + 32, r.size.x, 24), "center", true)
|
||||
Style.label(slots, "Neu!", fs, Style.GOLD, Rect2(r.position.x, y2, r.size.x, 20), "center", true)
|
||||
elif res.upgraded:
|
||||
Style.label(slots, "Aufgewertet", 16, Style.GREEN, Rect2(r.position.x, r.end.y + 32, r.size.x, 24), "center", true)
|
||||
Style.label(slots, "Aufgewertet", fs, Style.GREEN, Rect2(r.position.x - 10, y2, r.size.x + 20, 20), "center", true)
|
||||
else:
|
||||
Style.label(slots, "+%d Staub" % res.dust, 14, Style.MUTED, Rect2(r.position.x, r.end.y + 32, r.size.x, 24), "center")
|
||||
Style.label(slots, "+%d Staub" % res.dust, fs - 2, Style.MUTED, Rect2(r.position.x, y2, r.size.x, 20), "center")
|
||||
else:
|
||||
var back := CardBack.new(r.size)
|
||||
back.position = r.position
|
||||
back.pivot_offset = r.size / 2
|
||||
back.clicked.connect(reveal.bind(i))
|
||||
slots.add_child(back)
|
||||
if fly:
|
||||
fly_in(back, r.position, i, per_row)
|
||||
fly = false
|
||||
fresh = {}
|
||||
if rows > 0 and last_pack != "" and pack_tiles[last_pack].pulse and Style.animations:
|
||||
var tile: PackTile = pack_tiles[last_pack]
|
||||
var tw := tile.create_tween().set_loops(3)
|
||||
tw.tween_property(tile, "scale", Vector2(1.04, 1.04), 0.35).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
tw.tween_property(tile, "scale", Vector2.ONE, 0.35).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT)
|
||||
Reference in new issue
Block a user