update
Tests / headless (push) Waiting to run
Tests / fenster-schnell (push) Waiting to run
Tests / fenster-voll (push) Skipped
Tests / balance (push) Skipped

This commit is contained in:
team3 committed 2026-10-15 17:16:01 +02:00
1 parent 1d563b9eec
commit 04fe9716e8
22 files changed
+764 -151

No files matched your search

+1 -1
View File
@@ -113,7 +113,7 @@ func _ready() -> void:
## Eigener Stapel und Kartenstufen und das eigene Merkmal (nach Kapitel 1).
func own_rules() -> Dictionary:
var p: Profile = app.profile
var rules := {"looks": p.looks(), "deck": p.deck_cards()}
var rules := {"looks": p.looks(), "deck": p.deck_cards(), "bonus_attack": p.skill("stark"), "bonus_health": p.skill("zaeh"), "cheap_from": Skills.cheap_from(p.skill("sparsam"))}
var perks := p.battle_perks()
if not perks.is_empty():
rules["human_perks"] = perks
+9 -7
View File
@@ -12,7 +12,7 @@ var node_buttons: Array = []
func _ready() -> void:
var p: Profile = app.profile
chapter = mini(Campaign.chapter(p.campaign_stage), Campaign.CHAPTER_ELEMENTS.size())
chapter = mini(Campaign.chapter(p.campaign_stage), Campaign.chapters())
selected = mini(p.campaign_stage, Campaign.total() - 1)
build()
@@ -24,16 +24,18 @@ func build() -> void:
Style.header(self, "Kampagne", func(): app.show_screen(MenuScreen.new()))
var n := Campaign.STAGES_PER_CHAPTER
var x := 40
for c in Campaign.CHAPTER_ELEMENTS.size():
var b := Style.button(self, "Kapitel %d" % (c + 1), Rect2(x, 88, 130, 38), set_chapter.bind(c + 1), Style.PRIMARY if chapter == c + 1 else null)
for c in Campaign.chapters():
var b := Style.button(self, "Kapitel %d" % (c + 1), Rect2(x, 88, 96, 38), set_chapter.bind(c + 1), Style.PRIMARY if chapter == c + 1 else null)
b.add_theme_font_size_override("font_size", 14)
b.disabled = p.campaign_stage < c * n
x += 138
x += 104
var element := Campaign.element_of(chapter)
Style.label(self, tr(element), 20, Cards.ELEMENT_COLORS[element], Rect2(0, 136, 1280, 30), "center", true)
var round_text := tr(element) if chapter <= Campaign.CHAPTER_ELEMENTS.size() else tr("%s mit Effektkarten") % tr(element)
Style.label(self, round_text, 20, Cards.ELEMENT_COLORS[element], Rect2(0, 136, 700, 30), "center", true)
if p.perk_unlocked():
Style.label(self, "Dein Merkmal:", 15, Style.MUTED, Rect2(740, 96, 150, 24), "right")
Style.label(self, "Dein Merkmal:", 15, Style.MUTED, Rect2(740, 140, 150, 24), "right")
perk_box = OptionButton.new()
perk_box.position = Vector2(900, 88)
perk_box.position = Vector2(900, 132)
perk_box.size = Vector2(340, 38)
perk_box.add_item("keins")
var keys: Array = Game.PERKS.keys()
+4 -3
View File
@@ -24,6 +24,7 @@ func _ready() -> void:
["Kapitel 3 frei", func(): app.profile.campaign_stage = maxi(app.profile.campaign_stage, 20)],
["Kapitel 4 frei", func(): app.profile.campaign_stage = maxi(app.profile.campaign_stage, 30)],
["Kapitel 5 frei", func(): app.profile.campaign_stage = maxi(app.profile.campaign_stage, 40)],
["Werkstatt frei", func(): app.profile.campaign_stage = maxi(app.profile.campaign_stage, 50)],
["Kampagne zurücksetzen", func(): app.profile.campaign_stage = 0]]],
["Sammlung", [["Alle Karten (Common)", func(): _give_all(0)], ["Alle Karten Ultimate Rare", func(): _give_all(4)],
["Eigene Karten +1 Stufe", func():
@@ -36,9 +37,9 @@ func _ready() -> void:
Style.label(self, g[0], 18, Style.TEXT, Rect2(40, y, 200, 30), "left", true)
var x := 250
for entry in g[1]:
var b := Style.button(self, entry[0], Rect2(x, y - 4, 190, 40), _apply.bind(entry[1]))
b.add_theme_font_size_override("font_size", 14)
x += 198
var b := Style.button(self, entry[0], Rect2(x, y - 4, 160, 40), _apply.bind(entry[1]))
b.add_theme_font_size_override("font_size", 13)
x += 168
y += 70
msg_label = Style.label(self, "", 15, Style.GREEN, Rect2(40, y + 10, 1200, 24), "left")
refresh()
+6 -6
View File
@@ -11,7 +11,7 @@ const MAX_COST := 10
var app
var filter := "" # Element oder ""
var cost_filter := -1 # Kosten 0 bis 10 oder -1
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
@@ -37,7 +37,7 @@ class ElementButton extends Button:
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.
## 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 = []
@@ -67,7 +67,7 @@ class CurveView extends Control:
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")
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.
@@ -113,7 +113,7 @@ func _ready() -> void:
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))
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))
@@ -205,7 +205,7 @@ func cards() -> Array:
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 = 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
@@ -242,7 +242,7 @@ func refresh() -> void:
var per_element := {}
for id in p.deck:
var c := Cards.by_id(id)
counts[clampi(c.cost, 0, 10)] += 1
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)
+4 -2
View File
@@ -1,7 +1,7 @@
class_name MenuScreen
extends Control
## Hauptmenü: links Titel und Spielarten (darunter Booster, Sammlung, Skilltree, dann Einstellungen und
## Beenden), rechts drei Karten der eigenen Sammlung als Schaufenster, unten rechts die Cheats.
## Hauptmenü: links Titel und Spielarten (darunter Booster, Sammlung, Skilltree, nach Kapitel 5 die
## Werkstatt, dann Einstellungen und Beenden), rechts drei Karten der eigenen Sammlung als Schaufenster, unten rechts die Cheats.
var app
var coins_label: Label
@@ -29,6 +29,8 @@ func _ready() -> void:
["Skilltree", func(): app.show_screen(SkillScreen.new())]]:
Style.button(self, entry[0], Rect2(x, y, 166, 44), entry[1])
x += 176
if p.workshop_unlocked():
Style.button(self, "Werkstatt", Rect2(x, y, 166, 44), func(): app.show_screen(WorkshopScreen.new()), Color("5a4628"))
y += 56
x = 80
for entry in [["Einstellungen", func(): app.show_screen(SettingsScreen.new())],
+95 -26
View File
@@ -1,43 +1,92 @@
class_name SkillScreen
extends Control
## Skilltree: oben Level, Erfahrung bis zum nächsten Level und freie Skillpunkte; darunter drei
## Äste (Skills.BRANCHES) mit ihren Skills als Karten: Name, Wirkung, aktuelle und nächste Stufe
## in Zahlen (Skills.effect), Stufe und „Lernen“.
## Skilltree: oben Level, Erfahrungsbalken bis zum nächsten Level und freie Skillpunkte; darunter
## vier Äste (Skills.BRANCHES) als senkrechte Pfade: Knoten mit Name, Wirkung, Rangpunkten, „Jetzt“
## und „Nächste“ in Zahlen (Skills.effect), durch Linien verbunden. Der nächste Knoten öffnet, sobald
## der davor eine Stufe hat; „Lernen“ steht nur, wenn lernbar, fertige Knoten zeigen „Gelernt“.
const COL_W := 285
const COL_STEP := 300
const NODE_TOP := 218
const NODE_H := 150
const NODE_STEP := 172
var app
var level_label: Label
var xp_bar: ProgressBar
var xp_label: Label
var points_label: Label
var msg_label: Label
var nodes := {} # Skill-ID -> SkillNode
var learn_buttons := {} # Skill-ID -> Knopf
var rank_labels := {} # Skill-ID -> Label
var now_labels := {} # Skill-ID -> Wirkung jetzt
var next_labels := {} # Skill-ID -> Wirkung der nächsten Stufe
var state_labels := {} # Skill-ID -> „Gelernt“, „Erst … lernen“ oder „Kein Skillpunkt frei“
var links := {} # Skill-ID -> Linie zum Knoten davor
## Ein Knoten des Baums: Rahmen und Rangpunkte zeichnet er selbst, Texte und Knopf sind Kinder.
class SkillNode extends Control:
var rank := 0
var max_rank := 1
var open := true # der Skill davor hat eine Stufe (oder es gibt keinen)
func _init(rect: Rect2, mx: int) -> void:
max_rank = mx
position = rect.position
size = rect.size
func _draw() -> void:
var done := rank >= max_rank
var bg: Color = Style.PANEL_LIGHT if open else Style.darker(Style.PANEL, 0.9)
var border: Color = Style.GREEN if done else (Style.GOLD if rank > 0 else (Style.MUTED if open else Style.darker(Style.MUTED, 0.5)))
draw_style_box(Style.box(bg, 8, border, 2), Rect2(Vector2.ZERO, size))
for i in max_rank: # Rangpunkte oben rechts
var c := Vector2(size.x - 16 - (max_rank - 1 - i) * 18, 21)
draw_circle(c, 6, Style.GOLD if i < rank else Style.darker(Style.MUTED, 0.6))
func _ready() -> void:
Style.header(self, "Skilltree", func(): app.show_screen(MenuScreen.new()))
level_label = Style.chip(self, "", Rect2(40, 86, 220, 38))
xp_label = Style.chip(self, "", Rect2(276, 86, 400, 38), Style.BLUE)
points_label = Style.chip(self, "", Rect2(692, 86, 300, 38), Style.GREEN)
Style.label(self, "Erfahrung sind deine verdienten Münzen. Jedes Level bringt einen Skillpunkt.", 14, Style.MUTED, Rect2(40, 134, 1200, 22), "left")
level_label = Style.chip(self, "", Rect2(40, 86, 160, 38))
xp_bar = ProgressBar.new()
xp_bar.position = Vector2(216, 86)
xp_bar.size = Vector2(520, 38)
xp_bar.show_percentage = false
xp_bar.add_theme_stylebox_override("background", Style.box(Style.PANEL_LIGHT, 19))
xp_bar.add_theme_stylebox_override("fill", Style.box(Style.darker(Style.BLUE, 0.7), 19))
add_child(xp_bar)
xp_label = Style.label(self, "", 16, Style.TEXT, Rect2(216, 86, 520, 38), "center", true)
points_label = Style.chip(self, "", Rect2(752, 86, 260, 38), Style.GREEN)
Style.label(self, "Erfahrung sind deine verdienten Münzen. Jedes Level bringt einen Skillpunkt. Jeder Ast öffnet sich von oben nach unten.", 14, Style.MUTED, Rect2(40, 134, 1200, 22), "left")
for b in Skills.BRANCHES.size():
var branch: String = Skills.BRANCHES[b]
var x := 40 + b * 410
Style.panel(self, Rect2(x, 170, 390, 580))
Style.label(self, branch, 22, Style.GOLD, Rect2(x, 184, 390, 32), "center", true)
var y := 236
for id in Skills.in_branch(branch):
var x := 40 + b * COL_STEP
Style.panel(self, Rect2(x, 166, COL_W, 600))
Style.label(self, branch, 20, Style.GOLD, Rect2(x, 178, COL_W, 30), "center", true)
var path: Array = Skills.in_branch(branch)
for i in path.size():
var id: String = path[i]
var sk: Dictionary = Skills.SKILLS[id]
Style.panel(self, Rect2(x + 16, y, 358, 176), Style.PANEL_LIGHT, 8)
Style.label(self, sk.name, 18, Style.TEXT, Rect2(x + 32, y + 10, 326, 26), "left", true)
Style.label(self, sk.text, 14, Style.MUTED, Rect2(x + 32, y + 38, 326, 22), "left")
now_labels[id] = Style.label(self, "", 14, Style.TEXT, Rect2(x + 32, y + 64, 326, 22), "left")
next_labels[id] = Style.label(self, "", 14, Style.GREEN, Rect2(x + 32, y + 86, 326, 22), "left")
rank_labels[id] = Style.label(self, "", 15, Style.GOLD, Rect2(x + 32, y + 126, 150, 30), "left", true)
learn_buttons[id] = Style.button(self, "Lernen", Rect2(x + 214, y + 122, 144, 40), learn.bind(id), Style.PRIMARY)
y += 192
msg_label = Style.label(self, "", 15, Style.RED, Rect2(0, 760, 1280, 24), "center")
var y := NODE_TOP + i * NODE_STEP
if i > 0:
var line := ColorRect.new()
line.position = Vector2(x + COL_W / 2.0 - 2, y - NODE_STEP + NODE_H)
line.size = Vector2(4, NODE_STEP - NODE_H)
add_child(line)
links[id] = line
var node := SkillNode.new(Rect2(x + 14, y, COL_W - 28, NODE_H), int(sk.max))
add_child(node)
nodes[id] = node
var w := COL_W - 28 - 28
Style.label(node, sk.name, 17, Style.TEXT, Rect2(14, 8, w - 60, 26), "left", true)
var text := Style.label(node, sk.text, 12, Style.MUTED, Rect2(14, 34, w, 34), "left")
text.autowrap_mode = TextServer.AUTOWRAP_WORD
now_labels[id] = Style.label(node, "", 12, Style.TEXT, Rect2(14, 70, w, 20), "left")
next_labels[id] = Style.label(node, "", 12, Style.GREEN, Rect2(14, 88, w, 20), "left")
learn_buttons[id] = Style.button(node, "Lernen", Rect2(14, 110, w, 32), learn.bind(id), Style.PRIMARY)
state_labels[id] = Style.label(node, "", 14, Style.MUTED, Rect2(14, 112, w, 28), "center", true)
msg_label = Style.label(self, "", 15, Style.RED, Rect2(0, 770, 1280, 24), "center")
refresh()
@@ -60,11 +109,31 @@ func refresh() -> void:
var lvl := p.player_level()
var base := Skills.xp_for_level(lvl)
level_label.text = tr("Level %d") % lvl
xp_bar.max_value = Skills.xp_for_level(lvl + 1) - base
xp_bar.value = p.xp - base
xp_label.text = tr("%d / %d EP bis Level %d") % [p.xp - base, Skills.xp_for_level(lvl + 1) - base, lvl + 1]
points_label.text = tr("Skillpunkte: %d") % p.skill_points()
for id in Skills.SKILLS:
var sk: Dictionary = Skills.SKILLS[id]
rank_labels[id].text = tr("Stufe %d / %d") % [p.skill(id), int(sk.max)]
now_labels[id].text = tr("Jetzt: %s") % Skills.effect(id, p.skill(id))
next_labels[id].text = tr("Nächste Stufe: %s") % Skills.effect(id, p.skill(id) + 1) if p.skill(id) < int(sk.max) else tr("Höchste Stufe erreicht")
learn_buttons[id].disabled = p.skill_points() <= 0 or p.skill(id) >= int(sk.max)
var node: SkillNode = nodes[id]
var prev := Skills.previous(id)
node.rank = p.skill(id)
node.open = prev == "" or p.skill(prev) > 0
node.modulate = Color(1, 1, 1, 1 if node.open else 0.55)
node.queue_redraw()
var done := node.rank >= int(sk.max)
now_labels[id].text = tr("Jetzt: %s") % Skills.effect(id, node.rank)
next_labels[id].text = "" if done else tr("Nächste: %s") % Skills.effect(id, node.rank + 1)
var learnable := node.open and not done and p.skill_points() > 0
learn_buttons[id].visible = learnable
var state := ""
if done:
state = tr("Gelernt")
elif not node.open:
state = tr("Erst %s lernen") % tr(Skills.SKILLS[prev].name)
elif not learnable:
state = tr("Kein Skillpunkt frei")
state_labels[id].text = state
state_labels[id].add_theme_color_override("font_color", Style.GREEN if done else Style.MUTED)
if links.has(id):
links[id].color = Style.GOLD if node.open else Style.darker(Style.MUTED, 0.5)
+4 -15
View File
@@ -534,7 +534,7 @@ static func fill_elements(ci: CanvasItem, rect: Rect2, elements: Array, radius:
## bei Ultimate Rare golden und als Relief.
static func picture(ci: CanvasItem, card, c: Vector2, r: float, rar: String, seed: int) -> void:
var src = card.art_card()
var ring := lighter(Cards.ELEMENT_COLORS[src.elements[0]], 0.3)
var ring := lighter(Cards.ELEMENT_COLORS[Cards.strongest_element(src.elements, hash(src.id))], 0.3)
ci.draw_circle(c, r + 3, Color(0.05, 0.05, 0.08, 0.8))
ci.draw_circle(c, r, Color(0.09, 0.09, 0.13, 0.9))
glyph(ci, src, c, r * 1.3, 0.95, rarity_relief(rar))
@@ -616,23 +616,12 @@ static func glyph(ci: CanvasItem, card, center: Vector2, size: float, alpha: flo
## Symbol in einer festen Farbe (Relief) oder, bei durchsichtigem tint, in Elementfarben.
static func _glyph_plain(ci: CanvasItem, card, center: Vector2, size: float, tint: Color, alpha: float = 0.95) -> void:
var variant := absi(hash(card.id)) % 3
var col := lighter(Cards.ELEMENT_COLORS[card.elements[0]], 0.38)
var e := Cards.strongest_element(card.elements, hash(card.id)) # mehrere Elemente: nur das stärkste
var col := lighter(Cards.ELEMENT_COLORS[e], 0.38)
col.a = alpha
if tint.a > 0:
col = tint
var elems: Array = card.elements
if elems.size() == 1:
_element_glyph(ci, elems[0], center, size, variant, col)
return
# Verschmolzen: die ersten beiden Elemente nebeneinander, ab dem dritten ein Ring
_element_glyph(ci, elems[0], center + Vector2(-size * 0.28, 0), size * 0.62, variant, col)
var col2 := lighter(Cards.ELEMENT_COLORS[elems[1]], 0.38)
col2.a = alpha
if tint.a > 0:
col2 = tint
_element_glyph(ci, elems[1], center + Vector2(size * 0.28, 0), size * 0.62, variant, col2)
if elems.size() >= 3:
ci.draw_arc(center, size * 0.72, 0, TAU, 40, Color(col, 0.5), 2)
_element_glyph(ci, e, center, size, variant, col)
## Elementsymbol in einer Farbe (Filterknöpfe, Listen), Grundvariante.
+158
View File
@@ -0,0 +1,158 @@
class_name WorkshopScreen
extends Control
## Werkstatt (nach Kapitel 5): eigene Karten aus Staub bauen. Links Elemente (beliebig viele),
## Angriff, Leben, Eigenschaften (beliebig viele) und der Name; rechts die Vorschau mit Kosten
## (Cards.cost_with), Wert und Preis (Profile.build_price). „Bauen“ legt die Karte als Common in
## Sammlung und Stapel.
var app
var elements: Array = []
var keywords: Array = []
var attack_slider: HSlider
var health_slider: HSlider
var attack_label: Label
var health_label: Label
var element_buttons := {}
var keyword_boxes := {}
var name_box: LineEdit
var info_label: Label
var msg_label: Label
var dust_label: Label
var preview_holder: Control
var built := 0 # für den Vorschlagsnamen
func _ready() -> void:
Style.header(self, "Werkstatt", func(): app.show_screen(MenuScreen.new()))
dust_label = Style.chip(self, "", Rect2(860, 26, 240, 38), Style.BLUE)
Style.panel(self, Rect2(40, 86, 700, 696))
Style.label(self, "Baue eigene Karten aus Staub: so viele Elemente und Eigenschaften du willst, jede kostet Mana.", 14, Style.MUTED, Rect2(60, 98, 660, 22), "left")
Style.label(self, "Elemente", 16, Style.TEXT, Rect2(60, 134, 140, 30), "left", true)
var all_elements: Array = Cards.ELEMENTS + [Cards.NEUTRAL]
for i in all_elements.size():
var e: String = all_elements[i]
var b := DeckScreen.ElementButton.new(e)
b.position = Vector2(200 + i * 48, 130)
b.size = Vector2(40, 40)
b.pressed.connect(toggle_element.bind(e))
Style.juice(b)
add_child(b)
element_buttons[e] = b
Style.label(self, "Angriff", 16, Style.TEXT, Rect2(60, 194, 140, 30), "left", true)
attack_slider = _slider(Rect2(200, 198, 400, 24), 0, 10, 3)
attack_label = Style.label(self, "", 18, Style.AMBER, Rect2(620, 192, 60, 30), "center", true)
Style.label(self, "Leben", 16, Style.TEXT, Rect2(60, 244, 140, 30), "left", true)
health_slider = _slider(Rect2(200, 248, 400, 24), 1, 10, 3)
health_label = Style.label(self, "", 18, Style.LIFE, Rect2(620, 242, 60, 30), "center", true)
Style.label(self, "Eigenschaften", 16, Style.TEXT, Rect2(60, 300, 200, 30), "left", true)
for k in Cards.KEYWORDS.size():
var kw: String = Cards.KEYWORDS[k]
var cb := CheckButton.new()
cb.text = tr(kw)
cb.position = Vector2(200, 300 + k * 44)
cb.size = Vector2(220, 36)
cb.toggled.connect(func(on: bool): toggle_keyword(kw, on))
add_child(cb)
keyword_boxes[kw] = cb
Style.label(self, tr(Cards.KEYWORD_TEXT[kw]), 13, Style.MUTED, Rect2(430, 306 + k * 44, 300, 24), "left")
Style.label(self, "Name", 16, Style.TEXT, Rect2(60, 490, 140, 30), "left", true)
name_box = LineEdit.new()
name_box.position = Vector2(200, 486)
name_box.size = Vector2(400, 36)
name_box.max_length = 24
name_box.text_changed.connect(func(_t: String): refresh())
add_child(name_box)
info_label = Style.label(self, "", 16, Style.GOLD, Rect2(60, 546, 660, 30), "left", true)
Style.button(self, "Bauen", Rect2(60, 600, 300, 50), build, Style.PRIMARY)
msg_label = Style.label(self, "", 15, Style.RED, Rect2(60, 666, 660, 48), "left")
msg_label.autowrap_mode = TextServer.AUTOWRAP_WORD
Style.panel(self, Rect2(760, 86, 480, 696))
preview_holder = Control.new()
preview_holder.mouse_filter = Control.MOUSE_FILTER_IGNORE
add_child(preview_holder)
_suggest_name()
refresh()
func _slider(rect: Rect2, lo: int, hi: int, value: int) -> HSlider:
var s := HSlider.new()
s.position = rect.position
s.size = rect.size
s.min_value = lo
s.max_value = hi
s.step = 1
s.value = value
s.value_changed.connect(func(_v: float): refresh())
add_child(s)
return s
func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_cancel") and not name_box.has_focus():
app.show_screen(MenuScreen.new())
func _suggest_name() -> void:
name_box.text = tr("Eigenbau %d") % (app.profile.customs.size() + 1)
## Ein Element an- oder abwählen.
func toggle_element(e: String) -> void:
if e in elements:
elements.erase(e)
else:
elements.append(e)
say("")
refresh()
func toggle_keyword(kw: String, on: bool) -> void:
if on and not (kw in keywords):
keywords.append(kw)
elif not on:
keywords.erase(kw)
keywords = Cards.KEYWORDS.filter(func(k): return k in keywords)
refresh()
func say(m: String, ok: bool = false) -> void:
msg_label.text = m
msg_label.add_theme_color_override("font_color", Style.GREEN if ok else Style.RED)
## Die Karte, wie sie gebaut würde (für die Vorschau ohne Element neutral).
func preview_card() -> Cards.Card:
var els := elements if not elements.is_empty() else [Cards.NEUTRAL]
return Cards.custom("c_vorschau", name_box.text.strip_edges(), els, int(attack_slider.value), int(health_slider.value), keywords)
func build() -> void:
var p: Profile = app.profile
var err: String = p.build_card(name_box.text, elements, int(attack_slider.value), int(health_slider.value), keywords)
if err != "":
say(tr(err))
return
app.save()
app.sfx.play("combo")
say(tr("Gebaut: %s") % name_box.text.strip_edges(), true)
_suggest_name()
refresh()
func refresh() -> void:
var p: Profile = app.profile
dust_label.text = "Staub: %d" % p.dust
attack_label.text = str(int(attack_slider.value))
health_label.text = str(int(health_slider.value))
for e in element_buttons:
var active: bool = e in elements
var base: Color = Style.darker(Cards.ELEMENT_COLORS[e], 0.8)
element_buttons[e].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))
var c := preview_card()
info_label.text = tr("Kosten %d Mana · Wert %d · Preis %d Staub") % [c.cost, c.value(), p.build_price(c)]
Style.clear(preview_holder)
var v := CardView.new(c, Vector2(240, 336), false, "Common")
v.position = Vector2(880, 160)
v.mouse_filter = Control.MOUSE_FILTER_IGNORE
preview_holder.add_child(v)
Style.label(preview_holder, c.text(), 15, Style.MUTED, Rect2(780, 520, 440, 30), "center")
+1
View File
@@ -0,0 +1 @@
uid://cwy4h3joivrfr