update
Some checks are pending
Tests / headless (push) Waiting to run
Tests / fenster-schnell (push) Waiting to run
Tests / fenster-voll (push) Waiting to run
Tests / balance (push) Waiting to run

This commit is contained in:
team3
2026-10-13 15:39:55 +02:00
parent b91f475a6e
commit f2c609a08b
25 changed files with 1338 additions and 776 deletions

View File

@@ -5,8 +5,9 @@ extends Control
## die Leertaste oder der Rechtsklick ins Leere.
## Mittlere Maustaste auf eine Handkarte legt sie verdeckt. Rechtsklick auf eine Handkarte oder
## ein eigenes Wesen öffnet das Kartenmenü: verdeckt legen, aufdecken, Eigenschaften kaufen.
## Verschmelzen: eine Karte auf eine andere ziehen und dort eine Sekunde stillhalten. Mit
## Gamepad öffnet Y das Menü, dort zusätzlich mit „Verschmelzen mit …“.
## Verschmelzen: eine Handkarte oder ein eigenes Wesen auf ein anderes ziehen und loslassen; das
## Ergebnis (eine neue Handkarte) erscheint über dem Ziel, darunter Bestätigen oder Abbrechen.
## Mit Gamepad öffnet Y das Menü, dort zusätzlich mit „Verschmelzen mit …“.
## Modi: Einzelspieler und Kampagne.
var app
@@ -17,15 +18,13 @@ var fx: BattleFx
var selected = null # Game.Creature
var hover_target = null # fremdes Wesen unter der Maus: Vorschau des Angriffs mit dem gewählten Wesen
var fuse_first = null # Karte oder Wesen, für das im Menü „Verschmelzen mit …“ gewählt wurde
var hold_time := 1.0 # so lange still über dem Ziel halten, dann verschmelzen die beiden
var fuse_pending = null # [a, b] nach dem Loslassen über einem Ziel, wartet auf Bestätigung
var press_target = null # Karte oder Wesen unter der gedrückten linken Maustaste
var press_view: Control = null
var press_pos := Vector2.ZERO
var drag_ghost: Control = null # schwebende Kopie der gezogenen Karte
var drag_target = null # mögliches Verschmelzungsziel unter dem Zeiger
var drag_target_view: Control = null
var hold_t := 0.0
var hold_pos := Vector2.ZERO
var msg_t := 0.0
var ai_delay := 0.75
var ai_timer := 0.0
@@ -77,6 +76,7 @@ var info_label: Label # Vorschau: Elementbonus, Merkmale
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
var hand_arrivals := {} # Karte -> Rect2: neue Handkarte, die aus der Verschmelzung in die Hand gleitet
var banner_turn := -1 # Zug, für den die Zugansage schon lief
var overlay: Control
var result_label: Label
@@ -408,15 +408,6 @@ func _process(delta: float) -> void:
undo_t -= delta
if undo_t <= 0.0:
clear_undo()
if drag_ghost != null and drag_target != null:
_set_hold(hold_t + delta)
if hold_t >= hold_time:
var a = press_target
var b = drag_target
press_target = null
_end_drag()
do_fuse(a, b)
after_action()
if game.winner != "":
if not ended:
ended = true
@@ -487,7 +478,7 @@ func cancel() -> void:
_end_drag()
# --- Ziehen mit der Maus: Klick beim Loslassen, Verschmelzen nach einer Sekunde über dem Ziel ---
# --- Ziehen mit der Maus: Klick beim Loslassen; losgelassen über einem Ziel fragt nach dem Verschmelzen ---
func _press(obj, view: Control, pos: Vector2) -> void:
_end_drag()
@@ -505,23 +496,28 @@ func _drag_motion(pos: Vector2) -> void:
_start_drag(pos)
drag_ghost.position = pos - drag_ghost.size / 2
var t = _fusion_target_at(pos)
if t != drag_target or pos.distance_to(hold_pos) > 6.0:
_set_hold(0.0)
if t != drag_target:
_mark_target(false)
drag_target = t
drag_target_view = _view_of(t)
hold_pos = pos
_mark_target(true)
## Loslassen ohne Ziehen ist der Klick.
## Loslassen ohne Ziehen ist der Klick; über einem Ziel öffnet es die Verschmelzungsfrage.
func _release() -> void:
var obj = press_target
var target = drag_target
var target_rect := _rect_of(drag_target_view) if drag_target_view != null else Rect2()
var dragged := drag_ghost != null
press_target = null
_end_drag()
if obj == null:
return
if dragged:
refresh()
if target != null:
open_fuse_confirm(obj, target, target_rect)
else:
refresh()
elif obj is Cards.Card:
on_click("hand", obj)
else:
@@ -549,24 +545,27 @@ func _end_drag() -> void:
drag_ghost = null
if is_instance_valid(press_view):
press_view.modulate.a = 1.0
_set_hold(0.0)
_mark_target(false)
drag_target = null
drag_target_view = null
func _set_hold(v: float) -> void:
hold_t = v
func _mark_target(on: bool) -> void:
if is_instance_valid(drag_target_view):
drag_target_view.hold = clampf(v / hold_time, 0.0, 1.0) if v > 0.0 else 0.0
drag_target_view.drop = on
drag_target_view.queue_redraw()
## Karte oder Wesen unter pos, mit dem sich die gezogene Karte verschmelzen lässt.
## Platz einer Ansicht: Kacheln zählen an ihrem Platz, auch während sie dorthin gleiten.
func _rect_of(v: Control) -> Rect2:
return Rect2(v.slot, v.size) if v is CreatureView else v.get_global_rect()
## Handkarte oder eigenes Wesen unter pos, mit dem sich die gezogene Karte verschmelzen lässt.
func _fusion_target_at(pos: Vector2):
for v in (hand_views if press_target is Cards.Card else own_views):
for v in hand_views + own_views:
var obj = v.card if v is CardView else v.creature
var r: Rect2 = Rect2(v.slot, v.size) if v is CreatureView else v.get_global_rect() # Kacheln zählen an ihrem Platz, auch während sie dorthin gleiten
if obj != press_target and r.has_point(pos) and game.can_fuse(perspective, press_target, obj)[0]:
if obj != press_target and _rect_of(v).has_point(pos) and game.can_fuse(perspective, press_target, obj)[0]:
return obj
return null
@@ -581,9 +580,9 @@ func _view_of(obj) -> Control:
return null
## Mögliche Partner für „Verschmelzen mit …“: andere Handkarten bzw. andere eigene Wesen.
## Mögliche Partner für „Verschmelzen mit …“: alle anderen Handkarten und eigenen Wesen.
func fusion_partners(target) -> Array:
var pool: Array = perspective.hand if target is Cards.Card else perspective.board
var pool: Array = perspective.hand + perspective.board
return pool.filter(func(o): return o != target and game.can_fuse(perspective, target, o)[0])
@@ -675,6 +674,37 @@ func close_menu() -> void:
menu_layer.visible = false
Style.clear(menu_layer)
menu_target = null
fuse_pending = null
## Nach dem Loslassen über einem Ziel: die neue Handkarte schwebt über dem Ziel, darunter
## Bestätigen und Abbrechen; beide Ausgangskarten bleiben sichtbar. Klick daneben oder Esc bricht ab.
func open_fuse_confirm(a, b, target: Rect2) -> void:
cancel()
Style.clear(menu_layer)
fuse_pending = [a, b]
var card := Game.fusion_result(perspective, a, b)
var size := Vector2(BattleLayout.HAND_W, BattleLayout.HAND_H)
var bw := 150.0
var x := clampf(target.get_center().x - size.x / 2, 10 + bw - size.x / 2, 1270 - bw - size.x / 2)
var y := maxf(10.0, target.position.y - size.y - 48)
var v := CardView.new(card, size, false, Cards.RARITIES[card.look])
v.position = Vector2(x, y)
v.mouse_filter = Control.MOUSE_FILTER_IGNORE
menu_layer.add_child(v)
var by := y + size.y + 6
var bx := x + size.x / 2 - bw - 3
var yes := Style.button(menu_layer, tr("Verschmelzen"), Rect2(bx, by, bw, 36), func():
var pair: Array = fuse_pending
close_menu()
do_fuse(pair[0], pair[1], Rect2(v.position, v.size))
after_action(), Color("285a3c"))
yes.add_theme_font_size_override("font_size", 15)
var no := Style.button(menu_layer, tr("Abbrechen"), Rect2(bx + bw + 6, by, bw, 36), func():
close_menu()
refresh())
no.add_theme_font_size_override("font_size", 15)
menu_layer.visible = true
## Abwerfen: eine Handkarte gegen Mana loswerden.
@@ -696,13 +726,26 @@ func do_draw() -> void:
fx.sfx("buy")
## Verschmilzt zwei Handkarten oder zwei eigene Wesen (siehe Game.fuse).
func do_fuse(a, b) -> void:
## Verschmilzt zwei eigene Karten zur neuen Handkarte (siehe Game.fuse). from: von dort gleitet
## die neue Karte in die Hand, Eltern auf der Hand blenden aus (Wesen tut das der Abgleich).
func do_fuse(a, b, from: Rect2 = Rect2()) -> void:
cancel()
clear_undo()
var before: Array = perspective.hand.duplicate()
var err := game.fuse(perspective, a, b)
if err != "":
say(tr(err))
return
if from.size.x <= 0:
return
for x in [a, b]:
var pv := _view_of(x)
if pv is CardView:
pv.reparent(fx_layer)
fx.fade_view(pv)
for c in perspective.hand:
if not (c in before):
hand_arrivals[c] = from
func clear_undo() -> void:
@@ -747,7 +790,8 @@ func on_click(kind: String, obj) -> void:
var first = fuse_first
fuse_first = null
if obj != null and obj != first and g.can_fuse(perspective, first, obj)[0]:
do_fuse(first, obj)
var tv := _view_of(obj)
do_fuse(first, obj, _rect_of(tv) if tv != null else Rect2())
else:
say(tr("Abgebrochen"))
after_action()
@@ -846,6 +890,8 @@ func creature_info(cr: Game.Creature) -> String:
var perk := cr.power() - cr.attack - cr.aura
if perk > 0:
parts.append(tr("Merkmal +%d") % perk)
if cr.card.is_neutral():
parts.append(tr(Cards.NEUTRAL_TEXT))
for k in cr.keywords():
if not (k in cr.card.keywords):
parts.append(tr("%s durch das Element") % tr(k))
@@ -1089,6 +1135,9 @@ func refresh() -> void:
var arc: Array = BattleLayout.hand_arc(h.hand.size(), i)
v.rotation_degrees = arc[0]
v.position.y += arc[1]
if hand_arrivals.has(card):
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]: