update
This commit is contained in:
1 parent
f2c609a08b
commit
1d563b9eec
34 files changed
+5486
-643
No files matched your search
+28
-85
@@ -6,11 +6,10 @@ extends RefCounted
|
||||
const MAX_BOARD := 5
|
||||
const START_HAND := 5 # Startkarten: die obersten des eigenen Stapels
|
||||
const START_MANA := 1 # Mana in Zug 1; jede Runde eins mehr, ohne Grenze; Rest verfällt
|
||||
const DECK_MIN := 40 # Karten je Stapel: mindestens so viele (Gegner und Startdecks genau so viele)
|
||||
const DECK_MIN := 30 # Karten je Stapel: mindestens so viele (Gegner genau so viele: alle Karten eines Elements)
|
||||
const DECK_MAX := 60 # und höchstens so viele
|
||||
const START_LIFE := 30
|
||||
const ELEMENT_PAIR := 2 # so viele eigene offene Wesen eines Elements: Sondereffekt des Elements
|
||||
const ELEMENT_COUNT := 3 # je so viele ausgespielte Karten eines Elements: +1/+1 für die eigenen Wesen des Elements
|
||||
const DISCARD_GAIN := 1 # Abwerfen einer Handkarte bringt so viel Mana
|
||||
|
||||
# Merkmale der Kampagnen-Bosse (Player.perks).
|
||||
@@ -23,7 +22,7 @@ const PERKS := {
|
||||
"leben_35": "Beginnt mit 5 Leben mehr",
|
||||
"alle_flink": "Wesen greifen sofort an",
|
||||
"start_energie": "Hat im ersten Zug 2 Mana mehr",
|
||||
"element_doppelt": "Elementbonus zählt doppelt",
|
||||
"kein_nachteil": "Wesen haben keinen Elementnachteil",
|
||||
}
|
||||
|
||||
## Für wiederholbare Tests: 0 = echter Zufall, sonst Startwert (zählt je Aufruf hoch).
|
||||
@@ -50,8 +49,7 @@ class Creature extends RefCounted:
|
||||
var max_health: int
|
||||
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
|
||||
var hidden := false # verdeckt gelegt: greift nicht an, zählt nicht fürs Elementpaar
|
||||
|
||||
func _init(c: Cards.Card, o, id: int) -> void:
|
||||
card = c
|
||||
@@ -90,9 +88,9 @@ class Creature extends RefCounted:
|
||||
out.append(k)
|
||||
return out
|
||||
|
||||
## Angriffswert inklusive Elementbonus und Boss-Merkmalen.
|
||||
## Angriffswert inklusive Boss-Merkmalen.
|
||||
func power() -> int:
|
||||
var v := attack + aura
|
||||
var v := attack
|
||||
var o = owner()
|
||||
if o != null:
|
||||
if "feuer_angriff" in o.perks and "Feuer" in card.elements:
|
||||
@@ -118,7 +116,6 @@ class Creature extends RefCounted:
|
||||
c.attacks_done = attacks_done
|
||||
c.ready = ready
|
||||
c.hidden = hidden
|
||||
c.aura = aura
|
||||
return c
|
||||
|
||||
|
||||
@@ -137,7 +134,7 @@ class Player extends RefCounted:
|
||||
var perks: Array = [] # Boss-Merkmale (siehe PERKS)
|
||||
var looks := {} # Karten-ID -> Seltenheitsstufe, reine Optik (siehe Profile)
|
||||
var turns := 0 # begonnene eigene Züge
|
||||
var played := {} # Element -> bisher ausgespielte Karten (je ELEMENT_COUNT: +1/+1)
|
||||
var cards_played := 0 # ausgespielte Karten (Münzen nach dem Kampf)
|
||||
var draws := 0 # in diesem Zug gezogene Karten; die n-te kostet n-1 Mana
|
||||
var fatigue := 0 # Züge ohne Karten im Stapel: so viel Leben kostet der nächste
|
||||
|
||||
@@ -147,8 +144,6 @@ class Player extends RefCounted:
|
||||
is_human = h
|
||||
life = l
|
||||
max_life = l
|
||||
for e in Cards.ELEMENTS:
|
||||
played[e] = 0
|
||||
|
||||
func alive() -> bool:
|
||||
return life > 0
|
||||
@@ -178,7 +173,7 @@ class Player extends RefCounted:
|
||||
p.perks = perks.duplicate()
|
||||
p.looks = looks
|
||||
p.turns = turns
|
||||
p.played = played.duplicate()
|
||||
p.cards_played = cards_played
|
||||
p.deck = deck.duplicate()
|
||||
p.draws = draws
|
||||
p.fatigue = fatigue
|
||||
@@ -406,14 +401,12 @@ func play_card(p: Player, card: Cards.Card, face_down: bool = false) -> String:
|
||||
cr.health += 1
|
||||
cr.max_health += 1
|
||||
p.board.append(cr)
|
||||
for e in card.elements:
|
||||
p.played[e] = int(p.played.get(e, 0)) + 1
|
||||
p.cards_played += 1
|
||||
if face_down:
|
||||
say("%s legt ein Wesen verdeckt." % p.name)
|
||||
else:
|
||||
say("%s spielt %s." % [p.name, card.name])
|
||||
emit({"type": "play", "player": players.find(p), "uid": cr.uid, "card": card, "hidden": face_down})
|
||||
apply_auras()
|
||||
cleanup()
|
||||
check_end()
|
||||
return ""
|
||||
@@ -463,12 +456,11 @@ func reveal(p: Player, cr: Creature) -> String:
|
||||
return ""
|
||||
|
||||
|
||||
## Deckt ein Wesen auf; die Elementboni gelten sofort.
|
||||
## Deckt ein Wesen auf; das Elementpaar gilt sofort.
|
||||
func _flip(cr: Creature) -> void:
|
||||
cr.hidden = false
|
||||
say("%s wird aufgedeckt." % cr.name)
|
||||
emit({"type": "reveal", "uid": cr.uid, "card": cr.card})
|
||||
apply_auras()
|
||||
|
||||
|
||||
# --- Verschmelzen ------------------------------------------------------------
|
||||
@@ -523,49 +515,6 @@ func fuse(p: Player, a, b) -> String:
|
||||
return ""
|
||||
|
||||
|
||||
# --- Elementboni ---------------------------------------------------------------
|
||||
|
||||
## +1/+1 je volle ELEMENT_COUNT ausgespielte Karten eines Elements des Spielers; mehrere Elemente
|
||||
## zählen alle, auch Neutral (eigener Zähler, aber kein Paar).
|
||||
static func aura_of(elements: Array, played: Dictionary) -> int:
|
||||
var a := 0
|
||||
for e in elements:
|
||||
a += int(played.get(e, 0)) / ELEMENT_COUNT
|
||||
return a
|
||||
|
||||
|
||||
## Bonus eines offenen Wesens aus den ausgespielten Karten seines Besitzers; verdeckte bekommen nichts.
|
||||
func aura_for(c: Creature) -> int:
|
||||
var o = c.owner()
|
||||
if c.hidden or o == null:
|
||||
return 0
|
||||
var a := aura_of(c.card.elements, o.played)
|
||||
if "element_doppelt" in o.perks:
|
||||
a *= 2
|
||||
return a
|
||||
|
||||
|
||||
## Rechnet die Elementboni neu. Kommt ein Bonus, steigen Leben und Höchstleben; geht er
|
||||
## (beim Verdecken), sinkt nur das Höchstleben, das Leben wird gekappt.
|
||||
func apply_auras() -> void:
|
||||
for p in players:
|
||||
for c in p.board:
|
||||
var a := aura_for(c)
|
||||
if a != c.aura:
|
||||
var delta: int = a - c.aura
|
||||
c.max_health += delta
|
||||
c.health = mini(c.health + maxi(delta, 0), c.max_health)
|
||||
c.aura = a
|
||||
|
||||
|
||||
## Vorschau fürs Ausspielen: der Elementbonus, den die Karte offen gelegt bekäme (sie zählt selbst mit).
|
||||
func forecast(p: Player, card: Cards.Card) -> int:
|
||||
var played := p.played.duplicate()
|
||||
for e in card.elements:
|
||||
played[e] = int(played.get(e, 0)) + 1
|
||||
return aura_of(card.elements, played) * (2 if "element_doppelt" in p.perks else 1)
|
||||
|
||||
|
||||
# --- Kampf -------------------------------------------------------------------
|
||||
|
||||
## Leben lassen sich erst angreifen, wenn der Gegner kein Wesen mehr hat; Wesen mit Spott
|
||||
@@ -582,7 +531,7 @@ func can_attack(cr: Creature, target) -> Array:
|
||||
var tgt_owner = target.owner() if target is Creature else target
|
||||
if not (tgt_owner in enemies_of(cr.owner())):
|
||||
return [false, "Kein gültiges Ziel"]
|
||||
if cr.power() + matchup_bonus(cr, target) <= 0: # 0 Angriff reicht nur mit Elementvorteil
|
||||
if cr.power() + matchup_bonus(cr, target) <= 0: # 0 Angriff macht auch im Vorteil nichts
|
||||
return [false, "%s würde keinen Schaden machen" % cr.name]
|
||||
if not (target is Creature) and not tgt_owner.board.is_empty():
|
||||
return [false, "Erst die Wesen"]
|
||||
@@ -609,17 +558,18 @@ func attack(cr: Creature, target) -> String:
|
||||
return ""
|
||||
|
||||
|
||||
## Elementvorteil beim Zuschlagen und beim Konter: im Vorteil zählt der Stapelbonus des Ziels
|
||||
## nicht (so viel Schaden mehr), im Nachteil der eigene nicht (so viel weniger). Gegen Helden
|
||||
## gibt es keinen.
|
||||
## Elementvorteil beim Zuschlagen und beim Konter, als Verschiebung des Schadens: im Vorteil
|
||||
## doppelt (+ Angriff), im Nachteil halb, aufgerundet (− Angriff / 2). Gegen Helden gibt es keinen;
|
||||
## mit dem Merkmal kein_nachteil entfällt der Nachteil.
|
||||
static func matchup_bonus(source: Creature, target) -> int:
|
||||
if not (target is Creature):
|
||||
return 0
|
||||
var m := Cards.matchup(source.card.elements, target.card.elements)
|
||||
if m > 0:
|
||||
return target.aura
|
||||
return source.power()
|
||||
if m < 0:
|
||||
return -source.aura
|
||||
var o = source.owner()
|
||||
return 0 if o != null and "kein_nachteil" in o.perks else -(source.power() / 2)
|
||||
return 0
|
||||
|
||||
|
||||
@@ -634,24 +584,17 @@ func damage(target, amount: int) -> void:
|
||||
emit({"type": "damage", "target": ref_of(target), "amount": amount})
|
||||
|
||||
|
||||
## Räumt Tote ab; danach gelten die Elementboni neu, was weitere Tote nach sich ziehen kann.
|
||||
## Räumt Tote ab; wer besiegt ist, nimmt seine Wesen mit vom Tisch.
|
||||
func cleanup() -> void:
|
||||
for round in 8:
|
||||
var died := false
|
||||
for p in players:
|
||||
for c in p.board.duplicate():
|
||||
# Wer besiegt ist, nimmt seine Wesen mit vom Tisch.
|
||||
if c.health <= 0 or not p.alive():
|
||||
p.board.erase(c)
|
||||
if c.health > 0:
|
||||
say("%s verschwindet mit %s." % [c.name, p.name])
|
||||
else:
|
||||
say("%s stirbt." % c.name)
|
||||
emit({"type": "death", "target": ref_of(c)})
|
||||
died = true
|
||||
if not died:
|
||||
return
|
||||
apply_auras()
|
||||
for p in players:
|
||||
for c in p.board.duplicate():
|
||||
if c.health <= 0 or not p.alive():
|
||||
p.board.erase(c)
|
||||
if c.health > 0:
|
||||
say("%s verschwindet mit %s." % [c.name, p.name])
|
||||
else:
|
||||
say("%s stirbt." % c.name)
|
||||
emit({"type": "death", "target": ref_of(c)})
|
||||
|
||||
|
||||
## Ende: einer von beiden hat kein Leben mehr.
|
||||
@@ -676,11 +619,11 @@ static func shuffle(arr: Array, r: RandomNumberGenerator) -> void:
|
||||
arr[j] = tmp
|
||||
|
||||
|
||||
## Duell gegen einen zufälligen Gegner. rules: {"human_perks": [...], "opponent_life": int, "looks": {...}, "deck": [ids]}
|
||||
## Duell gegen einen zufälligen Gegner (Tests, Simulation). rules: {"human_perks": [...], "looks": {...}, "deck": [ids]}
|
||||
static func new_battle(r: RandomNumberGenerator = null, rules: Dictionary = {}) -> Game:
|
||||
if r == null:
|
||||
r = make_rng()
|
||||
return new_battle_with(DeckGen.random_opponent(int(rules.get("opponent_life", START_LIFE))), r, false, rules)
|
||||
return new_battle_with(DeckGen.random_opponent(), r, false, rules)
|
||||
|
||||
|
||||
static func new_battle_with(opp: Player, r: RandomNumberGenerator = null, fixed_order: bool = false, rules: Dictionary = {}) -> Game:
|
||||
|
||||
Reference in new issue
Block a user