diff --git a/README.md b/README.md index 3929585..9474341 100644 --- a/README.md +++ b/README.md @@ -20,19 +20,19 @@ Der Spielstand liegt unter `user://spielstand.json` ## Spielsysteme -- 77 Karten in `data/karten.json`, alle Wesen, jede mit genau einem der vier Elemente; Kombos sind die Namen verschmolzener Wesen -- Karten haben nur Element, Angriff und Leben; die Kosten sind zwei Drittel Angriff plus ein Drittel Leben, abgerundet, mindestens 1 -- Mana: 5 im ersten Zug, jede Runde eins mehr ohne Grenze; was übrig bleibt, verfällt. Mana geht in Ziehen, Legen, Verbessern und Verschmelzen -- Kein Deckbau: beide ziehen vom gemeinsamen Stapel (jede Karte dreimal). Zu Beginn fünf Karten, Ziehen kostet 1 Mana und bringt eine; es kommt nur, was das Mana dieses Zugs hergibt, der Rest wandert unter den Stapel +- 81 Karten in `data/karten.json`, alle Wesen, jede mit genau einem der vier Elemente; Kombos sind die Namen verschmolzener Wesen +- Karten haben nur Element, Angriff und Leben; die Kosten sind zwei Drittel Angriff plus ein Drittel Leben, abgerundet; je Element gibt es ein 0/2-Wesen für 0 Mana +- Mana: 1 im ersten Zug, jede Runde eins mehr ohne Grenze; was übrig bleibt, verfällt. Mana geht in Ziehen, Legen, Verbessern und Verschmelzen +- Kein Deckbau: beide ziehen vom gemeinsamen Stapel (jede Karte dreimal). Zu Beginn fünf Karten, alle im ersten Zug spielbar; jeder Zug beginnt mit einer Karte vom Stapel (nicht bei voller Hand), weiteres Ziehen kostet 2 Mana; es kommt nur, was das Mana dieses Zugs hergibt, der Rest wandert unter den Stapel - Eigenschaften: Spott (2, muss zuerst angegriffen werden), Flink (2, greift sofort an), Regeneration (3, heilt am Zugende ganz) und Doppelschlag (4) lassen sich für Mana auf jede Hand- oder Brettkarte kaufen (Rechtsklick oder Y). Gelegte Wesen greifen erst im nächsten Zug an, außer sie sind flink; verletzte Wesen heilen nicht von selbst - Verdeckt legen (mittlere Maustaste auf die Handkarte oder im Kartenmenü): gleich teuer, das Wesen greift nicht an und zählt nicht für die Elementboni; aufdecken ist kostenlos, frühestens im nächsten Zug, danach greift es sofort an; ein Angriff deckt es auf - Jede Karte gehört jedem von Anfang an. Gesammelt wird die Seltenheitsstufe, und die ist reine Optik wie bei Yu-Gi-Oh: Common matt, Rare silberner Name und Rahmen mit Glanzstreifen, Super Rare Holo-Bild, Ultra Rare goldener Name und Rahmen mit Holo-Bild, Ultimate Rare Relief, Holo auf Bild und Rahmen, Funkeln und schillernde Farben; auch auf dem Feld für alle sichtbar, Werte bleiben gleich. Verschmolzene Wesen tragen die höhere Stufe ihrer Eltern - Booster (100 Münzen): fünf Karten mit gewürfelter Stufe (50/25/15/8/2 %, mindestens eine Rare), höhere werten die Karte auf, der Rest wird zu Staub (50/100/200/500/1000). Aufwerten kostet 100/200/500/1000 Staub, eine Stufe abgeben bringt den Zerlegewert zurück - Elementboni: zwei eigene offene Wesen eines Elements geben allen eigenen Wesen dieses Elements seinen Effekt (Erde Spott, Luft Flink, Wasser Regeneration, Feuer Doppelschlag); drei offene Wesen eines Elements auf dem Tisch geben dem Element +1/+1 (fällt der Bonus weg, sinkt nur das Höchstleben). Verschmolzene Wesen zählen für alle ihre Elemente und bekommen alle Boni -- Elementvorteil beim Kampf (Wasser > Feuer > Luft > Erde > Wasser): der Angreifer macht 1 Schaden mehr im Vorteil und 1 weniger im Nachteil, der Konter bleibt gleich, gegen Helden nichts; ein Wesen mit mehreren Elementen kämpft mit dem, das gegen den Gegner am besten steht +- Elementvorteil beim Kampf (Wasser > Feuer > Luft > Erde > Wasser): wer im Vorteil ist, macht 1 Schaden mehr, wer im Nachteil ist, 1 weniger, beim Angriff wie beim Konter, gegen Helden nichts; ein Wesen mit mehreren Elementen kämpft mit dem, das gegen den Gegner am besten steht. Ist ein Angreifer gewählt, zeigen die Gegnerkarten ihren Konter gegen ihn (grün mehr, rot weniger); über einem Ziel zeigt der Angreifer seinen Angriff mit Vorteil, und wer stirbt, trägt die Marke „stirbt“ - Angriffe treffen erst Wesen (Wesen mit Spott zuerst); das Leben ist nur ohne Wesen angreifbar. Nichts schlägt durch - Verschmelzen: eine Karte auf eine andere ziehen und eine Sekunde halten, auf dem Brett wie auf der Hand (Gamepad: Y, „Verschmelzen mit …“). Das neue Wesen hat je Wert den besseren plus ein Drittel des schwächeren (aufgerundet), Kosten passend dazu, Elemente und Eigenschaften beider; kostet 1 Mana, verdoppelt je weiterer Verschmelzung (1, 2, 4, 8); drei oder mehr Elemente ergeben eine Chimäre -- Duell mit ausgeloster Zugreihenfolge, wer nicht beginnt, bekommt einmalig 1 Mana; die Hand fasst 5 Karten, das Feld 5 Wesen +- Duell mit ausgeloster Zugreihenfolge, beide beginnen mit 1 Mana; die Hand fasst 5 Karten, das Feld 5 Wesen - Schwierigkeit Leicht/Normal/Schwer: Gegner mit 25/30/35 Leben, einfache oder Vorausschau-KI, Münzen 0,75/1/1,5-fach - KI mit Vorausschau in zwei Stufen und Zeitbudget (150 ms), sie rechnet im Hintergrund-Thread - Kampagne: drei Kapitel mit je zehn Magiern, Bosse mit ein bis drei Merkmalen, eigenes Merkmal nach Kapitel 1 diff --git a/anforderungen.md b/anforderungen.md index 2f8186f..d104e83 100644 --- a/anforderungen.md +++ b/anforderungen.md @@ -15,8 +15,8 @@ - Gesammelt wird die Seltenheitsstufe einer Karte (Common, Rare, Super Rare, Ultra Rare, Ultimate Rare), und die ist reine Optik wie bei Yu-Gi-Oh: silberner oder goldener Name, Holo-Bild, Relief, Funkeln, Schillern; auch im Netzwerkspiel sichtbar; Fusionen tragen die höhere Stufe der Eltern - Booster öffnen (Chancen 50/25/15/8/2 %, mindestens eine Rare): höhere Stufen werten Karten auf, der Rest wird zu Staub (50/100/200/500/1000) - Staub wertet gezielt auf (100/200/500/1000), eine Stufe abgeben bringt den Zerlegewert zurück -- Kein Deckbau: alle ziehen vom gemeinsamen Stapel (jede Karte dreimal); zu Beginn fünf Karten; Ziehen kostet 1 Mana und bringt eine Karte; es kommt nur, was man sich in diesem Zug leisten kann -- Mana für Ziehen, Legen (Kosten = 2/3 Angriff + 1/3 Leben, abgerundet, mindestens 1), Verbessern und Verschmelzen; 5 im ersten Zug, dann je Runde eins mehr; der Rest verfällt; höchstens 5 Handkarten +- Kein Deckbau: alle ziehen vom gemeinsamen Stapel (jede Karte dreimal); zu Beginn fünf Karten, alle im ersten Zug spielbar; jeder Zug beginnt mit einer Karte vom Stapel (nicht bei voller Hand), weiteres Ziehen kostet 2 Mana; es kommt nur, was man sich in diesem Zug leisten kann +- Mana für Ziehen, Legen (Kosten = 2/3 Angriff + 1/3 Leben, abgerundet; 0/2 kostet 0), Verbessern und Verschmelzen; 1 im ersten Zug, dann je Runde eins mehr; der Rest verfällt; höchstens 5 Handkarten - Karten haben nur Angriff und Leben; Eigenschaften kauft man per Rechtsklick für Mana dazu, auf Hand und Brett: Spott 2 (muss zuerst angegriffen werden), Flink 2 (greift sofort an), Regeneration 3 (heilt sich am Zugende ganz), Doppelschlag 4 - Gelegte Wesen greifen erst im nächsten Zug an, außer sie sind flink (auch im ersten Zug); verletzte Wesen heilen nicht von selbst - Wesen lassen sich verdeckt legen (mittlere Maustaste oder Kartenmenü); aufdecken ist frei, frühestens im nächsten Zug, dann greift es sofort an; ein Angriff deckt auf @@ -28,6 +28,6 @@ - Kombos sind die Namen verschmolzener Wesen (Feuer + Wasser wird Dampf, gleiche Elemente werden Inferno, Tiefsee, Berg, Orkan) - Zwei eigene offene Wesen eines Elements: Sondereffekt für alle eigenen Wesen des Elements (Erde Spott, Luft Flink, Wasser Regeneration, Feuer Doppelschlag) - Drei offene Wesen eines Elements auf dem Tisch: +1/+1 für das Element, kein Malus; die Karte zeigt vorher, was sie bekäme; verschmolzene Wesen zählen für alle ihre Elemente und bekommen alle Effekte und Boni -- Elementvorteil im Kreis: Wasser schlägt Feuer, Feuer schlägt Luft, Luft schlägt Erde, Erde schlägt Wasser (der Angreifer macht 1 Schaden mehr im Vorteil, 1 weniger im Nachteil, der Konter bleibt gleich; ein Wesen mit mehreren Elementen kämpft mit dem, das gegen den Gegner am besten steht) +- Elementvorteil im Kreis: Wasser schlägt Feuer, Feuer schlägt Luft, Luft schlägt Erde, Erde schlägt Wasser (wer im Vorteil ist, macht 1 Schaden mehr, wer im Nachteil ist, 1 weniger, beim Angriff wie beim Konter; ein Wesen mit mehreren Elementen kämpft mit dem, das gegen den Gegner am besten steht) - Verschmelzen: eine Karte auf eine andere ziehen und eine Sekunde stillhalten, auf dem Brett oder auf der Hand (Gamepad: über das Kartenmenü); das neue erbt Elemente und Eigenschaften beider, je Wert den besseren plus ein Drittel des schwächeren (aufgerundet), Kosten folgen den Werten; Fusionskosten 1 Mana, verdoppelt je weiterer Verschmelzung - Alle Karten sind Wesen: keine Zauber, keine Fallen; Effekte lösen beim Ausspielen aus diff --git a/data/karten.json b/data/karten.json index 53ca608..0eba2b6 100644 --- a/data/karten.json +++ b/data/karten.json @@ -769,6 +769,46 @@ ], "attack": 5, "health": 4 + }, + { + "id": "f_funke", + "name": "Glutfunke", + "name_en": "Ember Spark", + "elements": [ + "Feuer" + ], + "attack": 0, + "health": 2 + }, + { + "id": "w_tropfen", + "name": "Tautropfen", + "name_en": "Dewdrop", + "elements": [ + "Wasser" + ], + "attack": 0, + "health": 2 + }, + { + "id": "e_kruemel", + "name": "Erdkrümel", + "name_en": "Soil Crumb", + "elements": [ + "Erde" + ], + "attack": 0, + "health": 2 + }, + { + "id": "l_hauch", + "name": "Windhauch", + "name_en": "Breeze", + "elements": [ + "Luft" + ], + "attack": 0, + "health": 2 } ] } diff --git a/design.md b/design.md index f2934bc..ec6a241 100644 --- a/design.md +++ b/design.md @@ -39,7 +39,7 @@ Stand: September 2026. Kein Enddesign, sondern die Richtung mit umgesetztem Entw Nur, was gerade zählt, ist zu sehen. Von unten nach oben: -- **Hand** als bis zu fünf große Karten, links der Zieh-Stapel mit „◆ 1“ (Klick zieht), rechts +- **Hand** als bis zu fünf große Karten, links der Zieh-Stapel mit „◆ 2“ (Klick zieht), rechts „Zug beenden“. - **Verschmelzen** per Ziehen: eine Hand- oder Brettkarte auf eine passende ziehen, ein goldener Ring füllt sich eine Sekunde lang, dann verschmelzen die beiden. Mit Gamepad über diff --git a/scripts/cards.gd b/scripts/cards.gd index d6c0c1d..c0c778f 100644 --- a/scripts/cards.gd +++ b/scripts/cards.gd @@ -94,9 +94,9 @@ static func best_element(own: Array, other: Array) -> String: return best -## Kosten einer Karte: zwei Drittel Angriff plus ein Drittel Leben, abgerundet, mindestens 1. +## Kosten einer Karte: zwei Drittel Angriff plus ein Drittel Leben, abgerundet (0/2 kostet nichts). static func cost_of(attack: int, health: int) -> int: - return maxi(1, (2 * attack + health) / 3) + return (2 * attack + health) / 3 ## Wert eines verschmolzenen Wesens: der bessere Wert plus ein Drittel des schwächeren, diff --git a/scripts/game.gd b/scripts/game.gd index 35aebf2..506a5f3 100644 --- a/scripts/game.gd +++ b/scripts/game.gd @@ -6,8 +6,8 @@ extends RefCounted const MAX_BOARD := 5 const MAX_HAND := 5 const START_HAND := 5 # Startkarten, alle im ersten Zug bezahlbar -const START_MANA := 5 # Mana in Zug 1; jede Runde eins mehr, ohne Grenze; Rest verfällt -const DRAW_COST := 1 # Ziehen: eine bezahlbare Karte vom Stapel +const START_MANA := 1 # Mana in Zug 1; jede Runde eins mehr, ohne Grenze; Rest verfällt +const DRAW_COST := 2 # Ziehen: eine bezahlbare Karte vom Stapel const START_LIFE := 30 const ELEMENT_PAIR := 2 # so viele eigene offene Wesen eines Elements: Sondereffekt des Elements const ELEMENT_STACK := 3 # so viele offene Wesen eines Elements auf dem Tisch: +1/+1 @@ -131,7 +131,6 @@ class Player extends RefCounted: var board: Array = [] var energy := 0 # Mana in diesem Zug; der Rest verfällt var max_energy := 0 # Mana zu Zugbeginn (START_MANA + eigene Züge) - var start_bonus := 0 # einmaliges Extra-Mana für den späteren Start var order := 0 # Platz in der Zugreihenfolge var perks: Array = [] # Boss-Merkmale (siehe PERKS) var looks := {} # Karten-ID -> Seltenheitsstufe, reine Optik (siehe Profile) @@ -161,7 +160,6 @@ class Player extends RefCounted: p.hand = hand.duplicate() p.energy = energy p.max_energy = max_energy - p.start_bonus = start_bonus p.order = order p.perks = perks.duplicate() p.looks = looks @@ -277,9 +275,7 @@ func start(fixed_order: bool = false) -> void: for i in players.size(): var p: Player = players[i] p.order = i - # Wer nicht beginnt, bekommt im ersten Zug einmalig 1 Mana extra. - p.start_bonus = 0 if i == 0 else 1 - for c in draw_cards(START_HAND, START_MANA + p.start_bonus): + for c in draw_cards(START_HAND, START_MANA): p.hand.append(c.copy()) turn = 1 current_idx = 0 @@ -349,16 +345,19 @@ func begin_turn(p: Player) -> void: p.energy = p.max_energy + (1 if "extra_mana" in p.perks else 0) # der Rest vom letzten Zug ist verfallen if p.turns == 0 and "start_energie" in p.perks: p.energy += 2 - if p.start_bonus > 0: - p.energy += p.start_bonus - say("%s erhält %d Mana extra für den späteren Start." % [p.name, p.start_bonus]) - p.start_bonus = 0 for c in p.board: c.ready = true # lag eine Runde: greift jetzt an, Verdecktes darf aufgedeckt werden c.attacks_done = 0 p.turns += 1 say("--- Zug %d: %s ---" % [turn, p.name]) emit({"type": "turn", "player": players.find(p)}) + # Jeder Zug beginnt mit einer Karte vom Stapel, die man sich in diesem Zug leisten kann. + if p.hand.size() < MAX_HAND: + p.hand.append(draw_cards(1, p.max_energy)[0].copy()) + say("%s zieht eine Karte." % p.name) + emit({"type": "draw", "player": players.find(p)}) + else: + say("%s hat die Hand voll und zieht nicht." % p.name) ## Zugende: Wesen mit Regeneration heilen ganz, dann ist der andere dran. @@ -661,15 +660,15 @@ func attack(cr: Creature, target) -> String: say("%s greift %s an." % [cr.name, target.name]) emit({"type": "attack", "attacker": ref_of(cr), "target": ref_of(target)}) damage(target, cr.power() + matchup_bonus(cr, target)) - if target is Creature and target.power() > 0: # wer nichts austeilt, kontert auch nicht; der Konter kennt keinen Elementvorteil - damage(cr, target.power()) + if target is Creature and target.power() > 0: # wer nichts austeilt, kontert auch nicht + damage(cr, target.power() + matchup_bonus(target, cr)) cleanup() check_end() return "" -## Elementvorteil beim Zuschlagen: +1 im Vorteil, -1 im Nachteil (die Verteidigung des -## Ziels), gegen Helden gibt es keinen. +## Elementvorteil beim Zuschlagen und beim Konter: +1 im Vorteil, -1 im Nachteil, +## gegen Helden gibt es keinen. static func matchup_bonus(source: Creature, target) -> int: if not (target is Creature): return 0 diff --git a/scripts/netstate.gd b/scripts/netstate.gd index 338d310..b3263c8 100644 --- a/scripts/netstate.gd +++ b/scripts/netstate.gd @@ -26,7 +26,7 @@ static func snapshot(g: Game, viewer: int) -> Dictionary: "max_health": 0 if masked else c.max_health, "attacks_done": c.attacks_done, "ready": c.ready, "hidden": c.hidden, "aura": c.aura}) ps.append({"name": p.name, "title": p.title, "is_human": p.is_human, "life": p.life, "max_life": p.max_life, - "energy": p.energy, "max_energy": p.max_energy, "start_bonus": p.start_bonus, + "energy": p.energy, "max_energy": p.max_energy, "order": p.order, "perks": p.perks, "looks": p.looks, "turns": p.turns, "hand": p.hand.map(card_ref) if mine else p.hand.size(), "board": board}) var evs := [] @@ -50,7 +50,6 @@ static func from_snapshot(d: Dictionary) -> Game: p.max_life = int(pd.max_life) p.energy = int(pd.energy) p.max_energy = int(pd.max_energy) - p.start_bonus = int(pd.start_bonus) p.order = int(pd.order) p.perks = Array(pd.perks) for cid in pd.get("looks", {}): diff --git a/scripts/ui/battle_screen.gd b/scripts/ui/battle_screen.gd index 5c8b2c7..5a0356c 100644 --- a/scripts/ui/battle_screen.gd +++ b/scripts/ui/battle_screen.gd @@ -16,6 +16,7 @@ var game: Game var perspective: Game.Player # der Mensch, dessen Hand unten liegt 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 press_target = null # Karte oder Wesen unter der gedrückten linken Maustaste @@ -928,6 +929,23 @@ func matchup_for(cr: Game.Creature) -> int: return 0 +## Vorschau für den Angriff auf das Wesen unter der Maus: der Angreifer zeigt seinen Angriff mit +## Elementvorteil, und wer dabei stirbt, bekommt die Marke. Verdeckte Ziele bleiben unbekannt. +func update_attack_preview() -> void: + var target = hover_target + if selected == null or target == null or target.hidden or not game.can_attack(selected, target)[0]: + target = null + for v in enemy_views: + v.doomed = target != null and v.creature == target and selected.power() + Game.matchup_bonus(selected, target) >= target.health + v.queue_redraw() + for v in own_views: + var mine: bool = v.creature == selected + v.attack_shift = Game.matchup_bonus(selected, target) if mine and target != null else 0 + var counter: int = target.power() + Game.matchup_bonus(target, selected) if target != null and target.power() > 0 else 0 + v.doomed = mine and target != null and counter >= selected.health + v.queue_redraw() + + class Deck extends Control: signal clicked var enabled := true @@ -1078,6 +1096,8 @@ func refresh() -> void: tile_layer.add_child(tile) var looks: Dictionary = p.looks tile.hover.connect(func(card, entered): + hover_target = tile.creature if entered else null + update_attack_preview() if tile.secret: show_preview(null, false) else: @@ -1123,6 +1143,7 @@ func refresh() -> void: var t: CreatureView = tiles[uid] tiles.erase(uid) fx.fade_out(t) + update_attack_preview() deck.visible = not spectating() deck.enabled = mine and g.can_draw(h)[0] deck.count = g.deck.size() diff --git a/scripts/ui/card_view.gd b/scripts/ui/card_view.gd index d6ee151..020fa09 100644 --- a/scripts/ui/card_view.gd +++ b/scripts/ui/card_view.gd @@ -161,7 +161,7 @@ func _draw_full(rect: Rect2, frame: Color, seed: int) -> void: fx, HORIZONTAL_ALIGNMENT_LEFT, box.size.x - 8, tsize, max_lines, Style.TEXT) # Werte springen aus den unteren Ecken, dazwischen der Edelstein Style.stat_circle(self, Vector2(r_stat - 2, size.y - r_stat + 2), r_stat, str(card.attack), Style.AMBER, f_stat) - Style.stat_circle(self, Vector2(size.x - r_stat + 2, size.y - r_stat + 2), r_stat, str(card.health), Style.CRIMSON, f_stat) + Style.stat_circle(self, Vector2(size.x - r_stat + 2, size.y - r_stat + 2), r_stat, str(card.health), Style.LIFE, f_stat) if bonus > 0: Style.text_outlined(self, "+%d/+%d" % [bonus, bonus], f_text, Style.GREEN, Vector2(r_stat * 2 + 4, size.y - r_stat + 2), "midleft", 3) Style.rarity_gem(self, rarity, Vector2(size.x / 2, size.y - r_stat + 1), 8 if big else 6, seed) @@ -188,7 +188,7 @@ func _draw_compact(rect: Rect2, frame: Color, seed: int) -> void: Style.keyword_icon(self, k, Vector2(x + 6, size.y - 13), 6) x += 18 Style.stat_circle(self, Vector2(11, size.y - 11), 10, str(card.attack), Style.AMBER, 12) - Style.stat_circle(self, Vector2(size.x - 11, size.y - 11), 10, str(card.health), Style.CRIMSON, 12) + Style.stat_circle(self, Vector2(size.x - 11, size.y - 11), 10, str(card.health), Style.LIFE, 12) if bonus > 0: Style.text_outlined(self, "+%d" % bonus, 10, Style.GREEN, Vector2(4, size.y - 29), "midleft", 3) Style.rarity_sparkle(self, rect, rarity, seed) diff --git a/scripts/ui/creature_view.gd b/scripts/ui/creature_view.gd index c9d06dc..1b095d3 100644 --- a/scripts/ui/creature_view.gd +++ b/scripts/ui/creature_view.gd @@ -11,7 +11,9 @@ var width := 2 var flash := false # kurz rot: wurde gerade angegriffen var slot: Vector2 # Zielposition im Raster var busy := false # wird gerade animiert (kein Neupositionieren) -var matchup := 0 # Elementvorteil des gewählten Angreifers gegen dieses Wesen (+1/-1) +var matchup := 0 # Elementvorteil des gewählten Angreifers gegen dieses fremde Wesen (+1/-1); die Werte zeigen dann, was ihn erwartet +var attack_shift := 0 # eigener Angreifer: Elementvorteil gegen das Ziel unter der Maus +var doomed := false # stirbt bei dem Angriff, der gerade unter der Maus liegt var secret := false # verdecktes fremdes Wesen: nur der Rücken ist zu sehen var hold := 0.0 # Fortschritt des Verschmelzens, wenn eine Karte darüber gehalten wird (0..1) @@ -66,14 +68,24 @@ func _draw() -> void: bcol = Style.RED Style.border(self, rect, bcol, 4 if flash else width, 6) var fs := 11 if small else 13 - var name_w: float = size.x - (30 if matchup != 0 else 6) # Platz für den Vorteilskreis oben rechts + var name_w: float = size.x - 6 var nf := Style.fit_size(creature.card.title(), true, fs, name_w) draw_rect(Rect2(2, 2, size.x - 4, Style.fs(fs) + 8), Color(0.05, 0.05, 0.08, 0.7)) Style.text(self, Style.fit(creature.card.title(), true, Style.fs(nf), name_w), nf, Style.rarity_name_color(rar, seed), Vector2(3 + name_w / 2, 6), "midtop", true) + # Elementbonus als ein Abzeichen rechts unter dem Namen; die Symbole rücken dafür nach links + var badge := "" + var bw := 0.0 + if creature.aura != 0: + badge = "+%d/+%d" % [creature.aura, creature.aura] if creature.aura > 0 else "−%d/−%d" % [-creature.aura, -creature.aura] + bw = Style.FONT_BOLD.get_string_size(badge, HORIZONTAL_ALIGNMENT_LEFT, -1, Style.fs(10)).x + 8 + var by := Style.fs(fs) + 13 + draw_rect(Rect2(size.x - bw - 4, by, bw, 16), Color(0.05, 0.05, 0.08, 0.75)) + Style.text(self, badge, 10, Style.GREEN if creature.aura > 0 else Style.RED, Vector2(size.x - 4 - bw / 2, by + 8), "center", true) # Eigenschaften als Symbole: gekaufte weiß, die vom Elementpaar in der Elementfarbe var icon := 7.0 if small else 9.0 var kws := creature.keywords() - var x := size.x / 2 - (kws.size() * (icon * 2 + 6) - 6) / 2.0 + var avail := size.x - (bw + 8 if badge != "" else 0.0) + var x := avail / 2 - (kws.size() * (icon * 2 + 6) - 6) / 2.0 var y := Style.fs(fs) + 14 + icon for k in kws: var col := Color("f2f2f6") @@ -83,11 +95,14 @@ func _draw() -> void: col = Style.lighter(Cards.ELEMENT_COLORS[e], 0.45) Style.keyword_icon(self, k, Vector2(x + icon, y), icon, col) x += icon * 2 + 6 - _draw_stats(15 if small else 17, 10 if small else 12, 12.0 if small else 14.0) + _draw_stats(15 if small else 17, 12.0 if small else 14.0) Style.rarity_gem(self, rar, Vector2(size.x / 2, size.y - 9), 5, seed) if creature.hidden: draw_rect(rect, Color(0.05, 0.05, 0.1, 0.55)) Style.text_outlined(self, tr("verdeckt"), 12, Style.TEXT, Vector2(size.x / 2, size.y / 2), "center", 3) + if doomed: + draw_rect(Rect2(0, size.y * 0.5 - 12, size.x, 24), Color(0.05, 0.02, 0.02, 0.8)) + Style.text_outlined(self, tr("stirbt"), 14, Style.RED, Vector2(size.x / 2, size.y * 0.5), "center", 3) Style.rarity_sparkle(self, rect, rar, seed) _draw_hold() @@ -110,21 +125,26 @@ func _draw_back(rect: Rect2) -> void: draw_style_box(Style.box(Color("3c285a"), 6, bcol, 4 if flash else maxi(width, 2)), rect) Style.border(self, rect.grow(-10), Color("5a3c82"), 2, 6) Style.text(self, "?", 36, Style.GOLD, size / 2, "center", true) - if matchup != 0: - pass # unbekanntes Element: kein Vorteil anzeigen -## Angriff (grün, wenn der Elementbonus ihn hebt), Leben, Elementvorteil des Angreifers. -func _draw_stats(f: int, f_small: int, r: float) -> void: - var extra := creature.power() - creature.attack - var atk_col := Style.GREEN if extra > 0 else (Style.RED if extra < 0 else Style.AMBER) - Style.stat_circle(self, Vector2(r - 1, size.y - r + 1), r, str(creature.power()), atk_col, f) - if extra != 0: - Style.text_outlined(self, ("+%d" % extra) if extra > 0 else ("−%d" % -extra), f_small, Style.GREEN if extra > 0 else Style.RED, Vector2(r * 2 + 3, size.y - r + 1), "midleft", 3) - var hp_col := Style.CRIMSON if creature.health < creature.max_health else Color("b84a4a") - Style.stat_circle(self, Vector2(size.x - r + 1, size.y - r + 1), r, str(creature.health), hp_col, f) +## Angriff und Leben für den Angriff, der gerade vorbereitet wird. Fremdes Wesen bei gewähltem +## Angreifer: sein Konter gegen ihn (im Nachteil 1 weniger, wer 0 Angriff hat, kontert gar nicht), +## das Leben bleibt echt. Eigener Angreifer über einem Ziel: sein Angriff mit Elementvorteil. +func shown_stats() -> Array: if matchup != 0: - var c := Vector2(size.x - 12, 12) - draw_circle(c, 11, Color("14141c")) - Style.text(self, ("+%d" % matchup) if matchup > 0 else ("−%d" % -matchup), f_small + 1, - Style.GREEN if matchup > 0 else Style.RED, c, "center", true) + var atk := 0 if creature.power() <= 0 else maxi(0, creature.power() - matchup) + return [atk, creature.health] + if attack_shift != 0: + return [maxi(0, creature.power() + attack_shift), creature.health] + return [creature.power(), creature.health] + + +## Angriff gelb, Leben blau; die Zahl wird grün, wenn sie für den aktuellen Angriff steigt, rot, wenn sie sinkt. +func _draw_stats(f: int, r: float) -> void: + var shown := shown_stats() + var atk: int = shown[0] + var hp: int = shown[1] + var atk_text := Style.GREEN if atk > creature.power() else (Style.RED if atk < creature.power() else Color.WHITE) + var hp_text := Style.GREEN if hp > creature.health else (Style.RED if hp < creature.health else Color.WHITE) + Style.stat_circle(self, Vector2(r - 1, size.y - r + 1), r, str(atk), Style.AMBER, f, atk_text) + Style.stat_circle(self, Vector2(size.x - r + 1, size.y - r + 1), r, str(hp), Style.LIFE, f, hp_text) diff --git a/scripts/ui/element_view.gd b/scripts/ui/element_view.gd index d0e53f1..6e7857e 100644 --- a/scripts/ui/element_view.gd +++ b/scripts/ui/element_view.gd @@ -68,9 +68,6 @@ func _draw() -> void: draw_circle(c, 4.5, Color(0, 0, 0, 0.35)) draw_arc(c, 4.5, 0, TAU, 16, Color(col, 0.6), 1.0) px += Game.ELEMENT_STACK * 13 + 2 - if n > Game.ELEMENT_STACK: - Style.text(self, "+%d" % (n - Game.ELEMENT_STACK), 11, Style.MUTED, Vector2(px, cy), "midleft") - px += 18 if paired(e): Style.keyword_icon(self, Cards.ELEMENT_KEYWORD[e], Vector2(px + 10, cy), 7, Style.lighter(col, 0.45)) if strong(e): diff --git a/scripts/ui/style.gd b/scripts/ui/style.gd index 9eadb84..0cad253 100644 --- a/scripts/ui/style.gd +++ b/scripts/ui/style.gd @@ -19,7 +19,7 @@ const YELLOW := Color("ffe65a") const ORANGE := Color("ff963c") const PURPLE := Color("8c78dc") const AMBER := Color("f0b429") # Angriff -const CRIMSON := Color("d64545") # Leben +const LIFE := Color("4a86d8") # Leben const MANA := Color("3a78d2") # Kostenkristall const PRIMARY := Color("285a3c") # Hauptaktion const DANGER := Color("5a2828") @@ -476,11 +476,11 @@ static func cost_crystal(ci: CanvasItem, c: Vector2, r: float, cost: int, font: ## Wertkreis (Angriff gelb, Leben rot) mit Zahl. -static func stat_circle(ci: CanvasItem, c: Vector2, r: float, value: String, color: Color, font: int) -> void: +static func stat_circle(ci: CanvasItem, c: Vector2, r: float, value: String, color: Color, font: int, text_col: Color = Color.WHITE) -> void: ci.draw_circle(c, r + 2, Color("14141c")) ci.draw_circle(c, r, color) ci.draw_circle(c + Vector2(-r * 0.25, -r * 0.3), r * 0.45, Color(1, 1, 1, 0.16)) - text_outlined(ci, value, font, Color.WHITE, c, "center", 3) + text_outlined(ci, value, font, text_col, c, "center", 3) ## Eigenschaft als Symbol auf dunklem Kreis: Spott Ausrufezeichen, Flink Doppelpfeil, diff --git a/tests/test_runner.gd b/tests/test_runner.gd index 8676d9f..108daba 100644 --- a/tests/test_runner.gd +++ b/tests/test_runner.gd @@ -90,14 +90,14 @@ func _initialize() -> void: func test_kartenpool() -> void: - check(Cards.all().size() == 77, "77 Karten") + check(Cards.all().size() == 81, "81 Karten") check(Cards.all().all(func(c): return c.elements.size() == 1 and c.elements[0] in Cards.ELEMENTS), "jede Karte hat genau ein Element") for e in Cards.ELEMENTS: check(Cards.all().any(func(c): return c.elements == [e]), "Element vorhanden: " + e) for c in Cards.all(): - check(c.health > 0 and c.cost == maxi(1, floori((2 * c.attack + c.health) / 3.0)), "Kosten = (2 · Angriff + Leben) / 3, abgerundet: " + c.id) + check(c.health > 0 and c.cost == floori((2 * c.attack + c.health) / 3.0), "Kosten = (2 · Angriff + Leben) / 3, abgerundet: " + c.id) check(c.keywords.is_empty() and c.text() == "", "Karten kommen ohne Eigenschaften: " + c.id) - check(C("f_kobold").cost == 1 and C("f_wolf").cost == 2 and C("ff_phoenix").cost == 7 and C("e_wall").cost == 1 and C("f_drache").cost == 5, "Kostenbeispiele") + check(C("f_funke").cost == 0 and C("f_kobold").cost == 1 and C("f_wolf").cost == 2 and C("ff_phoenix").cost == 7 and C("e_wall").cost == 1 and C("f_drache").cost == 5, "Kostenbeispiele, 0/2 kostet nichts") var a := C("f_wolf") var b := C("f_wolf") a.keywords.append("Spott") @@ -111,7 +111,7 @@ func test_kombos() -> void: check(Cards.combo_name(["Feuer", "Wasser", "Erde"]) == "Chimäre", "drei Elemente sind eine Chimäre") -## Mana: 5 im ersten Zug, jede Runde eins mehr, der Rest verfällt; Legen kostet die Kartenkosten. +## Mana: 1 im ersten Zug, jede Runde eins mehr, der Rest verfällt; Legen kostet die Kartenkosten. func test_mana_und_kosten() -> void: var s := setup(["f_wolf", "f_drache"]) var g: Game = s[0] @@ -120,10 +120,10 @@ func test_mana_und_kosten() -> void: h.max_energy = 0 h.turns = 0 g.begin_turn(h) - check(h.energy == Game.START_MANA and h.max_energy == Game.START_MANA, "erster Zug: 5 Mana") + check(h.energy == Game.START_MANA and h.max_energy == Game.START_MANA, "erster Zug: 1 Mana") h.energy = 3 # Rest g.begin_turn(h) - check(h.energy == Game.START_MANA + 1, "zweiter Zug: 6 Mana, der Rest ist verfallen") + check(h.energy == Game.START_MANA + 1, "zweiter Zug: 2 Mana, der Rest ist verfallen") for i in 20: g.begin_turn(h) check(h.energy == Game.START_MANA + 21, "keine Obergrenze") @@ -133,7 +133,8 @@ func test_mana_und_kosten() -> void: check(Cards.fusion_value(4, 2) == 5 and Cards.fusion_value(8, 1) == 9 and Cards.fusion_value(2, 2) == 3 and Cards.fusion_value(0, 4) == 4 and Cards.fusion_value(8, 8) == 11, "Fusionswert: besserer Wert plus ein Drittel des schwächeren") -## Start: fünf bezahlbare Karten, keine Wahl. Ziehen: 1 Mana, eine bezahlbare Karte vom Stapel. +## Start: fünf im ersten Zug bezahlbare Karten, keine Wahl. Jeder Zug beginnt mit einer Karte; +## Ziehen: 2 Mana, eine weitere bezahlbare Karte vom Stapel. func test_ziehen() -> void: var r := RandomNumberGenerator.new() r.seed = 1 @@ -141,14 +142,17 @@ func test_ziehen() -> void: check(g.deck.size() == Cards.all().size() * DeckGen.COPIES - 2 * Game.START_HAND, "Stapel: jede Karte dreimal, zehn schon ausgegeben") for p in g.players: check(p.hand.size() == Game.START_HAND, "Start: fünf Karten auf der Hand") - check(p.hand.all(func(c): return c.cost <= Game.START_MANA + p.start_bonus), "nur Karten, die im ersten Zug spielbar sind") + check(p.hand.all(func(c): return c.cost <= Game.START_MANA), "nur Karten, die im ersten Zug spielbar sind") check(p.hand.all(func(c): return c != Cards.by_id(c.id)), "eigene Exemplare") var first: Game.Player = g.current() check(g.draw(first) == "Deine Hand ist voll", "fünf Handkarten sind das Limit") first.hand.pop_back() + check(g.draw(first) == "Nicht genug Mana", "im ersten Zug reicht das Mana nicht zum Ziehen") + first.energy = 3 + first.max_energy = 3 var mana := first.energy var deck_before := g.deck.size() - check(g.draw(first) == "" and first.energy == mana - 1 and first.hand.size() == Game.START_HAND, "gezogen: 1 Mana, eine Karte") + check(g.draw(first) == "" and first.energy == mana - Game.DRAW_COST and first.hand.size() == Game.START_HAND, "gezogen: 2 Mana, eine Karte") check(first.hand.back().cost <= first.max_energy and g.deck.size() == deck_before - 1, "bezahlbar; zu teure liegen wieder unten im Stapel") first.energy = 0 first.hand.pop_back() @@ -165,10 +169,25 @@ func test_ziehen() -> void: var seen_expensive := false for i in 12: first.hand = [] + first.energy = 20 g.draw(first) if first.hand[0].cost > 5: seen_expensive = true check(seen_expensive, "mit 20 Mana kommen auch teure Karten") + # Zugbeginn: eine Karte gratis, solange die Hand nicht voll ist + var g5 := Game.new_battle(r) + var p1: Game.Player = g5.current() + var p2: Game.Player = g5.enemy_of(p1) + g5.end_turn() + check(p2.hand.size() == Game.START_HAND, "volle Hand: zu Zugbeginn kommt nichts") + p2.hand.pop_back() + p2.hand.pop_back() + g5.end_turn() + p1.hand.pop_back() + g5.end_turn() + check(p2.hand.size() == Game.START_HAND - 1 and p2.hand.back().cost <= p2.max_energy, "Zugbeginn: eine bezahlbare Karte gezogen") + g5.end_turn() + check(p1.hand.size() == Game.START_HAND, "auch der Startspieler zieht zu Zugbeginn") ## Gelegte Wesen greifen erst im nächsten Zug an; kein Durchschlag, keine Heilung zu Zugbeginn. @@ -219,12 +238,12 @@ func test_elementvorteil() -> void: var wolf := put(o, "f_wolf") # Feuer 3/2 var life: int = o.life g.attack(tiefen, wolf) - check(wolf.health <= 0 and o.life == life and tiefen.health == 6 - 3, "Wasser gegen Feuer: +1 beim Zuschlagen, der Konter bleibt gleich, nichts schlägt durch") + check(wolf.health <= 0 and o.life == life and tiefen.health == 6 - 2, "Wasser gegen Feuer: +1 beim Zuschlagen, der Konter macht 1 weniger, nichts schlägt durch") var s3 := setup() var w3 := put(s3[1], "f_wolf") var t3 := put(s3[2], "w_tiefen") s3[0].attack(w3, t3) - check(t3.health == 6 - 2 and w3.health <= 0, "Feuer gegen Wasser: 1 weniger Schaden, der Konter bleibt gleich") + check(t3.health == 6 - 2 and w3.health == 2 - 5, "Feuer gegen Wasser: 1 weniger Schaden, der Konter macht 1 mehr") var s2 := setup() var g2: Game = s2[0] var geist := put(s2[1], "w_geist") # Wasser 2/2 @@ -451,7 +470,7 @@ func test_verschmelzen() -> void: check(hc.name == "Dampf" and hc.attack == 5 and hc.health == 7 and hc.cost == 5 and hc.fusions == 1, "Dampf auf der Hand mit 5/7 und Kosten 5") var lava := Cards.fused(C("f_asche"), C("f_kobold"), 4, 8) check(Cards.fused(lava, C("f_kobold")).attack == 5 and Cards.fused(lava, C("f_kobold")).health == 9, "ein Kobold macht Lava 4/8 zu 5/9, nie schlechter") - check(g4.play_card(h4, hc) == "" and h4.energy == 4 and h4.board.back().name == "Dampf", "Ausspielen kostet die neuen Kosten") + check(g4.play_card(h4, hc) == "" and h4.energy == 9 - 5 and h4.board.back().name == "Dampf", "Ausspielen kostet die neuen Kosten") check(g4.log.any(func(l): return l == "Flammenwolf und Tiefenwächter verschmelzen auf der Hand zu Dampf (5/7)."), "Verlauf") @@ -475,18 +494,16 @@ func test_gleiche_regeln() -> void: r.seed = 0 var g := Game.new_battle(r) var first: Game.Player = g.players[0] - check(g.current() == first and first.max_energy == Game.START_MANA and first.energy == Game.START_MANA, "Startspieler: 5 Mana, kein Bonus") + check(g.current() == first and first.max_energy == Game.START_MANA and first.energy == Game.START_MANA, "Startspieler: 1 Mana, kein Bonus") check(g.players.all(func(p): return p.hand.size() == Game.START_HAND), "jeder beginnt mit fünf Karten") for p in g.players: check(p.life == Game.START_LIFE, "gleiches Leben") - if p != first: - check(p.start_bonus == 1, "Nachzieher: Bonus offen") g.end_turn() var second: Game.Player = g.players[1] - check(g.current() == second and second.energy == Game.START_MANA + 1, "Nachzieher: 5 Mana plus Ausgleich") + check(g.current() == second and second.energy == Game.START_MANA, "Nachzieher: ebenfalls 1 Mana, kein Ausgleich") for i in 2: g.end_turn() - check(g.current() == second and second.max_energy == Game.START_MANA + 1 and second.energy == Game.START_MANA + 1, "Ausgleich nur einmal, Rest verfällt") + check(g.current() == second and second.max_energy == Game.START_MANA + 1 and second.energy == Game.START_MANA + 1, "zweiter Zug: 2 Mana") func test_zugreihenfolge() -> void: @@ -756,11 +773,11 @@ func test_merkmale() -> void: var archi: Game.Player = g3.opponent check(g3.players[0] == g3.human, "feste Reihenfolge: Mensch beginnt") g3.end_turn() - check(g3.current() == archi and archi.energy == Game.START_MANA + 2 + 1, "Archimagier hat im ersten Zug 2 Mana mehr (plus Ausgleich)") + check(g3.current() == archi and archi.energy == Game.START_MANA + 2, "Archimagier hat im ersten Zug 2 Mana mehr") var nyx: Game.Player = Campaign.opponent(5) var g4 := Game.new_battle_with(nyx, r, true) g4.end_turn() - check(nyx.max_energy == Game.START_MANA and nyx.energy == Game.START_MANA + 1 + 1, "Nyx bekommt 1 Mana mehr je Zug") + check(nyx.max_energy == Game.START_MANA and nyx.energy == Game.START_MANA + 1, "Nyx bekommt 1 Mana mehr je Zug") func test_ki_zwei_schritte() -> void: diff --git a/tests/ui_test.gd b/tests/ui_test.gd index de54b15..09debde 100644 --- a/tests/ui_test.gd +++ b/tests/ui_test.gd @@ -331,7 +331,7 @@ func test_gegnerauswahl() -> void: var opp: Game.Player = hard.game.opponent check(opp.max_life == Profile.DIFFICULTIES.schwer.life and hard.smart_ai, "Schwer: Gegner mit %d Leben, Vorausschau-KI" % Profile.DIFFICULTIES.schwer.life) check(hard.game.human.hand.size() == Game.START_HAND and hard.hand_views.size() == Game.START_HAND, "Start: fünf Karten auf der Hand") - check(hard.game.human.hand.all(func(c): return c.cost <= Game.START_MANA + 1), "alle fünf sind im ersten Zug spielbar") + check(hard.game.human.hand.all(func(c): return c.cost <= Game.START_MANA), "alle fünf sind im ersten Zug spielbar") await shot("05_starthand") check(hard.hero_panels[opp].position == BattleLayout.OPP_BOX.position, "der Gegner steht oben in der Mitte") await shot("11_kampf") @@ -507,7 +507,7 @@ func test_kampf_steuerung() -> void: g.human.hand = [Cards.by_id("f_wolf").copy()] scr.refresh() check(scr.hand_views.size() == 1 and scr.deck.enabled, "eine Handkarte, Stapel bereit") - # Ziehen: 1 Mana weg, eine Karte landet auf der Hand + # Ziehen: 2 Mana weg, eine Karte landet auf der Hand var mana: int = g.human.energy await click(scr.deck) check(g.human.energy == mana - Game.DRAW_COST and g.human.hand.size() == 2 and scr.hand_views.size() == 2, "gezogen: eine Karte auf der Hand") @@ -636,7 +636,7 @@ func test_kampf_elemente_und_verschmelzen() -> void: # Rechtsklick auf die Handkarte: Menü mit Verdeckt legen und Eigenschaften, ohne Verschmelzen await hover(scr.hand_views[0]) await right_click_at(scr.hand_views[0].get_global_rect().get_center()) - check(scr.menu_layer.visible and find_button("Verdeckt legen ◆ 1", scr.menu_layer) != null and find_button("Spott ◆ 2", scr.menu_layer) != null, "Rechtsklick öffnet das Kartenmenü") + check(scr.menu_layer.visible and find_button("Verdeckt legen ◆ %d" % g.human.hand[0].cost, scr.menu_layer) != null and find_button("Spott ◆ 2", scr.menu_layer) != null, "Rechtsklick öffnet das Kartenmenü") check(find_button("Verschmelzen mit … ◆ 1", scr.menu_layer) == null, "kein Verschmelzen im Rechtsklick-Menü") await shot("23_kartenmenue") await right_click() @@ -665,6 +665,10 @@ func test_kampf_elemente_und_verschmelzen() -> void: # Angriffsziel: Elementvorteil, verdeckte ohne Anzeige, Held gesperrt await click(scr.own_views[0]) check(scr.selected == wolf and scr.tiles[falke.uid].matchup == 1 and scr.tiles[secret.uid].matchup == 0, "Angreifer gewählt: +1 gegen Luft, nichts gegen Verdeckte") + check(scr.tiles[falke.uid].shown_stats() == [falke.power() - 1, falke.health], "der Falke zeigt seinen Konter gegen den Wolf (1 weniger) und sein echtes Leben") + await hover(scr.tiles[falke.uid]) + check(scr.tiles[falke.uid].doomed and not scr.own_views[0].doomed and scr.own_views[0].shown_stats()[0] == wolf.power() + 1, "über dem Falken: er stirbt, der Wolf nicht, der Wolf schlägt mit +1 zu") + await shot("23c_angreifer_gewaehlt") check(not g.can_attack(wolf, scr.opponent())[0], "Held nicht angreifbar, solange Wesen stehen") await click(scr.tiles[secret.uid]) check(not secret.hidden and not (secret in scr.opponent().board), "Angriff deckt auf und tötet den Kobold") @@ -686,7 +690,7 @@ func test_kampf_elemente_und_verschmelzen() -> void: check(g.human.hand.size() == 2 and g.human.hand[0].name == "Inferno" and g.human.hand[0].attack == 3 and g.human.hand[0].health == 2, "zwei Kobolde auf der Hand zu Inferno 3/2") # Gamepad-Menü (Y) bietet Verdeckt legen und Verschmelzen mit … scr.open_menu(g.human.hand[0], Rect2(scr.hand_views[0].position, scr.hand_views[0].size), true) - check(find_button("Verdeckt legen ◆ 2", scr.menu_layer) != null, "Gamepad-Menü: Verdeckt legen") + check(find_button("Verdeckt legen ◆ %d" % g.human.hand[0].cost, scr.menu_layer) != null and g.human.hand[0].cost == 2, "Gamepad-Menü: Verdeckt legen, Inferno kostet 2") await click(find_button("Verschmelzen mit … ◆ 2", scr.menu_layer)) check(scr.fuse_first == g.human.hand[0], "Partner wählen") await click(scr.hand_views[1]) diff --git a/translations/ui.csv b/translations/ui.csv index a1d347a..1f8d1ca 100644 --- a/translations/ui.csv +++ b/translations/ui.csv @@ -154,3 +154,4 @@ Erst die Wesen mit Spott,Taunting creatures first greift erst im nächsten Zug an,attacks next turn %s durch das Element,%s from its element Zwei verschiedene Karten wählen,Pick two different cards +stirbt,dies diff --git a/translations/ui.en.translation b/translations/ui.en.translation index bedd614..541238a 100644 Binary files a/translations/ui.en.translation and b/translations/ui.en.translation differ