Gegnerauswahl und Kampagne aufgeräumt, Hotseat entfernt

- Einzelspieler: Schwierigkeit, darunter nur die zwei Deckstärken, Gegnerzahl als
  Schieberegler, Starten und Zurück; kein Würfeln, die Gegnerdecks entstehen beim Start
- Kampagne: nur noch Name, Elementpunkte und Knopf, mit Innenabstand in den Zeilen
- Hotseat (Lokal zu mehreren) samt Übergabe-Vorhang, Auswahlbildschirm und Tests entfernt
- Netzwerktests laufen nur mit DINGER_NETZ=1, weil die Bot-Prozesse Minuten brauchen

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPgKvy5W9WRNFHjkm5QsHx
This commit is contained in:
Team3
2026-09-17 21:05:09 +02:00
parent 5c1084fd79
commit 0f2dc22ef6
16 changed files with 95 additions and 381 deletions

View File

@@ -156,7 +156,7 @@ class Player extends RefCounted:
var human: Player # der (erste) menschliche Spieler
var humans: Array = [] # alle menschlichen Spieler (Hotseat: mehrere)
var humans: Array = [] # alle menschlichen Spieler (im Netz mehrere)
var opponents: Array = [] # alle außer dem ersten Menschen
var players: Array = [] # in Zugreihenfolge
var winner_player: Player = null
@@ -746,19 +746,3 @@ static func new_battle_with(deck: Array, opps: Array, r: RandomNumberGenerator =
p.max_life = 35
g.start(fixed_order)
return g
## Hotseat: mehrere Menschen am selben Rechner, dazu KI-Gegner.
static func new_hotseat(human_decks: Array, n_ai: int, r: RandomNumberGenerator = null) -> Game:
assert(human_decks.size() >= 1 and human_decks.size() + n_ai <= MAX_OPPONENTS + 1, "höchstens 6 am Tisch")
if r == null:
r = make_rng()
var first := Player.new("Spieler 1", human_decks[0], true, START_LIFE)
var others := []
for i in range(1, human_decks.size()):
others.append(Player.new("Spieler %d" % (i + 1), human_decks[i], true, START_LIFE))
for i in n_ai:
others.append(random_opponent(r, i + 1))
var g := Game.new(first, others, r)
g.start()
return g

View File

@@ -64,9 +64,6 @@ func move(dir: int) -> void:
func confirm() -> void:
if screen.handover.visible:
screen.take_over()
return
if screen.mulligan.visible:
screen.do_mulligan([])
return

View File

@@ -1,14 +1,12 @@
class_name BattleScreen
extends Control
## Kampf: Zustand, Eingabe und Abgleich der Ansicht. Layout in BattleLayout, Animationen in BattleFx.
## Modi: Einzelspieler, Kampagne, Anleitung, Hotseat (Perspektivwechsel), Netzwerk (Host oder Mitspieler).
## Modi: Einzelspieler, Kampagne, Anleitung, Netzwerk (Host, Mitspieler oder Zuschauer).
var app
var n_opp: int
var stage := -1 # Kampagnenstufe oder -1
var tutorial := false
var hotseat_decks: Array = []
var hotseat_ai := 0
var net_play := false # Netzwerkspiel (auch als Zuschauer)
var online_seat := -1 # Netzwerk: eigener Platz, Zuschauer -1
var net: Net
@@ -41,7 +39,6 @@ var gp_index: int:
get:
return pad.index
var challenge := {} # wöchentliche Herausforderung oder leer
var preset_opponents: Array = [] # aus der Gegnervorschau übernommene Gegner
var skip_ai := false # Gegnerzüge im Schnelldurchlauf
var smart_ai := true # Vorausschau-KI (Leicht: einfache KI)
var undo_state: Game = null # Zustand vor der letzten rückgängig machbaren Aktion
@@ -83,8 +80,6 @@ var reward_label: Label
var notice_label: Label
var confirm: Control
var b_save_leave: Button
var handover: Control
var handover_label: Label
var mulligan: BattleMulligan
var mulligan_box: Control:
get:
@@ -129,13 +124,6 @@ func _init(n: int = 2, campaign_stage: int = -1) -> void:
stage = campaign_stage
static func hotseat(decks: Array, n_ai: int) -> BattleScreen:
var b := BattleScreen.new(0, -1)
b.hotseat_decks = decks
b.hotseat_ai = n_ai
return b
static func online(seat: int) -> BattleScreen:
var b := BattleScreen.new(0, -1)
b.net_play = true
@@ -150,12 +138,6 @@ static func guided(part: int = 1) -> BattleScreen:
return b
static func against(opps: Array) -> BattleScreen:
var b := BattleScreen.new(opps.size(), -1)
b.preset_opponents = opps
return b
## Unterbrochenen Kampf aus der Datei fortsetzen (siehe save_battle).
static func resume(data: Dictionary) -> BattleScreen:
var s := BattleScreen.new(int(data.get("n_opp", 1)), int(data.get("stage", -1)))
@@ -171,10 +153,6 @@ static func weekly(ch: Dictionary) -> BattleScreen:
return b
func is_hotseat() -> bool:
return game != null and game.humans.size() > 1 and not net_play
func is_online() -> bool:
return net_play
@@ -189,7 +167,7 @@ func is_client() -> bool:
func counts_for_profile() -> bool:
return not is_hotseat() and not is_online()
return not is_online()
func _ready() -> void:
@@ -213,8 +191,6 @@ func _ready() -> void:
return # der erste Zustand kommt vom Host
elif saved_game != null:
game = saved_game
elif not hotseat_decks.is_empty():
game = Game.new_hotseat(hotseat_decks, hotseat_ai)
elif tutorial:
guide = BattleTutorial.new(self, tutorial_part)
var r := Game.make_rng()
@@ -229,8 +205,6 @@ func _ready() -> void:
elif stage >= 0:
var r := Game.make_rng()
game = Game.new_battle_with(app.profile.deck_cards(), Campaign.opponents(stage, r, app.profile.target_rating()), r, false, own_rules())
elif not preset_opponents.is_empty():
game = Game.new_battle_with(app.profile.deck_cards(), preset_opponents, null, false, own_rules())
else:
game = Game.new_battle(app.profile.deck_cards(), n_opp, null, own_rules())
smart_ai = bool(app.profile.difficulty().smart) or not counts_for_profile()
@@ -336,12 +310,6 @@ func _build_static() -> void:
b_save_leave = Style.button(confirm, "Speichern und zurück", Rect2(550, 420, 180, 46), save_and_leave, Color("285a3c"))
b_save_leave.add_theme_font_size_override("font_size", 14)
Style.button(confirm, "Weiterspielen", Rect2(745, 420, 180, 46), func(): confirm.visible = false)
# Übergabe zwischen Menschen (Hotseat)
handover = _overlay()
Style.panel(handover, Rect2(340, 280, 600, 220), Style.PANEL, 10, Style.GOLD, 2)
handover_label = Style.label(handover, "", 30, Style.TEXT, Rect2(340, 310, 600, 44), "center", true)
Style.label(handover, "Maus weitergeben, dann weiter. Die Hand des Vorgängers ist verdeckt.", 16, Style.MUTED, Rect2(340, 362, 600, 24), "center")
Style.button(handover, "Weiter", Rect2(540, 420, 200, 50), take_over, Color("285a3c"))
func _layer() -> Control:
@@ -475,7 +443,7 @@ func my_turn() -> bool:
func any_overlay() -> bool:
return confirm.visible or handover.visible or mulligan.visible or overlay.visible
return confirm.visible or mulligan.visible or overlay.visible
func _input(event: InputEvent) -> void:
@@ -506,9 +474,7 @@ func _input(event: InputEvent) -> void:
if b_undo.visible:
undo()
KEY_ENTER, KEY_KP_ENTER:
if handover.visible:
take_over()
elif mulligan.visible:
if mulligan.visible:
do_mulligan([])
elif overlay.visible:
leave()
@@ -552,12 +518,7 @@ func _process(delta: float) -> void:
return
var cur := game.current()
if cur.is_human:
if cur != perspective and is_hotseat() and not handover.visible:
handover_label.text = "%s ist dran" % cur.name
handover.visible = true
hand_layer.visible = false
trap_layer.visible = false
elif cur == perspective and not spectating() and cur.turns == 1 and not cur.mulligan_done and not mulligan.visible and not handover.visible:
if cur == perspective and not spectating() and cur.turns == 1 and not cur.mulligan_done and not mulligan.visible:
show_mulligan()
return
if is_client():
@@ -623,17 +584,6 @@ func _finish() -> void:
refresh()
func take_over() -> void:
handover.visible = false
hand_layer.visible = true
trap_layer.visible = true
perspective = game.current()
cancel()
zoom_player = null
build_hero_boxes()
refresh()
func say(m: String) -> void:
msg_t = 2.5
msg_label.text = m
@@ -1110,7 +1060,6 @@ func refresh() -> void:
reward_label.text = "Gemeinsame Partie, keine Münzen"
overlay.visible = true
confirm.visible = false
handover.visible = false
mulligan.visible = false

View File

@@ -36,10 +36,6 @@ func build() -> void:
p.perk = "" if i == 0 else keys[i - 1]
app.save())
pr.add_child(perk_box)
var intro := ["Zehn Magier je Kapitel. Jede Stufe bringt mehr Gegner an den Tisch. Bosse haben Merkmale (★).",
"Die Magier kehren mit zwei Merkmalen und größerem Gefolge zurück.",
"Kapitel 3: drei Merkmale, volle Tische und die stärksten Decks."]
Style.clabel(col, intro[chapter - 1], 14, Style.MUTED)
var offset := (chapter - 1) * Campaign.STAGES.size()
for k in Campaign.STAGES.size():
var i := offset + k
@@ -50,31 +46,27 @@ func build() -> void:
panel.add_theme_stylebox_override("panel", Style.box(Style.PANEL if not done else Style.darker(Style.PANEL, 0.8), 6,
Style.GOLD if next else null, 2 if next else 0))
col.add_child(panel)
var r := Style.row(panel, 10)
var pad := MarginContainer.new() # Innenabstand, damit Name und Knopf nicht am Rahmen kleben
for side in ["left", "right"]:
pad.add_theme_constant_override("margin_" + side, 14)
panel.add_child(pad)
var r := Style.row(pad, 10)
r.custom_minimum_size = Vector2(0, 50)
Style.clabel(r, str(i + 1), 18, Style.GOLD if next else Style.MUTED, "center", true, 36)
Style.clabel(r, "%s · %s" % [st.name, st.title], 16, Style.TEXT if not done else Style.MUTED, "left", true, 290)
var nm := Style.clabel(r, "%s · %s" % [st.name, st.title], 16, Style.TEXT if not done else Style.MUTED, "left", true, 420)
nm.size_flags_horizontal = Control.SIZE_EXPAND_FILL # schiebt die Punkte und den Knopf nach rechts
var dots := Control.new()
dots.custom_minimum_size = Vector2(50, 0)
dots.custom_minimum_size = Vector2(60, 0)
dots.mouse_filter = Control.MOUSE_FILTER_IGNORE
var elems: Array = st.elements
dots.draw.connect(func():
for m in elems.size():
dots.draw_circle(Vector2(10 + m * 18, dots.size.y / 2), 7, Cards.ELEMENT_COLORS[elems[m]]))
r.add_child(dots)
var extra: int = mini(st.extra, Game.MAX_OPPONENTS - 1)
var table: String = "Duell" if extra == 0 else "%d am Tisch" % (extra + 2)
var perks_text: String = ", ".join(st.perks.map(func(pk): return Game.PERKS[pk]))
var target := Campaign.target_rating(i, p.target_rating())
Style.clabel(r, "%s · Stärke %.1f · %d Münzen\n★ %s" % [table, target, st.reward, perks_text], 12, Style.MUTED, "left", false, 360)
if done:
Style.clabel(r, "abgeschlossen", 13, Style.GREEN, "right", false, 100)
Style.cbutton(r, "Wiederholen", Vector2(130, 36), _start.bind(i))
elif next:
Style.clabel(r, "", 13, Style.GREEN, "right", false, 100)
Style.cbutton(r, "Kämpfen", Vector2(130, 36), _start.bind(i), Color("285a3c"))
else:
Style.clabel(r, "", 13, Style.GREEN, "right", false, 100)
var b := Style.cbutton(r, "Gesperrt", Vector2(130, 36), func(): pass)
b.disabled = true
if p.campaign_stage >= Campaign.total():

View File

@@ -1,111 +0,0 @@
class_name HotseatScreen
extends Control
## Lokal zu mehreren am selben Rechner: Spielerzahl, KI-Zahl und Decks wählen.
var app
var n_humans := 2
var n_ai := 0
var deck_choice: Array = [] # je Mensch: Slot-Index oder -1 für Zufallsdeck
var human_buttons: Array = []
var ai_buttons: Array = []
var deck_rows: VBoxContainer
var b_start: Button
var hint: Label
func _ready() -> void:
var col := Style.column(self, 760, 40, 10)
Style.clabel(col, "Lokal zu mehreren", 36, Style.GOLD, "center", true)
Style.clabel(col, "Alle spielen am selben Rechner und reichen die Maus weiter. Jeder gegen jeden, ohne Münzen.", 16, Style.MUTED)
Style.spacer(col, 10)
Style.clabel(col, "Spieler", 20, Style.TEXT, "center", true)
var hr := Style.row(col, 10)
hr.alignment = BoxContainer.ALIGNMENT_CENTER
for n in range(2, 5):
human_buttons.append(Style.cbutton(hr, "%d Spieler" % n, Vector2(150, 44), func(): set_humans(n)))
Style.clabel(col, "KI-Gegner", 20, Style.TEXT, "center", true)
var ar := Style.row(col, 10)
ar.alignment = BoxContainer.ALIGNMENT_CENTER
for n in range(0, 5):
ai_buttons.append(Style.cbutton(ar, "%d KI" % n if n > 0 else "keine KI", Vector2(120, 44), func(): set_ai(n)))
Style.spacer(col, 6)
Style.clabel(col, "Decks", 20, Style.TEXT, "center", true)
deck_rows = VBoxContainer.new()
deck_rows.add_theme_constant_override("separation", 6)
col.add_child(deck_rows)
Style.spacer(col, 6)
hint = Style.clabel(col, "", 15, Style.ORANGE)
var br := Style.row(col, 20)
br.alignment = BoxContainer.ALIGNMENT_CENTER
b_start = Style.cbutton(br, "Start", Vector2(200, 48), start, Color("285a3c"))
Style.cbutton(br, "Zurück", Vector2(160, 48), func(): app.show_screen(MenuScreen.new()))
for i in 4:
deck_choice.append(-1)
var p: Profile = app.profile
if p.deck_valid()[0]:
deck_choice[0] = p.active_deck
refresh()
func _input(event: InputEvent) -> void:
if event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE:
app.show_screen(MenuScreen.new())
func set_humans(n: int) -> void:
n_humans = n
n_ai = mini(n_ai, Game.MAX_OPPONENTS + 1 - n_humans)
refresh()
func set_ai(n: int) -> void:
n_ai = n
refresh()
func valid_slots() -> Array:
var out := []
for i in Profile.DECK_SLOTS:
if app.profile.deck_valid(i)[0]:
out.append(i)
return out
func refresh() -> void:
var p: Profile = app.profile
for i in human_buttons.size():
human_buttons[i].disabled = (i + 2) == n_humans
for i in ai_buttons.size():
ai_buttons[i].disabled = i == n_ai or n_humans + i > Game.MAX_OPPONENTS + 1
Style.clear(deck_rows)
var slots := valid_slots()
for h in n_humans:
var r := Style.row(deck_rows, 12)
r.alignment = BoxContainer.ALIGNMENT_CENTER
Style.clabel(r, "Spieler %d" % (h + 1), 16, Style.TEXT, "right", true, 140)
var ob := OptionButton.new()
ob.custom_minimum_size = Vector2(340, 36)
ob.add_item("Zufallsdeck", -1)
for slot in slots:
var elems := Game.deck_elements(p.deck_cards(slot))
ob.add_item("%s (%s)" % [p.deck_name(slot), ", ".join(elems)], slot)
ob.select(ob.get_item_index(deck_choice[h]) if deck_choice[h] >= 0 and slots.has(deck_choice[h]) else 0)
var idx := h
ob.item_selected.connect(func(i: int): deck_choice[idx] = ob.get_item_id(i))
r.add_child(ob)
hint.text = "%d am Tisch" % (n_humans + n_ai)
func start() -> void:
var p: Profile = app.profile
var r := Game.make_rng()
var decks := []
for h in n_humans:
var slot: int = deck_choice[h]
if slot >= 0 and p.deck_valid(slot)[0]:
decks.append(p.deck_cards(slot))
else:
var pool: Array = Cards.ELEMENTS.duplicate()
Game.shuffle(pool, r)
decks.append(Game.build_opponent_deck(r, [pool[0], pool[1]]))
app.show_screen(BattleScreen.hotseat(decks, n_ai))

View File

@@ -1 +0,0 @@
uid://crlne4y0uiygl

View File

@@ -4,7 +4,6 @@ extends Control
var app
var b_single: Button
var b_campaign: Button
var b_hotseat: Button
var b_online: Button
var b_tutorial: Button
var b_challenge: Button
@@ -44,7 +43,6 @@ func _ready() -> void:
Color("5a4628") if not (p0.tutorial_done and p0.tutorial2_done) else null)
b_single = Style.cbutton(grid, "Einzelspieler", sz, func(): app.show_screen(OpponentSelectScreen.new()), Color("285a3c"))
b_campaign = Style.cbutton(grid, "Kampagne", sz, func(): app.show_screen(CampaignScreen.new()), Color("285a3c"))
b_hotseat = Style.cbutton(grid, "Lokal zu mehreren", sz, func(): app.show_screen(HotseatScreen.new()), Color("285a3c"))
b_online = Style.cbutton(grid, "Netzwerkspiel", sz, func(): app.show_screen(LobbyScreen.new()), Color("285a3c"))
b_challenge = Style.cbutton(grid, "Herausforderung", sz, func(): app.show_screen(ChallengeScreen.new()), Color("285a3c"))
Style.cbutton(grid, "Booster öffnen", sz, func(): app.show_screen(BoosterScreen.new()))

View File

@@ -1,109 +1,79 @@
class_name OpponentSelectScreen
extends Control
## Einzelspieler: Schwierigkeit, Gegnerzahl und Vorschau der Gegner (Element, Stärke).
## Einzelspieler: Schwierigkeit wählen und die Gegnerzahl schieben. Die Gegnerdecks
## werden erst beim Start zufällig gezogen (siehe BattleScreen und DeckGen).
var app
var rating_label: Label
var tip_label: Label
var diff_buttons := {}
var previews := {} # n -> Array von Game.Player
var preview_labels := {}
var b_tip: Button
var b_reroll: Button
var rerolls_left := 3
var count_slider: HSlider
var count_label: Label
var info_label: Label
func _ready() -> void:
var col := Style.column(self, 1000, 30, 10)
var col := Style.column(self, 1000, 60, 14)
Style.clabel(col, "Einzelspieler", 36, Style.GOLD, "center", true)
Style.clabel(col, "Jeder kämpft gegen jeden, alle nach denselben Regeln mit %d Leben. Du gewinnst, wenn alle anderen besiegt sind." % Game.START_LIFE, 15, Style.MUTED)
Style.spacer(col, 10)
Style.clabel(col, "Schwierigkeit", 20, Style.TEXT, "center", true)
var dr := Style.row(col, 10)
dr.alignment = BoxContainer.ALIGNMENT_CENTER
for key in ["leicht", "normal", "schwer"]:
var k: String = key
diff_buttons[k] = Style.cbutton(dr, Profile.DIFFICULTIES[k].name, Vector2(150, 40), func(): set_difficulty(k))
rating_label = Style.clabel(col, "", 14, Style.MUTED)
var tr_row := Style.row(col, 10)
tr_row.alignment = BoxContainer.ALIGNMENT_CENTER
tip_label = Style.clabel(tr_row, "", 14, Style.GOLD, "right", false, 520)
b_tip = Style.cbutton(tr_row, "Übernehmen", Vector2(140, 32), take_tip)
Style.spacer(col, 2)
for i in range(1, Game.MAX_OPPONENTS + 1):
var n := i
var r := Style.row(col, 16)
r.alignment = BoxContainer.ALIGNMENT_CENTER
Style.cbutton(r, "%d Gegner" % n, Vector2(150, 46), func(): start(n), Color("285a3c"))
var info := (tr("Duell") if n == 1 else tr("%d am Tisch") % (n + 1)) + " · " + tr("%d Münzen") % Profile.REWARD_WIN[n]
Style.clabel(r, info, 13, Style.MUTED, "left", false, 150)
preview_labels[n] = Style.clabel(r, "", 13, Style.TEXT, "left", false, 600)
preview_labels[n].autowrap_mode = TextServer.AUTOWRAP_WORD
preview_labels[n].custom_minimum_size = Vector2(600, 0)
preview_labels[n].size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
Style.spacer(col, 4)
rating_label = Style.clabel(col, "", 15, Style.MUTED)
Style.spacer(col, 24)
count_label = Style.clabel(col, "", 20, Style.TEXT, "center", true)
var sr := Style.row(col, 12)
sr.alignment = BoxContainer.ALIGNMENT_CENTER
Style.clabel(sr, "1", 15, Style.MUTED, "right", false, 20)
count_slider = HSlider.new()
count_slider.min_value = 1
count_slider.max_value = Game.MAX_OPPONENTS
count_slider.step = 1
count_slider.value = 1
count_slider.tick_count = Game.MAX_OPPONENTS
count_slider.ticks_on_borders = true
count_slider.custom_minimum_size = Vector2(420, 32)
count_slider.value_changed.connect(func(_v): refresh())
sr.add_child(count_slider)
Style.clabel(sr, str(Game.MAX_OPPONENTS), 15, Style.MUTED, "left", false, 20)
info_label = Style.clabel(col, "", 15, Style.MUTED)
Style.spacer(col, 30)
var br := Style.row(col, 16)
br.alignment = BoxContainer.ALIGNMENT_CENTER
b_reroll = Style.cbutton(br, "Neu würfeln", Vector2(190, 40), reroll)
Style.cbutton(br, "Zurück", Vector2(150, 40), func(): app.show_screen(MenuScreen.new()))
refresh(true)
Style.cbutton(br, "Starten", Vector2(190, 46), start, Color("285a3c"))
Style.cbutton(br, "Zurück", Vector2(150, 46), func(): app.show_screen(MenuScreen.new()))
refresh()
func set_difficulty(key: String) -> void:
app.profile.settings.difficulty = key
app.save()
refresh(true)
refresh()
func take_tip() -> void:
var tip: String = app.profile.suggest_difficulty()
if tip != "":
set_difficulty(tip)
func opponents() -> int:
return int(count_slider.value)
## Höchstens dreimal neu würfeln, sonst ließen sich schwache Gegner erwürfeln.
func reroll() -> void:
if rerolls_left <= 0:
return
rerolls_left -= 1
refresh(true)
func start() -> void:
app.show_screen(BattleScreen.new(opponents()))
func start(n: int) -> void:
app.show_screen(BattleScreen.against(previews[n]))
## Baut die Gegner für jede Anzahl vor und zeigt Element und Stärke; der Kampf nutzt genau diese.
func refresh(reroll: bool = false) -> void:
func refresh() -> void:
var p: Profile = app.profile
var d := p.difficulty()
for key in diff_buttons:
var b: Button = diff_buttons[key]
var active: bool = key == p.settings.difficulty
b.add_theme_stylebox_override("normal", Style.box(Color("285a3c") if active else Style.PANEL_LIGHT, 8, Style.GOLD if active else Style.MUTED, 2))
b.add_theme_stylebox_override("hover", Style.box(Style.lighter(Color("285a3c") if active else Style.PANEL_LIGHT), 8, Style.GOLD if active else Style.MUTED, 2))
var arche: String = tr("schwache Archetypen") if d.tier == "leicht" else (tr("starke Archetypen") if d.tier == "schwer" else tr("alle Archetypen"))
rating_label.text = tr("Deine Deckstärke %.1f · Gegnerdecks %.1f · %s · Münzen x%.2f · KI: %s") % [
p.deck_rating(), p.target_rating(), arche, d.reward, tr("Vorausschau") if d.smart else tr("einfach")]
var tip := p.suggest_difficulty()
tip_label.text = (tr("Tipp: %s") % ((tr("%d Siege in Folge, versuche %s") % [p.records.streak, tr(Profile.DIFFICULTIES[tip].name)]) if p.records.streak >= 3
else (tr("%d Niederlagen in Folge, versuche %s") % [p.records.loss_streak, tr(Profile.DIFFICULTIES[tip].name)]))) if tip != "" else ""
b_tip.visible = tip != ""
if reroll:
var r := Game.make_rng()
for n in range(1, Game.MAX_OPPONENTS + 1):
var opps := []
for i in n:
opps.append(Game.random_opponent(r, i + 1, Game.START_LIFE, p.target_rating(), str(d.tier)))
previews[n] = opps
b_reroll.text = tr("Neu würfeln (%d)") % rerolls_left
b_reroll.disabled = rerolls_left <= 0
for n in previews:
var parts: Array[String] = []
for o in previews[n]:
parts.append("%s (%.1f)" % [o.title, Game.deck_rating(o.deck)])
preview_labels[n].text = ", ".join(parts)
rating_label.text = tr("Deine Deckstärke %.1f · Gegnerdecks %.1f") % [p.deck_rating(), p.target_rating()]
var n := opponents()
count_label.text = tr("1 Gegner") if n == 1 else tr("%d Gegner") % n
info_label.text = (tr("Duell") if n == 1 else tr("%d am Tisch") % (n + 1)) + " · " + tr("%d Münzen") % Profile.REWARD_WIN[n]
func _input(event: InputEvent) -> void:
if event is InputEventKey and event.pressed and event.keycode == KEY_ESCAPE:
if event.is_action_pressed("ui_cancel"):
app.show_screen(MenuScreen.new())

View File

@@ -300,7 +300,6 @@ static func rules_text() -> String:
lines.append("Ein unterbrochener Einzelspieler-Kampf lässt sich im Menü fortsetzen.")
lines.append("")
lines.append("Werkstatt: Karten zerlegen bringt Staub, aus Staub entstehen neue Karten. Fünf Deck-Slots.")
lines.append("Lokal zu mehreren: bis zu vier Menschen am selben Rechner, dazu KI, ohne Münzen.")
lines.append("Netzwerkspiel: Fällt der Host aus, übernimmt ein Mitspieler; Zuschauer sehen die Partie ohne Hände.")
lines.append("Mulligan: Im ersten eigenen Zug darfst du einmal Handkarten zurücklegen und nachziehen.")
lines.append("Deckstärke: Seltenheit, Schlüsselwörter und Effekte ergeben je Karte einen Wert. Gegnerdecks werden auf deine")