update
Some checks failed
Tests / headless (push) Has been cancelled
Tests / fenster-schnell (push) Has been cancelled
Tests / fenster-voll (push) Has been cancelled
Tests / balance (push) Has been cancelled

This commit is contained in:
Team3
2026-09-28 19:44:30 +02:00
parent dfcd3cb713
commit f0a8f18de8
37 changed files with 1588 additions and 880 deletions

View File

@@ -9,7 +9,6 @@ const SHOP_SIZE := 5 # so viele Karten liegen im Laden
const REROLL_COST := 1
# Jeder gegen jeden: alle Spieler haben dieselben Regeln und dasselbe Startleben.
const START_LIFE := 30
const MAX_OPPONENTS := 5
# Merkmale der Kampagnen-Bosse (Player.perks).
const PERKS := {
@@ -18,7 +17,7 @@ const PERKS := {
"waechter_leben": "Wächter kommen mit +2 Leben",
"wesen_leben": "Wesen kommen mit +1 Leben",
"extra_mana": "Bekommt jeden Zug 1 Mana mehr",
"leben_35": "Beginnt mit 35 Leben",
"leben_35": "Beginnt mit 5 Leben mehr",
"schild_wesen": "Wesen kommen mit Schild",
"alle_flink": "Alle Wesen sind flink",
"start_energie": "Beginnt mit 3 Mana",
@@ -151,8 +150,8 @@ class Player extends RefCounted:
var human: Player # der (erste) menschliche Spieler
var humans: Array = [] # alle menschlichen Spieler (im Netz mehrere)
var opponents: Array = [] # alle außer dem ersten Menschen
var humans: Array = [] # alle menschlichen Spieler (im Netz zwei)
var opponent: Player # der andere am Tisch
var players: Array = [] # in Zugreihenfolge
var winner_player: Player = null
var rng: RandomNumberGenerator
@@ -167,10 +166,10 @@ var field_element := "" # gemeinsames Feld aller am Tisch, "" = neutral
var field_charge := {} # Element -> gespielte Karten seit dem letzten Wechsel
func _init(h: Player, opps: Array, r: RandomNumberGenerator = null) -> void:
func _init(h: Player, o: Player, r: RandomNumberGenerator = null) -> void:
human = h
opponents = opps
players = [h] + opps
opponent = o
players = [h, o]
humans = players.filter(func(p): return p.is_human)
if r == null:
r = make_rng()
@@ -199,11 +198,15 @@ func ref_of(target) -> Dictionary:
return {"kind": "player", "index": players.find(target)}
## Jeder gegen jeden: alle anderen lebenden Spieler sind Gegner.
## Der Gegner von p, solange er lebt (als Liste, damit Effekte einheitlich bleiben).
func enemies_of(p: Player) -> Array:
return players.filter(func(o): return o != p and o.alive())
func enemy_of(p: Player) -> Player:
return opponent if p == human else human
func find_creature(uid: int) -> Creature:
for p in players:
for c in p.board:
@@ -218,7 +221,7 @@ func clone() -> Game:
for p in players:
copies.append(p.clone())
var idx := players.find(human)
var g := Game.new(copies[idx], copies.slice(0, idx) + copies.slice(idx + 1), RandomNumberGenerator.new())
var g := Game.new(copies[idx], copies[1 - idx], RandomNumberGenerator.new())
g.players = copies # gleiche Zugreihenfolge wie das Original
g.turn = turn
g.current_idx = current_idx
@@ -483,6 +486,26 @@ static func resonance_for(partners: int) -> int:
return DeckGen.RESONANCE_STEPS[mini(partners, DeckGen.RESONANCE_STEPS.size() - 1)]
## Vorschau fürs Ausspielen: Angriffsbonus aus Resonanz und Feld sowie die Feldkombo,
## die die Karte mit dem ersten eigenen Wesen aus anderem Element auslösen würde.
func forecast(p: Player, card: Cards.Card) -> Dictionary:
var partners := 0
var combo := ""
var partner: Creature = null
for o in p.board:
if o.card.elements.any(func(e): return e in card.elements):
partners += 1
if combo == "":
for e1 in card.elements:
for e2 in o.card.elements:
if e1 != e2 and combo == "":
combo = Cards.combo_name([e1, e2])
partner = o
var resonance := resonance_for(partners) * (2 if "resonanz_doppelt" in p.perks else 1)
var field := 1 if field_element != "" and field_element in card.elements else 0
return {"resonance": resonance, "field": field, "combo": combo, "partner": partner}
## Resonanz: abnehmender Angriffsbonus je Partner mit gleichem Element (siehe resonance_for).
func update_resonance() -> void:
for p in players:
@@ -642,20 +665,19 @@ func cleanup() -> void:
update_resonance()
## Ende: nur noch ein Spieler übrig, oder kein Mensch mehr am Tisch.
## Ende: einer von beiden hat kein Leben mehr.
func check_end() -> void:
if winner != "":
return
var alive := players.filter(func(p): return p.alive())
var humans_alive := alive.filter(func(p): return p.is_human)
if humans_alive.is_empty():
winner = "opponents"
winner_player = alive[0] if alive.size() == 1 else null
say("Du wurdest besiegt." if humans.size() == 1 else "Alle Spieler sind besiegt, die KI gewinnt.")
elif alive.size() == 1:
winner = "human"
winner_player = alive[0]
say("Alle Gegner sind besiegt!" if humans.size() == 1 else "%s gewinnt!" % winner_player.name)
if alive.size() != 1:
return
winner_player = alive[0]
winner = "human" if winner_player.is_human else "opponents"
if humans.size() == 1:
say("Der Gegner ist besiegt!" if winner == "human" else "Du wurdest besiegt.")
else:
say("%s gewinnt!" % winner_player.name)
# --- Zustand für das Netzwerk (siehe NetState) ------------------------------------
@@ -678,28 +700,23 @@ static func shuffle(arr: Array, r: RandomNumberGenerator) -> void:
arr[j] = tmp
## rules: {"human_perks": [...], "opponent_life": int, "looks": {...}}
static func new_battle(n_opp: int, r: RandomNumberGenerator = null, rules: Dictionary = {}) -> Game:
assert(n_opp >= 1 and n_opp <= MAX_OPPONENTS, "1 bis 5 Gegner")
## Duell gegen einen zufälligen Gegner. rules: {"human_perks": [...], "opponent_life": int, "looks": {...}}
static func new_battle(r: RandomNumberGenerator = null, rules: Dictionary = {}) -> Game:
if r == null:
r = make_rng()
var life: int = int(rules.get("opponent_life", START_LIFE))
var opps := []
for i in n_opp:
opps.append(DeckGen.random_opponent(i + 1, life))
return new_battle_with(opps, r, false, rules)
return new_battle_with(DeckGen.random_opponent(int(rules.get("opponent_life", START_LIFE))), r, false, rules)
static func new_battle_with(opps: Array, r: RandomNumberGenerator = null, fixed_order: bool = false, rules: Dictionary = {}) -> Game:
static func new_battle_with(opp: Player, r: RandomNumberGenerator = null, fixed_order: bool = false, rules: Dictionary = {}) -> Game:
if r == null:
r = make_rng()
var human := Player.new("Spieler", true, START_LIFE)
human.perks = Array(rules.get("human_perks", []))
human.looks = rules.get("looks", {})
var g := Game.new(human, opps, r)
var g := Game.new(human, opp, r)
for p in g.players:
if "leben_35" in p.perks and p.life == START_LIFE:
p.life = 35
p.max_life = 35
if "leben_35" in p.perks:
p.life += 5
p.max_life += 5
g.start(fixed_order)
return g