update
This commit is contained in:
@@ -60,7 +60,10 @@ var flash_until := {} # Spielerindex -> Zeitpunkt (ms)
|
||||
var hero_layer: Control
|
||||
var tile_layer: Control
|
||||
var hand_layer: Control
|
||||
var deck: Deck
|
||||
var offer_views: Array = [] # die offenen Karten des Stapels (CardView)
|
||||
var offer_layer: Control
|
||||
var deck_label: Label
|
||||
var draw_enabled := false
|
||||
var log_overlay: Control
|
||||
var b_log: Button
|
||||
var status_panel: Panel
|
||||
@@ -70,14 +73,12 @@ var preview_holder: Control
|
||||
var name_label: Label
|
||||
var life_label: Label
|
||||
var energy_label: Label
|
||||
var income_label: Label
|
||||
var status_label: Label
|
||||
var msg_label: Label
|
||||
var b_end: Button
|
||||
var b_giveup: Button
|
||||
var element_view: ElementView
|
||||
var element_view: ElementView # eigene Elementleiste
|
||||
var opp_element_view: ElementView # die des Gegners darüber
|
||||
var info_label: Label # Vorschau: Elementbonus, Merkmale
|
||||
var board_panel: Panel # eigenes Brett
|
||||
var menu_layer: Control # Kartenmenü (Rechtsklick)
|
||||
var menu_target = null # Karte oder Wesen, für das das Menü offen ist
|
||||
var arrivals := {} # uid -> Rect2: Wesen, das gerade aus der Hand aufs Brett fliegt
|
||||
@@ -181,43 +182,43 @@ func _build_static() -> void:
|
||||
b_giveup.add_theme_font_size_override("font_size", 13)
|
||||
b_log = Style.button(self, "Verlauf", L.B_LOG, toggle_log)
|
||||
b_log.add_theme_font_size_override("font_size", 13)
|
||||
b_end = Style.button(self, "Zug beenden", L.B_END, end_turn, Style.PRIMARY)
|
||||
b_end.add_theme_font_size_override("font_size", 15)
|
||||
b_skip = Style.button(self, "Überspringen", L.B_END, func(): skip_ai = true)
|
||||
b_skip.add_theme_font_size_override("font_size", 15)
|
||||
b_skip.visible = false
|
||||
# Mitte: Elementzähler, Meldung, eigenes Brett mit fünf Plätzen, eigene Leiste links daneben
|
||||
Style.panel(self, L.FIELD_OPP, Style.darker(Style.PANEL, 0.9), 8)
|
||||
opp_element_view = ElementView.new(L.FIELD_OPP)
|
||||
add_child(opp_element_view)
|
||||
Style.panel(self, L.FIELD, Style.darker(Style.PANEL, 0.9), 8)
|
||||
element_view = ElementView.new(L.FIELD)
|
||||
add_child(element_view)
|
||||
msg_label = Style.label(self, "", 15, Style.ORANGE, L.MSG, "center", true)
|
||||
board_panel = Style.panel(self, L.OWN_PANEL, Style.darker(Style.PANEL, 0.8), 10)
|
||||
msg_label = Style.label(self, "", 14, Style.ORANGE, L.MSG, "center", true)
|
||||
msg_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
var slots := Control.new()
|
||||
slots.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
slots.draw.connect(func():
|
||||
for j in Game.MAX_BOARD:
|
||||
Style.border(slots, L.own_slot(j), Color(1, 1, 1, 0.08), 2, 8)
|
||||
Style.border(slots, L.own_slot(j), Color(1, 1, 1, 0.05), 2, 8)
|
||||
Style.border(slots, L.opp_slot(j), Color(1, 1, 1, 0.05), 2, 8))
|
||||
add_child(slots)
|
||||
status_panel = Style.panel(self, L.STATUS)
|
||||
var sx: float = L.STATUS.position.x
|
||||
var sy: float = L.STATUS.position.y
|
||||
name_label = Style.label(self, "", 18, Style.TEXT, Rect2(sx + 12, sy + 8, 190, 26), "left", true)
|
||||
var sw: float = L.STATUS.size.x
|
||||
name_label = Style.label(self, "", 18, Style.TEXT, Rect2(sx + 8, sy + 6, sw - 16, 26), "center", true)
|
||||
name_label.clip_text = true
|
||||
life_label = Style.label(self, "", 26, Style.RED, Rect2(sx + 12, sy + 40, 190, 34), "left", true)
|
||||
energy_label = Style.label(self, "", 26, Style.BLUE, Rect2(sx + 12, sy + 78, 100, 34), "left", true)
|
||||
income_label = Style.label(self, "", 15, Style.MUTED, Rect2(sx + 100, sy + 88, 100, 22), "left")
|
||||
status_label = Style.label(self, "", 13, Style.MUTED, Rect2(sx + 12, sy + 124, 190, 22), "left")
|
||||
life_label = Style.label(self, "", 30, Style.RED, Rect2(sx + 8, sy + 42, sw / 2 - 8, 40), "left", true)
|
||||
energy_label = Style.label(self, "", 30, Style.BLUE, Rect2(sx + sw / 2, sy + 42, sw / 2 - 8, 40), "right", true)
|
||||
b_undo = Style.button(self, "Rückgängig", L.B_UNDO, undo)
|
||||
b_undo.add_theme_font_size_override("font_size", 15)
|
||||
b_undo.add_theme_font_size_override("font_size", 13)
|
||||
b_undo.visible = false
|
||||
# unten: Zieh-Stapel links, Hand in der Mitte, Zug beenden rechts
|
||||
deck = Deck.new(L.DECK)
|
||||
deck.clicked.connect(func(): on_click("deck", null))
|
||||
add_child(deck)
|
||||
b_end = Style.button(self, "Zug beenden", L.B_END, end_turn, Style.PRIMARY)
|
||||
b_end.add_theme_font_size_override("font_size", 20)
|
||||
b_skip = Style.button(self, "Gegnerzüge überspringen", L.B_END, func(): skip_ai = true)
|
||||
b_skip.add_theme_font_size_override("font_size", 14)
|
||||
b_skip.visible = false
|
||||
deck_label = Style.label(self, "", 13, Style.MUTED, L.DECK_LABEL, "center")
|
||||
wait_label = Style.label(self, "Warte auf den Host …", 22, Style.MUTED, Rect2(0, 300, 1280, 40), "center")
|
||||
wait_label.visible = false
|
||||
hero_layer = _layer()
|
||||
offer_layer = _layer()
|
||||
tile_layer = _layer()
|
||||
hand_layer = _layer()
|
||||
preview_holder = _layer()
|
||||
@@ -317,7 +318,7 @@ func target_center(ref: Dictionary) -> Vector2:
|
||||
return Vector2(-1, -1)
|
||||
|
||||
|
||||
## Kasten des Gegners oben in der Mitte; ein Klick zielt auf den Helden.
|
||||
## Kasten des Gegners links oben, gebaut wie der eigene; ein Klick zielt auf den Helden.
|
||||
func build_hero_boxes() -> void:
|
||||
Style.clear(hero_layer)
|
||||
hero_panels = {}
|
||||
@@ -335,11 +336,11 @@ func build_hero_boxes() -> void:
|
||||
var title: String = ("%s · %s" % [o.name, o.title]) if o.title != "" else o.name
|
||||
if not o.perks.is_empty():
|
||||
title += " ★"
|
||||
labels["name"] = Style.label(hero_layer, title, 18, Style.TEXT, Rect2(box.position.x + 8, box.position.y + 4, box.size.x - 16, 22), "left", true)
|
||||
labels["name"] = Style.label(hero_layer, title, 18, Style.TEXT, Rect2(box.position.x + 8, box.position.y + 6, box.size.x - 16, 26), "center", true)
|
||||
labels.name.clip_text = true
|
||||
labels.name.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
labels["life"] = Style.label(hero_layer, "", 20, Style.RED, Rect2(box.position.x + 8, box.position.y + 30, 120, 24), "left", true)
|
||||
labels["stats"] = Style.label(hero_layer, "", 14, Style.MUTED, Rect2(box.position.x + 80, box.position.y + 34, box.size.x - 88, 20), "right")
|
||||
labels["life"] = Style.label(hero_layer, "", 30, Style.RED, Rect2(box.position.x + 8, box.position.y + 42, box.size.x / 2 - 8, 40), "left", true)
|
||||
labels["stats"] = Style.label(hero_layer, "", 30, Style.BLUE, Rect2(box.position.x + box.size.x / 2, box.position.y + 42, box.size.x / 2 - 8, 40), "right", true)
|
||||
hero_labels[o] = labels
|
||||
|
||||
|
||||
@@ -366,8 +367,10 @@ func _input(event: InputEvent) -> void:
|
||||
open_menu(under.card, Rect2(under.position, under.size))
|
||||
elif under is CreatureView and under in own_views and my_turn():
|
||||
open_menu(under.creature, Rect2(under.slot, under.size))
|
||||
else:
|
||||
elif selected != null or fuse_first != null or any_overlay() or not my_turn():
|
||||
cancel_all()
|
||||
else:
|
||||
show_menu(tr("Zug %d") % game.turn, [[tr("Zug beenden"), end_turn, true]], Rect2(event.position, Vector2.ZERO))
|
||||
get_viewport().set_input_as_handled() # sonst schließt derselbe Klick das Menü gleich wieder
|
||||
return
|
||||
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_MIDDLE:
|
||||
@@ -677,6 +680,7 @@ func open_menu(target, anchor: Rect2, full: bool = false) -> void:
|
||||
entries.append([tr("Verdeckt legen ◆ %d") % card.cost, func():
|
||||
play(card, true)
|
||||
after_action(), ok[0]])
|
||||
entries.append([tr("Abwerfen +%d ◆") % Game.DISCARD_GAIN, func(): do_discard(card), game.can_discard(perspective, card)[0]])
|
||||
elif not is_card and target.hidden:
|
||||
entries.append([tr("Aufdecken"), func(): do_reveal(target), game.can_reveal(perspective, target)[0]])
|
||||
if full:
|
||||
@@ -688,6 +692,12 @@ func open_menu(target, anchor: Rect2, full: bool = false) -> void:
|
||||
if entries.is_empty(): # offenes eigenes Wesen per Maus: nichts zu wählen
|
||||
close_menu()
|
||||
return
|
||||
show_menu(card.title(), entries, anchor)
|
||||
|
||||
|
||||
## Kleines Menü neben anchor: Überschrift und Knöpfe [Text, Callable, aktiv].
|
||||
func show_menu(title: String, entries: Array, anchor: Rect2) -> void:
|
||||
Style.clear(menu_layer)
|
||||
var w := 230.0
|
||||
var h := 18.0 + entries.size() * 44 + 34
|
||||
var x := clampf(anchor.end.x + 8, 10, 1270 - w)
|
||||
@@ -696,7 +706,7 @@ func open_menu(target, anchor: Rect2, full: bool = false) -> void:
|
||||
var y := clampf(anchor.position.y, 10, 790 - h)
|
||||
var box := Rect2(x, y, w, h)
|
||||
Style.panel(menu_layer, box, Style.PANEL, 10, Style.GOLD, 2)
|
||||
Style.label(menu_layer, card.title(), 15, Style.GOLD, Rect2(box.position.x + 12, box.position.y + 8, w - 24, 22), "left", true)
|
||||
Style.label(menu_layer, title, 15, Style.GOLD, Rect2(box.position.x + 12, box.position.y + 8, w - 24, 22), "left", true)
|
||||
var by := box.position.y + 36
|
||||
for e in entries:
|
||||
var act: Callable = e[1]
|
||||
@@ -716,13 +726,25 @@ func close_menu() -> void:
|
||||
menu_target = null
|
||||
|
||||
|
||||
## Ziehen: kostet Mana, eine Karte kommt auf die Hand.
|
||||
func do_draw() -> void:
|
||||
## Abwerfen: eine Handkarte gegen Mana loswerden.
|
||||
func do_discard(card: Cards.Card) -> void:
|
||||
clear_undo()
|
||||
if is_client():
|
||||
net.send_action({"type": "draw"})
|
||||
net.send_action({"type": "discard", "hand": perspective.hand.find(card)})
|
||||
return
|
||||
var err := game.draw(perspective)
|
||||
var err := game.discard(perspective, card)
|
||||
if err != "":
|
||||
say(tr(err))
|
||||
after_action()
|
||||
|
||||
|
||||
## Ziehen: kostet Mana, eine Karte kommt auf die Hand.
|
||||
func do_draw(index: int = 0) -> void:
|
||||
clear_undo()
|
||||
if is_client():
|
||||
net.send_action({"type": "draw", "index": index})
|
||||
return
|
||||
var err := game.draw(perspective, index)
|
||||
if err != "":
|
||||
say(tr(err))
|
||||
else:
|
||||
@@ -811,7 +833,7 @@ func on_click(kind: String, obj) -> void:
|
||||
return
|
||||
match kind:
|
||||
"deck":
|
||||
do_draw()
|
||||
do_draw(int(obj) if obj != null else 0)
|
||||
"hand":
|
||||
var ok := g.can_play(perspective, obj)
|
||||
if not ok[0]:
|
||||
@@ -881,7 +903,7 @@ func show_hand_preview(card: Cards.Card, entered: bool, anchor: Rect2 = Rect2())
|
||||
var a := game.forecast(perspective, card)
|
||||
var info := ""
|
||||
if a > 0:
|
||||
info = tr("Elementbonus +%d/+%d") % [a, a]
|
||||
info = tr("Elementbonus +%d") % a
|
||||
show_preview(card, entered, perspective.looks, info, anchor)
|
||||
|
||||
|
||||
@@ -889,7 +911,7 @@ func show_hand_preview(card: Cards.Card, entered: bool, anchor: Rect2 = Rect2())
|
||||
func creature_info(cr: Game.Creature) -> String:
|
||||
var parts: Array[String] = []
|
||||
if cr.aura > 0:
|
||||
parts.append(tr("Elementbonus +%d/+%d") % [cr.aura, cr.aura])
|
||||
parts.append(tr("Elementbonus +%d") % cr.aura)
|
||||
var perk := cr.power() - cr.attack - cr.aura
|
||||
if perk > 0:
|
||||
parts.append(tr("Merkmal +%d") % perk)
|
||||
@@ -922,39 +944,25 @@ func update_attack_preview() -> void:
|
||||
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
|
||||
var counter: int = maxi(0, target.power() + Game.matchup_bonus(target, selected)) if target != null else 0
|
||||
v.doomed = mine and target != null and counter >= selected.health
|
||||
v.queue_redraw()
|
||||
|
||||
|
||||
class Deck extends Control:
|
||||
signal clicked
|
||||
var enabled := true
|
||||
var count := 0
|
||||
var cost := Game.DRAW_COST
|
||||
|
||||
## Eine verdeckte Handkarte des Gegners in voller Größe.
|
||||
class CardBack extends Control:
|
||||
func _init(rect: Rect2) -> void:
|
||||
position = rect.position
|
||||
size = rect.size
|
||||
mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
accept_event()
|
||||
clicked.emit()
|
||||
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
func _draw() -> void:
|
||||
for k in range(2, -1, -1):
|
||||
var r := Rect2(Vector2(k * 3, -k * 3), size - Vector2(6, 12)).grow(0)
|
||||
r.position.y += 6
|
||||
draw_style_box(Style.box(Color("3c285a"), 8, Style.GOLD if enabled else Style.MUTED, 2), r)
|
||||
if k == 0:
|
||||
Style.border(self, r.grow(-12), Color("5a3c82"), 2, 6)
|
||||
Style.text(self, tr("Ziehen"), 16, Style.TEXT if enabled else Style.MUTED, r.get_center() + Vector2(0, -12), "center", true)
|
||||
Style.text(self, "◆ %d" % cost, 20, Style.BLUE if enabled else Style.MUTED, r.get_center() + Vector2(0, 14), "center", true)
|
||||
Style.text(self, str(count), 12, Style.MUTED, Vector2(r.get_center().x, r.end.y - 18), "center")
|
||||
if not enabled:
|
||||
draw_rect(Rect2(Vector2.ZERO, size), Color(0, 0, 0, 0.35))
|
||||
var r := Rect2(Vector2.ZERO, size)
|
||||
draw_style_box(Style.box(Color("3c285a"), 8, Style.GOLD, 2), r)
|
||||
Style.border(self, r.grow(-12), Color("5a3c82"), 2, 6)
|
||||
Style.text(self, "?", 40, Style.GOLD, size / 2, "center", true)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1048,7 +1056,7 @@ func refresh() -> void:
|
||||
labels.name.add_theme_color_override("font_color", Style.TEXT if o.alive() else Style.MUTED)
|
||||
if o.alive():
|
||||
labels.life.text = "♥ %d" % maxi(o.life, 0)
|
||||
labels.stats.text = "Hand %d ◆ %d" % [o.hand.size(), o.energy]
|
||||
labels.stats.text = "◆ %d" % o.energy
|
||||
else:
|
||||
labels.life.text = "besiegt"
|
||||
labels.life.add_theme_color_override("font_color", Style.MUTED)
|
||||
@@ -1088,6 +1096,11 @@ func refresh() -> void:
|
||||
var c := game.find_creature(uid)
|
||||
if c != null:
|
||||
_press(c, tile, pos))
|
||||
tile.double_clicked.connect(func(_v):
|
||||
var c := game.find_creature(uid)
|
||||
if c != null and c.owner() == perspective and my_turn() and not any_overlay():
|
||||
selected = c # Doppelklick: gleich auf das Leben des Gegners
|
||||
on_click("hero", opponent()))
|
||||
if arrivals.has(cr.uid):
|
||||
fx.fly_in(tile, arrivals[cr.uid])
|
||||
arrivals.erase(cr.uid)
|
||||
@@ -1125,13 +1138,33 @@ func refresh() -> void:
|
||||
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()
|
||||
deck.cost = g.draw_cost(h)
|
||||
deck.queue_redraw()
|
||||
Style.clear(offer_layer)
|
||||
offer_views = []
|
||||
draw_enabled = mine and g.can_draw(h)[0]
|
||||
if not spectating():
|
||||
for i in h.offer.size():
|
||||
var oc: Cards.Card = h.offer[i]
|
||||
var orect := Rect2(BattleLayout.OFFER_X, BattleLayout.OFFER_Y + i * (BattleLayout.HAND_H + BattleLayout.OFFER_GAP), BattleLayout.HAND_W, BattleLayout.HAND_H)
|
||||
var ov := CardView.new(oc, orect.size, false, oc.rarity_in(h.looks))
|
||||
ov.position = orect.position
|
||||
ov.dim = not draw_enabled
|
||||
var idx := i
|
||||
ov.clicked.connect(func(_v): on_click("deck", idx))
|
||||
ov.hover.connect(func(c, e): show_preview(c, e, h.looks, "", orect))
|
||||
offer_layer.add_child(ov)
|
||||
offer_views.append(ov)
|
||||
deck_label.text = "%s ◆ %d · %d" % [tr("Ziehen"), g.draw_cost(h), g.deck.size()]
|
||||
deck_label.visible = not spectating()
|
||||
|
||||
Style.clear(hand_layer)
|
||||
for o in others(): # die Hand des Gegners oben, nur Rückseiten, spiegelverkehrter Bogen
|
||||
for i in o.hand.size():
|
||||
var back := CardBack.new(BattleLayout.opp_hand_rect(o.hand.size(), i))
|
||||
var arc: Array = BattleLayout.hand_arc(o.hand.size(), i, true)
|
||||
back.pivot_offset = back.size / 2
|
||||
back.rotation_degrees = arc[0]
|
||||
back.position.y += arc[1]
|
||||
hand_layer.add_child(back)
|
||||
if spectating():
|
||||
Style.label(hand_layer, "Zuschauer: Hände sind verdeckt", 18, Style.MUTED, Rect2(240, BattleLayout.HAND_Y + 18, 800, 30), "center")
|
||||
else:
|
||||
@@ -1141,6 +1174,9 @@ func refresh() -> void:
|
||||
var r := BattleLayout.hand_rect(h.hand.size(), i)
|
||||
var v := CardView.new(card, r.size, false, card.rarity_in(h.looks))
|
||||
v.position = r.position
|
||||
var arc: Array = BattleLayout.hand_arc(h.hand.size(), i)
|
||||
v.rotation_degrees = arc[0]
|
||||
v.position.y += arc[1]
|
||||
v.lift = mine
|
||||
v.bonus = g.forecast(h, card)
|
||||
if fuse_first != null and mine and g.can_fuse(h, fuse_first, card)[0]:
|
||||
@@ -1163,16 +1199,13 @@ func refresh() -> void:
|
||||
name_label.text = h.name
|
||||
life_label.text = "♥ %d" % maxi(h.life, 0)
|
||||
energy_label.text = "◆ %d" % h.energy
|
||||
income_label.text = tr("nächster Zug ◆ %d") % (h.max_energy + 1)
|
||||
status_label.text = (tr("Zug %d · Du bist dran") % g.turn) if mine else (tr("Zug %d · %s ist dran …") % [g.turn, g.current().name])
|
||||
if spectating():
|
||||
status_label.text = tr("Zuschauer") + " · " + status_label.text
|
||||
b_end.disabled = not mine
|
||||
var ai_running: bool = g.winner == "" and not g.current().is_human and not is_client()
|
||||
b_skip.visible = ai_running
|
||||
b_end.visible = not ai_running and not spectating()
|
||||
b_undo.visible = undo_state != null and mine
|
||||
element_view.update_from(g, h)
|
||||
opp_element_view.update_from(g, opponent())
|
||||
var lines: Array[String] = []
|
||||
var start := maxi(0, g.log.size() - 60)
|
||||
for i in range(start, g.log.size()):
|
||||
|
||||
Reference in New Issue
Block a user