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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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))
|
||||
@@ -1 +0,0 @@
|
||||
uid://crlne4y0uiygl
|
||||
@@ -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()))
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user