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-14 17:35:09 +02:00
1 parent f2c609a08b
commit 1d563b9eec
34 files changed
+5486 -643

No files matched your search

+53 -27
View File
@@ -31,7 +31,10 @@ var ai_timer := 0.0
var ai_task := -1 # WorkerThreadPool-Aufgabe der laufenden KI-Berechnung
var ai_copy: Game # Spielkopie, auf der die KI rechnet
var ai_box := {} # Ergebnis der Berechnung (vom Thread befüllt)
var reward := -1
var reward := -1 # gutgeschriebene Münzen (mit Kampagnenbonus)
var earned := 0 # Münzen aus Zügen, Karten und Tempo (Profile.battle_coins)
var fast_turns := 0 # eigene Züge unter Profile.FAST_TURN_SEC
var turn_start := 0 # Beginn des laufenden eigenen Zugs (ms)
var ended := false
var pad: BattleGamepad
var gp_zone: String:
@@ -72,7 +75,7 @@ var energy_label: Label
var msg_label: Label
var element_view: ElementView # eigene Elementleiste
var opp_element_view: ElementView # die des Gegners darüber
var info_label: Label # Vorschau: Elementbonus, Merkmale
var info_label: Label # Vorschau: Merkmale, Eigenschaften
var menu_layer: Control # Kartenmenü (Rechtsklick)
var menu_target = null # Karte oder Wesen, für das das Menü offen ist
var arrivals := {} # uid -> Rect2: Wesen, das gerade aus der Hand aufs Brett fliegt
@@ -81,6 +84,7 @@ var banner_turn := -1 # Zug, für den die Zugansage schon lief
var overlay: Control
var result_label: Label
var reward_label: Label
var detail_label: Label # Aufschlüsselung der Münzen
var notice_label: Label
var confirm: Control
var cursor_layer: Control
@@ -99,23 +103,20 @@ func _ready() -> void:
pad = BattleGamepad.new(self)
ai_delay = 0.75 / float(s.ai_speed)
_build_static()
if stage >= 0:
var r := Game.make_rng()
game = Game.new_battle_with(Campaign.opponent(stage, r), r, false, own_rules())
else:
game = Game.new_battle(Game.make_rng(), own_rules())
var r := Game.make_rng()
var opp: Game.Player = Campaign.opponent(stage) if stage >= 0 else app.profile.single_opponent(r)
game = Game.new_battle_with(opp, r, false, own_rules())
smart_ai = bool(app.profile.difficulty().smart)
_begin()
## Eigener Stapel und Kartenstufen, Gegnerleben (Schwierigkeit) und das eigene Merkmal (nach Kapitel 1).
## 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()}
if stage < 0:
rules["opponent_life"] = int(p.difficulty().life)
if p.perk != "" and p.perk_unlocked():
rules["human_perks"] = [p.perk]
var perks := p.battle_perks()
if not perks.is_empty():
rules["human_perks"] = perks
return rules
@@ -224,7 +225,8 @@ func _build_static() -> void:
overlay = _overlay()
result_label = Style.label(overlay, "", 56, Style.GREEN, Rect2(0, 250, 1280, 80), "center", true)
reward_label = Style.label(overlay, "", 26, Style.GOLD, Rect2(0, 335, 1280, 40), "center")
notice_label = Style.label(overlay, "", 18, Style.GREEN, Rect2(0, 385, 1280, 80), "center")
detail_label = Style.label(overlay, "", 17, Style.MUTED, Rect2(0, 372, 1280, 28), "center")
notice_label = Style.label(overlay, "", 18, Style.GREEN, Rect2(0, 405, 1280, 70), "center")
Style.button(overlay, "Zurück zum Menü", Rect2(530, 480, 220, 50), leave, Color("285a3c"))
confirm = Style.confirm(self, "Wirklich aufgeben?", "", "Ja, aufgeben", leave, "Weiterspielen")
@@ -459,8 +461,11 @@ func _exit_tree() -> void:
func _finish() -> void:
var won := game.winner == "human"
if game.current() == game.human:
_close_turn() # der Zug, der das Spiel entschied
var factor: float = float(app.profile.difficulty().reward) if stage < 0 else 1.0
reward = app.profile.record_battle(won, stage, factor)
earned = Profile.battle_coins(won, game.human.turns, game.human.cards_played, fast_turns, factor)
reward = app.profile.record_battle(won, earned, stage)
app.save()
app.sfx.play("win" if won else "lose")
refresh()
@@ -777,6 +782,7 @@ func end_turn() -> void:
return
cancel()
clear_undo()
_close_turn()
game.end_turn()
ai_timer = ai_delay
after_action()
@@ -839,11 +845,36 @@ func on_tile_click(cr: Game.Creature) -> void:
on_click("enemy", cr)
## Zugansage, wenn der eigene Zug beginnt.
## Zählt den laufenden eigenen Zug als schnell, wenn er unter Profile.FAST_TURN_SEC blieb.
func _close_turn() -> void:
if Time.get_ticks_msec() - turn_start < Profile.FAST_TURN_SEC * 1000:
fast_turns += 1
## Aufschlüsselung der Münzen fürs Ergebnisbild.
func reward_text() -> String:
var h := game.human
var s := "%s · %s · %s = %d" % [tr("%d Züge") % h.turns, tr("%d Karten") % h.cards_played, tr("%d schnelle Züge") % fast_turns,
Profile.COINS_TURN * h.turns + Profile.COINS_CARD * h.cards_played + Profile.COINS_FAST * fast_turns]
if game.winner == "human":
s += " · " + tr("Sieg ×2")
var d: Dictionary = app.profile.difficulty()
if stage < 0 and float(d.reward) != 1.0:
s += " · %s %s" % [tr(d.name), Style.factor(float(d.reward))]
var boosted := int(round(earned * app.profile.coin_factor()))
if boosted != earned:
s += " · %s %s" % [tr("Münzsegen"), Style.factor(app.profile.coin_factor())]
if reward > boosted:
s += " · " + tr("Kampagne +%d") % (reward - boosted)
return s
## Zugansage, wenn der eigene Zug beginnt; ab hier läuft die Zeit für den schnellen Zug.
func announce_turn() -> void:
if game == null or not my_turn() or banner_turn == game.turn:
return
banner_turn = game.turn
turn_start = Time.get_ticks_msec()
fx.banner(tr("Dein Zug"), Style.GOLD)
fx.sfx("turn")
@@ -871,23 +902,17 @@ func show_preview(card, entered: bool, owner_looks: Dictionary = {}, info: Strin
info_label.size = ir.size
## Eigene Handkarte: zeigt den Elementbonus, den sie offen gelegt bekäme.
## Eigene Handkarte in der Vorschau.
func show_hand_preview(card: Cards.Card, entered: bool, anchor: Rect2 = Rect2()) -> void:
if drag_ghost != null:
return
var a := game.forecast(perspective, card)
var info := ""
if a > 0:
info = tr("Elementbonus +%d") % a
show_preview(card, entered, perspective.looks, info, anchor)
show_preview(card, entered, perspective.looks, "", anchor)
## Was ein Wesen gerade hebt, was es kann und ob es schon angreift.
func creature_info(cr: Game.Creature) -> String:
var parts: Array[String] = []
if cr.aura > 0:
parts.append(tr("Elementbonus +%d") % cr.aura)
var perk := cr.power() - cr.attack - cr.aura
var perk := cr.power() - cr.attack
if perk > 0:
parts.append(tr("Merkmal +%d") % perk)
if cr.card.is_neutral():
@@ -902,10 +927,11 @@ func creature_info(cr: Game.Creature) -> String:
return " · ".join(parts)
## Elementvorteil des gewählten Angreifers gegen ein mögliches Ziel; gegen Verdeckte unbekannt.
## Konter eines möglichen Ziels gegen den gewählten Angreifer (Verschiebung durch sein Element);
## gegen Verdeckte unbekannt.
func matchup_for(cr: Game.Creature) -> int:
if selected != null and not cr.hidden and game.can_attack(selected, cr)[0]:
return Game.matchup_bonus(selected, cr)
return Game.matchup_bonus(cr, selected)
return 0
@@ -1139,7 +1165,6 @@ func refresh() -> void:
fx.glide(v, hand_arrivals[card])
hand_arrivals.erase(card)
v.lift = mine
v.bonus = g.forecast(h, card)
if fuse_first != null and mine and g.can_fuse(h, fuse_first, card)[0]:
v.border = Style.PURPLE
elif card == fuse_first:
@@ -1185,6 +1210,7 @@ func refresh() -> void:
result_label.text = "Sieg!" if won else "Niederlage"
result_label.add_theme_color_override("font_color", Style.GREEN if won else Style.RED)
reward_label.text = "+%d Münzen" % maxi(reward, 0)
detail_label.text = reward_text()
var notes: Array[String] = app.profile.take_notices()
if not notes.is_empty():
notice_label.text = "\n".join(notes)