update
This commit is contained in:
287
scripts/game.gd
287
scripts/game.gd
@@ -1,17 +1,16 @@
|
||||
class_name Game
|
||||
extends RefCounted
|
||||
## Spielregeln: Duell, Mana je Zug, Ziehen aus dem gemeinsamen Stapel, verdeckte Wesen,
|
||||
## Vier-Elemente-Regel, Verschmelzen und Verbessern.
|
||||
## Elementboni, Verschmelzen und Verbessern.
|
||||
|
||||
const MAX_BOARD := 5
|
||||
const MAX_HAND := 5
|
||||
const START_MANA := 5 # Mana in Zug 1; jede Runde eins mehr, ohne Grenze; Rest verfällt
|
||||
const DRAW_COST := 1 # Ziehen: drei Karten sehen, eine behalten
|
||||
const OFFER_SIZE := 3
|
||||
const START_OFFER := 5 # zu Beginn fünf sehen, drei behalten
|
||||
const START_PICKS := 3
|
||||
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_LIFE := 30
|
||||
const ELEMENT_STACK := 4 # so viele offene Wesen eines Elements auf dem Tisch: +1/+1, das geschlagene Element -1/-1
|
||||
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
|
||||
|
||||
# Merkmale der Kampagnen-Bosse (Player.perks).
|
||||
const PERKS := {
|
||||
@@ -21,7 +20,7 @@ const PERKS := {
|
||||
"wesen_leben": "Wesen kommen mit +1 Leben",
|
||||
"extra_mana": "Bekommt jeden Zug 1 Mana mehr",
|
||||
"leben_35": "Beginnt mit 5 Leben mehr",
|
||||
"schild_wesen": "Wesen kommen mit Schild",
|
||||
"alle_flink": "Wesen greifen sofort an",
|
||||
"verbessern_billig": "Eigenschaften kosten 1 weniger",
|
||||
"start_energie": "Hat im ersten Zug 2 Mana mehr",
|
||||
"element_doppelt": "Elementbonus zählt doppelt",
|
||||
@@ -49,10 +48,10 @@ class Creature extends RefCounted:
|
||||
var attack: int
|
||||
var health: int
|
||||
var max_health: int
|
||||
var shield: bool
|
||||
var attacks_left: int
|
||||
var hidden := false # verdeckt gelegt: greift nicht an, zählt nicht für die Elementregel
|
||||
var aura := 0 # Vier-Elemente-Regel: +1 oder -1 auf den Angriff (Leben wird direkt angepasst)
|
||||
var attacks_done := 0 # Angriffe in diesem Zug
|
||||
var ready := false # lag schon zu Beginn des eigenen Zugs: darf angreifen und aufgedeckt werden
|
||||
var hidden := false # verdeckt gelegt: greift nicht an, zählt nicht für die Elementboni
|
||||
var aura := 0 # Elementbonus (+1 je Element mit vollem Stapel); das Leben ist schon angepasst
|
||||
|
||||
func _init(c: Cards.Card, o, id: int) -> void:
|
||||
card = c
|
||||
@@ -62,12 +61,35 @@ class Creature extends RefCounted:
|
||||
attack = c.attack
|
||||
health = c.health
|
||||
max_health = c.health
|
||||
shield = "Schild" in c.keywords
|
||||
attacks_left = attacks_per_turn() # frisch gelegte Wesen greifen sofort an
|
||||
|
||||
func owner():
|
||||
return owner_ref.get_ref()
|
||||
|
||||
## Eigenschaft: gekauft, als Sondereffekt des Elementpaars der eigenen Seite oder als
|
||||
## Boss-Merkmal. Verdeckte Wesen haben keine.
|
||||
func has(kw: String) -> bool:
|
||||
if hidden:
|
||||
return false
|
||||
if kw in card.keywords:
|
||||
return true
|
||||
var o = owner()
|
||||
if o == null:
|
||||
return false
|
||||
if kw == "Flink" and "alle_flink" in o.perks:
|
||||
return true
|
||||
for e in card.elements:
|
||||
if Cards.ELEMENT_KEYWORD.get(e, "") == kw and o.element_count(e) >= Game.ELEMENT_PAIR:
|
||||
return true
|
||||
return false
|
||||
|
||||
## Alle wirksamen Eigenschaften in fester Reihenfolge.
|
||||
func keywords() -> Array[String]:
|
||||
var out: Array[String] = []
|
||||
for k in Cards.KEYWORDS:
|
||||
if has(k):
|
||||
out.append(k)
|
||||
return out
|
||||
|
||||
## Angriffswert inklusive Elementbonus und Boss-Merkmalen.
|
||||
func power() -> int:
|
||||
var v := attack + aura
|
||||
@@ -78,18 +100,22 @@ class Creature extends RefCounted:
|
||||
return v
|
||||
|
||||
func attacks_per_turn() -> int:
|
||||
return 2 if "Doppelschlag" in card.keywords else 1
|
||||
return 2 if has("Doppelschlag") else 1
|
||||
|
||||
func attacks_left() -> int:
|
||||
return maxi(0, attacks_per_turn() - attacks_done)
|
||||
|
||||
## Angreifen darf, wer offen liegt, noch Angriffe hat und seit einer Runde liegt (oder flink ist).
|
||||
func can_attack() -> bool:
|
||||
return attacks_left > 0 and not hidden and power() > 0
|
||||
return not hidden and attacks_left() > 0 and power() > 0 and (ready or has("Flink"))
|
||||
|
||||
func clone(new_owner) -> Creature:
|
||||
var c := Creature.new(card, new_owner, uid)
|
||||
c.attack = attack
|
||||
c.health = health
|
||||
c.max_health = max_health
|
||||
c.shield = shield
|
||||
c.attacks_left = attacks_left
|
||||
c.attacks_done = attacks_done
|
||||
c.ready = ready
|
||||
c.hidden = hidden
|
||||
c.aura = aura
|
||||
return c
|
||||
@@ -101,8 +127,6 @@ class Player extends RefCounted:
|
||||
var is_human: bool
|
||||
var life: int
|
||||
var max_life: int
|
||||
var offer: Array = [] # offene Wahl: angebotene Karten (Start: 5, Ziehen: 3)
|
||||
var picks_left := 0 # wie viele davon noch zu nehmen sind
|
||||
var hand: Array = [] # eigene Exemplare (Cards.Card.copy)
|
||||
var board: Array = []
|
||||
var energy := 0 # Mana in diesem Zug; der Rest verfällt
|
||||
@@ -123,11 +147,17 @@ class Player extends RefCounted:
|
||||
func alive() -> bool:
|
||||
return life > 0
|
||||
|
||||
## Offene eigene Wesen mit diesem Element.
|
||||
func element_count(e: String) -> int:
|
||||
var n := 0
|
||||
for c in board:
|
||||
if not c.hidden and e in c.card.elements:
|
||||
n += 1
|
||||
return n
|
||||
|
||||
func clone() -> Player:
|
||||
var p := Player.new(name, is_human, life, title)
|
||||
p.max_life = max_life
|
||||
p.offer = offer.duplicate()
|
||||
p.picks_left = picks_left
|
||||
p.hand = hand.duplicate()
|
||||
p.energy = energy
|
||||
p.max_energy = max_energy
|
||||
@@ -235,7 +265,6 @@ func masked_clone(viewer: Player) -> Game:
|
||||
c.attack = c.card.attack
|
||||
c.health = c.card.health
|
||||
c.max_health = c.card.health
|
||||
c.shield = false
|
||||
return g
|
||||
|
||||
|
||||
@@ -250,8 +279,8 @@ func start(fixed_order: bool = false) -> void:
|
||||
p.order = i
|
||||
# Wer nicht beginnt, bekommt im ersten Zug einmalig 1 Mana extra.
|
||||
p.start_bonus = 0 if i == 0 else 1
|
||||
p.offer = draw_cards(START_OFFER, START_MANA + p.start_bonus)
|
||||
p.picks_left = START_PICKS
|
||||
for c in draw_cards(START_HAND, START_MANA + p.start_bonus):
|
||||
p.hand.append(c.copy())
|
||||
turn = 1
|
||||
current_idx = 0
|
||||
say("Zugreihenfolge: " + ", ".join(players.map(func(p): return p.name)))
|
||||
@@ -282,17 +311,10 @@ func draw_cards(n: int, max_cost: int) -> Array:
|
||||
return out
|
||||
|
||||
|
||||
## Solange eine Wahl offen ist, geht nichts anderes.
|
||||
func blocked(p: Player) -> String:
|
||||
return "Erst wählen" if not p.offer.is_empty() else ""
|
||||
|
||||
|
||||
## Ziehen: kostet DRAW_COST, zeigt OFFER_SIZE Karten, eine davon kommt auf die Hand (siehe pick).
|
||||
## Ziehen: kostet DRAW_COST, eine Karte kommt auf die Hand.
|
||||
func can_draw(p: Player) -> Array:
|
||||
if winner != "" or p != current():
|
||||
return [false, "Nicht dein Zug"]
|
||||
if blocked(p) != "":
|
||||
return [false, blocked(p)]
|
||||
if p.hand.size() >= MAX_HAND:
|
||||
return [false, "Deine Hand ist voll"]
|
||||
if p.energy < draw_cost(p):
|
||||
@@ -315,36 +337,13 @@ func draw(p: Player) -> String:
|
||||
if not ok[0]:
|
||||
return ok[1]
|
||||
p.energy -= draw_cost(p)
|
||||
p.offer = draw_cards(OFFER_SIZE, p.max_energy)
|
||||
p.picks_left = 1
|
||||
var card: Cards.Card = draw_cards(1, p.max_energy)[0]
|
||||
p.hand.append(card.copy())
|
||||
say("%s zieht." % p.name)
|
||||
emit({"type": "draw", "player": players.find(p)})
|
||||
return ""
|
||||
|
||||
|
||||
## Nimmt eine der angebotenen Karten auf die Hand; jederzeit erlaubt, auch außerhalb des Zugs.
|
||||
## Was liegen bleibt, wandert unter den Stapel.
|
||||
func pick(p: Player, index: int) -> String:
|
||||
if winner != "":
|
||||
return "Vorbei"
|
||||
if p.offer.is_empty():
|
||||
return "Keine Wahl offen"
|
||||
if index < 0 or index >= p.offer.size():
|
||||
return "Keine solche Karte"
|
||||
var card: Cards.Card = p.offer[index]
|
||||
p.hand.append(card.copy())
|
||||
p.offer.remove_at(index)
|
||||
p.picks_left -= 1
|
||||
if p.picks_left <= 0 or p.hand.size() >= MAX_HAND:
|
||||
for rest in p.offer:
|
||||
deck.insert(0, rest)
|
||||
p.offer = []
|
||||
p.picks_left = 0
|
||||
say("%s nimmt eine Karte." % p.name)
|
||||
emit({"type": "pick", "player": players.find(p)})
|
||||
return ""
|
||||
|
||||
|
||||
func begin_turn(p: Player) -> void:
|
||||
p.max_energy = START_MANA + p.turns
|
||||
p.energy = p.max_energy + (1 if "extra_mana" in p.perks else 0) # der Rest vom letzten Zug ist verfallen
|
||||
@@ -355,18 +354,22 @@ func begin_turn(p: Player) -> void:
|
||||
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.health = c.max_health # Wesen erholen sich zu Zugbeginn ganz
|
||||
c.attacks_left = c.attacks_per_turn()
|
||||
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)})
|
||||
|
||||
|
||||
## Zugende: Wesen mit Regeneration heilen ganz, dann ist der andere dran.
|
||||
func end_turn() -> void:
|
||||
if winner != "":
|
||||
return
|
||||
while not current().offer.is_empty(): # offene Wahl: die erste Karte, damit nichts verfällt
|
||||
pick(current(), 0)
|
||||
for c in current().board:
|
||||
if c.has("Regeneration") and c.health < c.max_health:
|
||||
c.health = c.max_health
|
||||
say("%s regeneriert sich." % c.name)
|
||||
emit({"type": "heal", "uid": c.uid})
|
||||
var n := players.size()
|
||||
for i in range(1, n + 1):
|
||||
var idx := (current_idx + i) % n
|
||||
@@ -384,8 +387,6 @@ func end_turn() -> void:
|
||||
func can_play(p: Player, card: Cards.Card) -> Array:
|
||||
if winner != "" or p != current():
|
||||
return [false, "Nicht dein Zug"]
|
||||
if blocked(p) != "":
|
||||
return [false, blocked(p)]
|
||||
if not (card in p.hand):
|
||||
return [false, "Nicht auf der Hand"]
|
||||
if p.board.size() >= MAX_BOARD:
|
||||
@@ -395,7 +396,8 @@ func can_play(p: Player, card: Cards.Card) -> Array:
|
||||
return [true, ""]
|
||||
|
||||
|
||||
## Spielt eine Karte, offen oder verdeckt. Gibt "" zurück oder eine Fehlermeldung.
|
||||
## Spielt eine Karte, offen oder verdeckt. Das Wesen greift erst im nächsten Zug an (außer flink).
|
||||
## Gibt "" zurück oder eine Fehlermeldung.
|
||||
func play_card(p: Player, card: Cards.Card, face_down: bool = false) -> String:
|
||||
var ok := can_play(p, card)
|
||||
if not ok[0]:
|
||||
@@ -410,8 +412,6 @@ func play_card(p: Player, card: Cards.Card, face_down: bool = false) -> String:
|
||||
if "wesen_leben" in p.perks:
|
||||
cr.health += 1
|
||||
cr.max_health += 1
|
||||
if "schild_wesen" in p.perks:
|
||||
cr.shield = true
|
||||
p.board.append(cr)
|
||||
if face_down:
|
||||
say("%s legt ein Wesen verdeckt." % p.name)
|
||||
@@ -424,16 +424,17 @@ func play_card(p: Player, card: Cards.Card, face_down: bool = false) -> String:
|
||||
return ""
|
||||
|
||||
|
||||
## Ein eigenes verdecktes Wesen aufdecken: kostenlos, im eigenen Zug.
|
||||
## Ein eigenes verdecktes Wesen aufdecken: kostenlos, im eigenen Zug, frühestens einen Zug
|
||||
## nach dem Legen. Aufgedeckt greift es sofort an.
|
||||
func can_reveal(p: Player, cr: Creature) -> Array:
|
||||
if winner != "" or p != current():
|
||||
return [false, "Nicht dein Zug"]
|
||||
if blocked(p) != "":
|
||||
return [false, blocked(p)]
|
||||
if cr == null or not (cr in p.board):
|
||||
return [false, "Nur eigene Wesen"]
|
||||
if not cr.hidden:
|
||||
return [false, "%s liegt schon offen" % cr.name]
|
||||
if not cr.ready:
|
||||
return [false, "%s kann erst im nächsten Zug aufgedeckt werden" % cr.name]
|
||||
return [true, ""]
|
||||
|
||||
|
||||
@@ -447,7 +448,7 @@ func reveal(p: Player, cr: Creature) -> String:
|
||||
return ""
|
||||
|
||||
|
||||
## Deckt ein Wesen auf; die Elementregel gilt sofort.
|
||||
## Deckt ein Wesen auf; die Elementboni gelten sofort.
|
||||
func _flip(cr: Creature) -> void:
|
||||
cr.hidden = false
|
||||
say("%s wird aufgedeckt." % cr.name)
|
||||
@@ -455,14 +456,12 @@ func _flip(cr: Creature) -> void:
|
||||
apply_auras()
|
||||
|
||||
|
||||
# --- Verbessern: Schlüsselwörter für Mana -----------------------------------
|
||||
# --- Verbessern: Eigenschaften für Mana -------------------------------------
|
||||
|
||||
## target: eigenes Wesen auf dem Brett oder eigene Handkarte.
|
||||
func can_upgrade(p: Player, target, keyword: String) -> Array:
|
||||
if winner != "" or p != current():
|
||||
return [false, "Nicht dein Zug"]
|
||||
if blocked(p) != "":
|
||||
return [false, blocked(p)]
|
||||
if not Cards.KEYWORD_COST.has(keyword):
|
||||
return [false, "Unbekannte Eigenschaft"]
|
||||
var card: Cards.Card = target.card if target is Creature else target
|
||||
@@ -489,12 +488,6 @@ func upgrade(p: Player, target, keyword: String) -> String:
|
||||
target.card = card
|
||||
else:
|
||||
p.hand[p.hand.find(target)] = card
|
||||
if target is Creature:
|
||||
match keyword:
|
||||
"Schild":
|
||||
target.shield = true
|
||||
"Doppelschlag":
|
||||
target.attacks_left = mini(target.attacks_per_turn(), target.attacks_left + 1)
|
||||
say("%s bekommt %s." % [card.name, keyword])
|
||||
emit({"type": "upgrade", "player": players.find(p), "keyword": keyword, "uid": target.uid if target is Creature else -1})
|
||||
return ""
|
||||
@@ -502,40 +495,69 @@ func upgrade(p: Player, target, keyword: String) -> String:
|
||||
|
||||
# --- Verschmelzen ------------------------------------------------------------
|
||||
|
||||
## Kosten: 1 Mana, verdoppelt je Verschmelzung, die schon in einem der beiden steckt.
|
||||
static func fusion_cost(a: Creature, b: Creature) -> int:
|
||||
return 1 << maxi(a.card.fusions, b.card.fusions)
|
||||
static func card_of(x) -> Cards.Card:
|
||||
return x.card if x is Creature else x
|
||||
|
||||
|
||||
func can_fuse(p: Player, a: Creature, b: Creature) -> Array:
|
||||
## Seltenheitsstufe einer Karte von p: bei verschmolzenen die geerbte, sonst die der Sammlung.
|
||||
static func look_of(p: Player, c: Cards.Card) -> int:
|
||||
return c.look if c.look >= 0 else int(p.looks.get(c.id, 0))
|
||||
|
||||
|
||||
## Kosten: 1 Mana, verdoppelt je Verschmelzung, die schon in einer der beiden steckt.
|
||||
static func fusion_cost(a, b) -> int:
|
||||
return 1 << maxi(card_of(a).fusions, card_of(b).fusions)
|
||||
|
||||
|
||||
## a und b: zwei eigene Wesen auf dem Brett oder zwei eigene Handkarten.
|
||||
func can_fuse(p: Player, a, b) -> Array:
|
||||
if winner != "" or p != current():
|
||||
return [false, "Nicht dein Zug"]
|
||||
if blocked(p) != "":
|
||||
return [false, blocked(p)]
|
||||
if a == null or b == null or a == b:
|
||||
return [false, "Zwei verschiedene Wesen wählen"]
|
||||
if not (a in p.board) or not (b in p.board):
|
||||
return [false, "Nur eigene Wesen"]
|
||||
return [false, "Zwei verschiedene Karten wählen"]
|
||||
if a is Creature and b is Creature:
|
||||
if not (a in p.board) or not (b in p.board):
|
||||
return [false, "Nur eigene Wesen"]
|
||||
elif a is Creature or b is Creature:
|
||||
return [false, "Nur Hand mit Hand oder Brett mit Brett"]
|
||||
elif not (a in p.hand) or not (b in p.hand):
|
||||
return [false, "Nicht auf der Hand"]
|
||||
if p.energy < fusion_cost(a, b):
|
||||
return [false, "Nicht genug Mana"]
|
||||
return [true, ""]
|
||||
|
||||
|
||||
## Verschmilzt zwei eigene Wesen zu einem offenen Wesen: Elemente und Schlüsselwörter beider,
|
||||
## jeweils der bessere Wert (Angriff, Leben, Lebenshöchstwert) plus 1. Der Elementbonus der
|
||||
## Eltern wird herausgerechnet, das neue Wesen bekommt seinen eigenen.
|
||||
func fuse(p: Player, a: Creature, b: Creature) -> String:
|
||||
## Verschmilzt zwei Handkarten zu einer oder zwei eigene Wesen zu einem offenen: Elemente und
|
||||
## Eigenschaften beider, Werte nach Cards.fusion_value, Kosten folgen den Werten.
|
||||
func fuse(p: Player, a, b) -> String:
|
||||
var ok := can_fuse(p, a, b)
|
||||
if not ok[0]:
|
||||
return ok[1]
|
||||
p.energy -= fusion_cost(a, b)
|
||||
var card := Cards.fused(a.card, b.card, maxi(a.attack, b.attack) + 1,
|
||||
maxi(a.max_health - a.aura, b.max_health - b.aura) + 1)
|
||||
if a is Creature:
|
||||
return _fuse_board(p, a, b)
|
||||
var card := Cards.fused(a, b)
|
||||
card.look = maxi(look_of(p, a), look_of(p, b))
|
||||
var at := p.hand.find(a)
|
||||
p.hand.erase(a)
|
||||
p.hand.erase(b)
|
||||
p.hand.insert(mini(at, p.hand.size()), card)
|
||||
say("%s und %s verschmelzen auf der Hand zu %s (%d/%d)." % [a.name, b.name, card.name, card.attack, card.health])
|
||||
emit({"type": "fuse", "player": players.find(p), "uid": -1, "gone": []})
|
||||
return ""
|
||||
|
||||
|
||||
## Auf dem Brett zählen die Werte der Wesen (ohne Elementbonus, den bekommt das neue selbst);
|
||||
## das Ergebnis ist nur angriffsbereit, wenn beide es waren.
|
||||
func _fuse_board(p: Player, a: Creature, b: Creature) -> String:
|
||||
var card := Cards.fused(a.card, b.card, Cards.fusion_value(a.attack, b.attack),
|
||||
Cards.fusion_value(a.max_health - a.aura, b.max_health - b.aura))
|
||||
card.look = maxi(look_of(p, a.card), look_of(p, b.card))
|
||||
var cr := Creature.new(card, p, next_uid)
|
||||
next_uid += 1
|
||||
cr.health = maxi(a.health - a.aura, b.health - b.aura) + 1
|
||||
cr.shield = cr.shield or a.shield or b.shield
|
||||
cr.attacks_left = mini(a.attacks_left, b.attacks_left)
|
||||
cr.health = mini(card.health, Cards.fusion_value(mini(a.health, a.max_health - a.aura), mini(b.health, b.max_health - b.aura)))
|
||||
cr.ready = a.ready and b.ready
|
||||
cr.attacks_done = maxi(a.attacks_done, b.attacks_done)
|
||||
var at := p.board.find(a)
|
||||
p.board.erase(a)
|
||||
p.board.erase(b)
|
||||
@@ -548,7 +570,7 @@ func fuse(p: Player, a: Creature, b: Creature) -> String:
|
||||
return ""
|
||||
|
||||
|
||||
# --- Vier-Elemente-Regel -----------------------------------------------------
|
||||
# --- Elementboni ---------------------------------------------------------------
|
||||
|
||||
## Offene Wesen je Element auf dem ganzen Tisch.
|
||||
func element_counts() -> Dictionary:
|
||||
@@ -563,24 +585,16 @@ func element_counts() -> Dictionary:
|
||||
return counts
|
||||
|
||||
|
||||
## Bonus aus der Elementregel für diese Elemente: +1, wenn ELEMENT_STACK offene Wesen des
|
||||
## Elements stehen, -1, wenn das Element, das es schlägt, den Stapel voll hat.
|
||||
## Mehrere Elemente nehmen den besten Wert.
|
||||
## +1 je Element mit ELEMENT_STACK offenen Wesen auf dem Tisch; mehrere Elemente zählen alle.
|
||||
static func aura_of(elements: Array, counts: Dictionary) -> int:
|
||||
var best := 0
|
||||
for i in elements.size():
|
||||
var e: String = elements[i]
|
||||
var a := 0
|
||||
var a := 0
|
||||
for e in elements:
|
||||
if int(counts[e]) >= ELEMENT_STACK:
|
||||
a += 1
|
||||
var predator := Cards.beaten_by(e)
|
||||
if predator != "" and int(counts[predator]) >= ELEMENT_STACK:
|
||||
a -= 1
|
||||
best = a if i == 0 else maxi(best, a)
|
||||
return best
|
||||
return a
|
||||
|
||||
|
||||
## Bonus eines offenen Wesens aus der Elementregel; verdeckte zählen nicht.
|
||||
## Bonus eines offenen Wesens; verdeckte zählen nicht und bekommen nichts.
|
||||
func aura_for(c: Creature, counts: Dictionary) -> int:
|
||||
if c.hidden:
|
||||
return 0
|
||||
@@ -591,7 +605,8 @@ func aura_for(c: Creature, counts: Dictionary) -> int:
|
||||
return a
|
||||
|
||||
|
||||
## Rechnet die Elementregel neu; Leben folgt dem Bonus direkt (+1/+1 bzw. -1/-1).
|
||||
## Rechnet die Elementboni neu. Kommt ein Bonus, steigen Leben und Höchstleben; geht er,
|
||||
## sinkt nur das Höchstleben, das Leben wird höchstens darauf gekappt.
|
||||
func apply_auras() -> void:
|
||||
var counts := element_counts()
|
||||
for p in players:
|
||||
@@ -599,8 +614,8 @@ func apply_auras() -> void:
|
||||
var a := aura_for(c, counts)
|
||||
if a != c.aura:
|
||||
var delta: int = a - c.aura
|
||||
c.health += delta
|
||||
c.max_health += delta
|
||||
c.health = mini(c.health + maxi(delta, 0), c.max_health)
|
||||
c.aura = a
|
||||
|
||||
|
||||
@@ -614,14 +629,15 @@ func forecast(p: Player, card: Cards.Card) -> int:
|
||||
|
||||
# --- Kampf -------------------------------------------------------------------
|
||||
|
||||
## Leben lassen sich erst angreifen, wenn der Gegner kein Wesen mehr hat.
|
||||
## Leben lassen sich erst angreifen, wenn der Gegner kein Wesen mehr hat; Wesen mit Spott
|
||||
## müssen vor den anderen angegriffen werden.
|
||||
func can_attack(cr: Creature, target) -> Array:
|
||||
if winner != "" or cr.owner() != current():
|
||||
return [false, "Nicht dein Zug"]
|
||||
if blocked(cr.owner()) != "":
|
||||
return [false, blocked(cr.owner())]
|
||||
if cr.hidden:
|
||||
return [false, "%s liegt verdeckt" % cr.name]
|
||||
if not cr.ready and not cr.has("Flink") and cr.attacks_left() > 0 and cr.power() > 0:
|
||||
return [false, "%s greift erst im nächsten Zug an" % cr.name]
|
||||
if not cr.can_attack():
|
||||
return [false, "%s kann nicht angreifen" % cr.name]
|
||||
var tgt_owner = target.owner() if target is Creature else target
|
||||
@@ -629,40 +645,24 @@ func can_attack(cr: Creature, target) -> Array:
|
||||
return [false, "Kein gültiges Ziel"]
|
||||
if not (target is Creature) and not tgt_owner.board.is_empty():
|
||||
return [false, "Erst die Wesen"]
|
||||
if target is Creature and not target.has("Spott") and tgt_owner.board.any(func(c): return c.has("Spott")):
|
||||
return [false, "Erst die Wesen mit Spott"]
|
||||
return [true, ""]
|
||||
|
||||
|
||||
## Greift an. Ein verdecktes Ziel wird zuerst aufgedeckt. Schaden über das Leben eines Wesens
|
||||
## hinaus schlägt auf seinen Besitzer durch (sonst wäre eine Mauer unverwundbar).
|
||||
## Gibt "" zurück oder eine Fehlermeldung.
|
||||
## Greift an. Ein verdecktes Ziel wird zuerst aufgedeckt. Gibt "" zurück oder eine Fehlermeldung.
|
||||
func attack(cr: Creature, target) -> String:
|
||||
var ok := can_attack(cr, target)
|
||||
if not ok[0]:
|
||||
return ok[1]
|
||||
cr.attacks_left -= 1
|
||||
cr.attacks_done += 1
|
||||
if target is Creature and target.hidden:
|
||||
_flip(target)
|
||||
if target.health <= 0: # der Elementbonus hat es schon erledigt
|
||||
cleanup()
|
||||
check_end()
|
||||
return ""
|
||||
say("%s greift %s an." % [cr.name, target.name])
|
||||
emit({"type": "attack", "attacker": ref_of(cr), "target": ref_of(target)})
|
||||
var dealt := cr.power() + matchup_bonus(cr, target)
|
||||
if target is Creature:
|
||||
var had_shield: bool = target.shield
|
||||
var hp_before: int = target.health
|
||||
damage(target, dealt)
|
||||
if not had_shield and dealt > hp_before:
|
||||
var o = target.owner()
|
||||
var excess := dealt - hp_before
|
||||
o.life -= excess
|
||||
say("%d Schaden schlagen durch auf %s." % [excess, o.name])
|
||||
emit({"type": "damage", "target": ref_of(o), "amount": excess})
|
||||
if target.power() > 0: # wer nichts austeilt, kontert auch nicht; der Konter kennt keinen Elementvorteil
|
||||
damage(cr, target.power())
|
||||
else:
|
||||
damage(target, dealt)
|
||||
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())
|
||||
cleanup()
|
||||
check_end()
|
||||
return ""
|
||||
@@ -680,11 +680,6 @@ func damage(target, amount: int) -> void:
|
||||
if amount <= 0:
|
||||
return
|
||||
if target is Creature:
|
||||
if target.shield:
|
||||
target.shield = false
|
||||
say("Schild von %s blockt den Schaden." % target.name)
|
||||
emit({"type": "blocked", "target": ref_of(target)})
|
||||
return
|
||||
target.health -= amount
|
||||
else:
|
||||
target.life -= amount
|
||||
@@ -692,7 +687,7 @@ func damage(target, amount: int) -> void:
|
||||
emit({"type": "damage", "target": ref_of(target), "amount": amount})
|
||||
|
||||
|
||||
## Räumt Tote ab; danach gilt die Elementregel neu, was weitere Tote nach sich ziehen kann.
|
||||
## Räumt Tote ab; danach gelten die Elementboni neu, was weitere Tote nach sich ziehen kann.
|
||||
func cleanup() -> void:
|
||||
for round in 8:
|
||||
var died := false
|
||||
|
||||
Reference in New Issue
Block a user