update
This commit is contained in:
187
scripts/game.gd
187
scripts/game.gd
@@ -4,6 +4,8 @@ extends Node3D
|
||||
const START_DICE := 5
|
||||
const BID_TIMEOUT := 30.0
|
||||
const REVEAL_PAUSE := 5.0 ## Anzeigedauer des Rundenergebnisses
|
||||
const WURF_ZEIT := 1.1 ## wie lange die geworfene Münze zu sehen ist,
|
||||
## bevor ihre Wirkung einsetzt
|
||||
|
||||
var table: GameTable
|
||||
var hud: CanvasLayer
|
||||
@@ -19,12 +21,13 @@ var bid: Dictionary = {}
|
||||
var turn := 0
|
||||
var starter := 0
|
||||
var fogged: Dictionary = {} ## seat -> Array[int] verdeckter Würfel
|
||||
var peeked: Dictionary = {} ## viewer -> {target_seat: [idx, value]}
|
||||
var peeked: Dictionary = {} ## viewer -> {target_seat: Array[[idx, value]]}
|
||||
var min_raise: Dictionary = {} ## seat -> int
|
||||
var must_bid: Dictionary = {} ## seat -> bool
|
||||
var hand: Dictionary = {} ## seat -> Array[String], Karten auf der Hand
|
||||
var letzte_karte: Dictionary = {} ## seat -> zuletzt gespielte Karte, nur Anzeige
|
||||
var _karte_laeuft := false ## eine Karte wird gerade aufgelöst
|
||||
var hand: Dictionary = {} ## seat -> Array[String], Münzen beim Spieler
|
||||
var mitte: Dictionary = {} ## seat -> zuletzt in die Mitte gelegte Münze
|
||||
var _muenze_laeuft := false ## eine Münze wird gerade aufgelöst
|
||||
var _wurf_nr := 0 ## zählt die Würfe, damit das HUD neue erkennt
|
||||
var last_bids: Dictionary = {} ## seat -> letzte Ansage dieser Runde
|
||||
var log_lines: Array = []
|
||||
var phase := "lobby"
|
||||
@@ -78,7 +81,9 @@ func setup(cfg: Dictionary) -> void:
|
||||
hud.pause_changed.connect(_on_pause_changed)
|
||||
|
||||
for i in seats.size():
|
||||
hand[i] = []
|
||||
# Die Startmünze zieht nur der Host. Beim Client kommt sie mit dem
|
||||
# ersten Zustand, sonst läge dort kurz eine andere.
|
||||
hand[i] = [BoosterDB.zieh(rng)] if multiplayer.is_server() else []
|
||||
table.set_dice_count(i, START_DICE)
|
||||
table.set_dice_visible(i, false)
|
||||
table.set_cup_down(i, i != local_seat)
|
||||
@@ -113,6 +118,7 @@ func _refresh_labels() -> void:
|
||||
elif phase == "bid" and i == turn:
|
||||
col = Color(1.0, 0.85, 0.3)
|
||||
table.set_label(i, txt, col)
|
||||
table.set_active_seat(turn if phase == "bid" else -1)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- Host-Ablauf
|
||||
@@ -199,7 +205,8 @@ func _phase_roll() -> void:
|
||||
peeked.clear()
|
||||
min_raise.clear()
|
||||
must_bid.clear()
|
||||
letzte_karte.clear()
|
||||
mitte.clear()
|
||||
_wurf_nr = 0
|
||||
banner = ""
|
||||
_log("--- Neue Runde ---")
|
||||
|
||||
@@ -254,57 +261,81 @@ func _await_settled(list: Array, timeout := 7.0) -> void:
|
||||
return
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- Karten
|
||||
# ---------------------------------------------------------------- Münzen
|
||||
|
||||
## Wer einen Würfel abgibt, zieht eine Karte. Das ist der einzige Nachschub.
|
||||
func _zieh_karte(seat: int) -> void:
|
||||
var karten: Array = hand.get(seat, [])
|
||||
if karten.size() >= BoosterDB.HANDLIMIT:
|
||||
_log("%s hat die Hand voll und zieht nicht nach." % seats[seat].name)
|
||||
## Wer einen Würfel abgibt, bekommt eine Münze. Das ist der einzige Nachschub.
|
||||
func _zieh_muenze(seat: int) -> void:
|
||||
var muenzen: Array = hand.get(seat, [])
|
||||
if muenzen.size() >= BoosterDB.HANDLIMIT:
|
||||
_log("%s hat schon drei Münzen und bekommt keine." % seats[seat].name)
|
||||
return
|
||||
var id := BoosterDB.zieh(rng)
|
||||
karten.append(id)
|
||||
hand[seat] = karten
|
||||
_log("%s zieht %s." % [seats[seat].name, BoosterDB.get_booster(id).name])
|
||||
muenzen.append(id)
|
||||
hand[seat] = muenzen
|
||||
_log("%s bekommt %s." % [seats[seat].name, BoosterDB.get_booster(id).name])
|
||||
|
||||
|
||||
## Ausspielen heißt: Münze in die Mitte legen und werfen. Wappen verstärkt,
|
||||
## Zahl lässt es bei der Grundwirkung. Beides hilft - verschenkt ist nie ein Zug.
|
||||
func _do_play(seat: int, id: String, target: int) -> void:
|
||||
if seat < 0 or phase != "bid" or not seats[seat].alive:
|
||||
return
|
||||
# Zwei Karten gleichzeitig würden sich beim Würfeln in die Quere kommen.
|
||||
if _karte_laeuft:
|
||||
# Zwei Münzen gleichzeitig würden sich beim Würfeln in die Quere kommen.
|
||||
if _muenze_laeuft:
|
||||
return
|
||||
var karten: Array = hand.get(seat, [])
|
||||
if not karten.has(id):
|
||||
var muenzen: Array = hand.get(seat, [])
|
||||
if not muenzen.has(id):
|
||||
return
|
||||
karten.erase(id)
|
||||
hand[seat] = karten
|
||||
letzte_karte[seat] = id
|
||||
_log("%s spielt %s." % [seats[seat].name, BoosterDB.get_booster(id).name])
|
||||
_karte_laeuft = true
|
||||
await _wirkung(seat, id, target)
|
||||
_karte_laeuft = false
|
||||
muenzen.erase(id)
|
||||
hand[seat] = muenzen
|
||||
_muenze_laeuft = true
|
||||
|
||||
var stark := rng.randf() < BoosterDB.WAPPEN_CHANCE
|
||||
_wurf_nr += 1
|
||||
mitte[seat] = {"id": id, "stark": stark, "nr": _wurf_nr}
|
||||
_log("%s wirft %s - %s." % [seats[seat].name,
|
||||
BoosterDB.get_booster(id).name, "Wappen" if stark else "Zahl"])
|
||||
|
||||
# Erst die Münze zeigen, dann wirken lassen. Andersherum sähe man die
|
||||
# Folge vor der Ursache.
|
||||
_push_state()
|
||||
await _sleep(WURF_ZEIT)
|
||||
if _alive():
|
||||
await _wirkung(seat, id, target, stark)
|
||||
_muenze_laeuft = false
|
||||
if not _alive():
|
||||
return
|
||||
# Der Widerruf hat keine eigene Verstärkung; bei Wappen bleibt er liegen.
|
||||
if stark and BoosterDB.keeps_on_wappen(id):
|
||||
var zurueck: Array = hand.get(seat, [])
|
||||
if zurueck.size() < BoosterDB.HANDLIMIT:
|
||||
zurueck.append(id)
|
||||
hand[seat] = zurueck
|
||||
_snapshot_all()
|
||||
_push_state()
|
||||
|
||||
|
||||
func _wirkung(seat: int, id: String, target: int) -> void:
|
||||
func _wirkung(seat: int, id: String, target: int, stark: bool) -> void:
|
||||
var vals: Array = host_values[seat]
|
||||
var ziel: int = clampi(target, 0, maxi(0, vals.size() - 1))
|
||||
match id:
|
||||
"reroll_all":
|
||||
await _neu_werfen(seat, range(vals.size()))
|
||||
if stark and not host_values[seat].is_empty():
|
||||
_set_die(seat, rng.randi_range(0, host_values[seat].size() - 1), 6)
|
||||
"reroll_one":
|
||||
await _neu_werfen(seat, [ziel])
|
||||
await _neu_werfen(seat, _ziel_und_zufall(seat, ziel, stark))
|
||||
"set_six":
|
||||
_set_die(seat, ziel, 6)
|
||||
for k: int in _ziel_und_zufall(seat, ziel, stark):
|
||||
_set_die(seat, k, 6)
|
||||
"gain_die":
|
||||
seats[seat].count += 1
|
||||
table.set_dice_count(seat, seats[seat].count)
|
||||
host_values[seat].append(1)
|
||||
await _neu_werfen(seat, [host_values[seat].size() - 1])
|
||||
var neu: int = host_values[seat].size() - 1
|
||||
await _neu_werfen(seat, [neu])
|
||||
if stark:
|
||||
_set_die(seat, neu, 6)
|
||||
"cancel":
|
||||
fogged.erase(seat)
|
||||
min_raise.erase(seat)
|
||||
@@ -312,33 +343,69 @@ func _wirkung(seat: int, id: String, target: int) -> void:
|
||||
"peek":
|
||||
var info := {}
|
||||
for o in _alive_seats():
|
||||
if o != seat and host_values[o].size() > 0:
|
||||
var k := rng.randi_range(0, host_values[o].size() - 1)
|
||||
info[o] = [k, host_values[o][k]]
|
||||
if o != seat:
|
||||
info[o] = _zufalls_paare(o, 2 if stark else 1)
|
||||
peeked[seat] = info
|
||||
"bid_up":
|
||||
if not bid.is_empty():
|
||||
bid.count = int(bid.count) + 1
|
||||
bid.count = int(bid.count) + (2 if stark else 1)
|
||||
last_bids[int(bid.seat)] = {"count": bid.count, "face": bid.face}
|
||||
"enemy_lower":
|
||||
var minus := 2 if stark else 1
|
||||
for o in _alive_seats():
|
||||
if o != seat and host_values[o].size() > 0:
|
||||
var k := rng.randi_range(0, host_values[o].size() - 1)
|
||||
_set_die(o, k, maxi(1, host_values[o][k] - 1))
|
||||
if o != seat:
|
||||
for k: int in _zufalls_idx(o, 1):
|
||||
_set_die(o, k, maxi(1, int(host_values[o][k]) - minus))
|
||||
"fog":
|
||||
for o in _alive_seats():
|
||||
if o != seat and host_values[o].size() > 0:
|
||||
var k := rng.randi_range(0, host_values[o].size() - 1)
|
||||
var liste: Array = fogged.get(o, [])
|
||||
if o == seat:
|
||||
continue
|
||||
var liste: Array = fogged.get(o, [])
|
||||
for k: int in _zufalls_idx(o, 2 if stark else 1):
|
||||
if not liste.has(k):
|
||||
liste.append(k)
|
||||
fogged[o] = liste
|
||||
fogged[o] = liste
|
||||
"raise_min":
|
||||
for o in _alive_seats():
|
||||
if o != seat:
|
||||
min_raise[o] = 2
|
||||
min_raise[o] = 3 if stark else 2
|
||||
"gag":
|
||||
must_bid[turn] = true
|
||||
if stark:
|
||||
for o in _alive_seats():
|
||||
if o != seat:
|
||||
must_bid[o] = true
|
||||
else:
|
||||
must_bid[turn] = true
|
||||
|
||||
|
||||
## Bis zu `n` verschiedene Würfelplätze eines Sitzes, zufällig gezogen.
|
||||
func _zufalls_idx(seat: int, n: int) -> Array:
|
||||
var frei: Array = range(host_values[seat].size())
|
||||
var out: Array = []
|
||||
while out.size() < n and not frei.is_empty():
|
||||
out.append(frei.pop_at(rng.randi_range(0, frei.size() - 1)))
|
||||
return out
|
||||
|
||||
|
||||
func _zufalls_paare(seat: int, n: int) -> Array:
|
||||
var out: Array = []
|
||||
for k: int in _zufalls_idx(seat, n):
|
||||
out.append([k, host_values[seat][k]])
|
||||
return out
|
||||
|
||||
|
||||
## Der selbst gewählte Würfel, bei Wappen zusätzlich ein anderer zufälliger.
|
||||
func _ziel_und_zufall(seat: int, ziel: int, stark: bool) -> Array:
|
||||
var out: Array = [ziel]
|
||||
if not stark:
|
||||
return out
|
||||
var frei: Array = []
|
||||
for k in host_values[seat].size():
|
||||
if k != ziel:
|
||||
frei.append(k)
|
||||
if not frei.is_empty():
|
||||
out.append(frei[rng.randi_range(0, frei.size() - 1)])
|
||||
return out
|
||||
|
||||
|
||||
## Würfel neu werfen und die Augenzahl danach übernehmen.
|
||||
@@ -386,8 +453,8 @@ func _phase_bid() -> Dictionary:
|
||||
_push_state()
|
||||
if seats[turn].ai:
|
||||
await _sleep(1.1)
|
||||
# Bots spielen ihre Karten, bevor sie ansagen.
|
||||
await _bot_karte(turn)
|
||||
# Bots werfen ihre Münzen, bevor sie ansagen.
|
||||
await _bot_muenze(turn)
|
||||
if not _alive():
|
||||
return {}
|
||||
var a := DiceAI.choose_action(host_values[turn], _total_dice(), bid,
|
||||
@@ -413,16 +480,16 @@ func _phase_bid() -> Dictionary:
|
||||
return {}
|
||||
|
||||
|
||||
## Ein Bot legt manchmal eine Karte, statt sie bis zum Ausscheiden zu horten.
|
||||
func _bot_karte(seat: int) -> void:
|
||||
var karten: Array = hand.get(seat, [])
|
||||
if karten.is_empty():
|
||||
## Ein Bot wirft manchmal eine Münze, statt sie bis zum Ausscheiden zu horten.
|
||||
func _bot_muenze(seat: int) -> void:
|
||||
var muenzen: Array = hand.get(seat, [])
|
||||
if muenzen.is_empty():
|
||||
return
|
||||
# Volle Hand: raus damit. Sonst nur ab und zu, sonst ist sofort alles weg.
|
||||
var lust := 1.0 if karten.size() >= BoosterDB.HANDLIMIT else 0.35
|
||||
# Voller Vorrat: raus damit. Sonst nur ab und zu, sonst ist alles sofort weg.
|
||||
var lust := 1.0 if muenzen.size() >= BoosterDB.HANDLIMIT else 0.35
|
||||
if rng.randf() > lust:
|
||||
return
|
||||
var id: String = karten[rng.randi_range(0, karten.size() - 1)]
|
||||
var id: String = muenzen[rng.randi_range(0, muenzen.size() - 1)]
|
||||
if id == "bid_up" and bid.is_empty():
|
||||
return
|
||||
await _do_play(seat, id, DiceAI.target_for(id, host_values[seat]))
|
||||
@@ -510,17 +577,20 @@ func _phase_reveal(result: Dictionary) -> void:
|
||||
return
|
||||
|
||||
seats[loser].count -= 1
|
||||
_zieh_karte(loser)
|
||||
if seats[loser].count <= 0:
|
||||
seats[loser].alive = false
|
||||
seats[loser].count = 0
|
||||
host_values[loser] = []
|
||||
table.set_dice_count(loser, 0)
|
||||
_log("%s scheidet aus." % seats[loser].name)
|
||||
else:
|
||||
_zieh_muenze(loser)
|
||||
starter = loser if seats[loser].alive else _next_alive(loser)
|
||||
# Nur wer noch spielt, bekommt den Becher wieder. Auf einer leeren Zone
|
||||
# sähe er aus, als lägen dort noch Würfel.
|
||||
for i in seats.size():
|
||||
table.set_dice_visible(i, false)
|
||||
table.set_cup_down(i, true)
|
||||
table.set_cup_down(i, seats[i].alive)
|
||||
banner = ""
|
||||
_push_state()
|
||||
|
||||
@@ -645,14 +715,14 @@ func _push_state() -> void:
|
||||
_state.rpc_id(peer, _state_for(i))
|
||||
|
||||
|
||||
## Wer hat diese Runde schon eine Karte gespielt? Beim Würfeln steht noch
|
||||
## nichts an, danach ist es offen.
|
||||
## Welche Münze liegt von wem in der Mitte? Beim Würfeln steht noch nichts
|
||||
## an, danach ist es offen.
|
||||
func _round_boosters() -> Array:
|
||||
var out: Array = []
|
||||
if phase == "roll":
|
||||
return out
|
||||
for i in seats.size():
|
||||
out.append(String(letzte_karte.get(i, "")))
|
||||
out.append(mitte.get(i, {}))
|
||||
return out
|
||||
|
||||
|
||||
@@ -688,7 +758,6 @@ func _state_for(seat: int) -> Dictionary:
|
||||
"boosts": _round_boosters(),
|
||||
"actual": reveal_total if phase == "reveal" else 0,
|
||||
"bids": _seat_bids(),
|
||||
"boost": String(letzte_karte.get(seat, "")),
|
||||
"hand": (hand.get(seat, []) as Array).duplicate(),
|
||||
}
|
||||
|
||||
@@ -719,7 +788,7 @@ func _apply_state(st: Dictionary) -> void:
|
||||
for i in seats.size():
|
||||
if i != local_seat:
|
||||
table.set_dice_visible(i, false)
|
||||
table.set_cup_down(i, true)
|
||||
table.set_cup_down(i, seats[i].alive)
|
||||
table.set_dice_visible(local_seat, true)
|
||||
_refresh_labels()
|
||||
_apply_fog_to_table()
|
||||
|
||||
Reference in New Issue
Block a user