first commit
This commit is contained in:
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"godotTools.editorPath.godot4": "c:\\Program Files\\Godot\\Godot_v4.3-stable_win64.exe"
|
||||
}
|
||||
1
icon.svg
Normal file
1
icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
||||
|
After Width: | Height: | Size: 994 B |
37
icon.svg.import
Normal file
37
icon.svg.import
Normal file
@@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dcwx6pxubtivy"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
16
project.godot
Normal file
16
project.godot
Normal file
@@ -0,0 +1,16 @@
|
||||
; Engine configuration file.
|
||||
; It's best edited using the editor UI and not directly,
|
||||
; since the parameters that go here are not all obvious.
|
||||
;
|
||||
; Format:
|
||||
; [section] ; section goes between []
|
||||
; param=value ; assign values to parameters
|
||||
|
||||
config_version=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="dujorak"
|
||||
run/main_scene="res://src/scenes/game.tscn"
|
||||
config/features=PackedStringArray("4.3", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
BIN
src/assets/card_atlas.png
Normal file
BIN
src/assets/card_atlas.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 150 KiB |
34
src/assets/card_atlas.png.import
Normal file
34
src/assets/card_atlas.png.import
Normal file
@@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dj3k1qd5ahoa2"
|
||||
path="res://.godot/imported/card_atlas.png-574f342db327405a75c03c3a7250402c.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/assets/card_atlas.png"
|
||||
dest_files=["res://.godot/imported/card_atlas.png-574f342db327405a75c03c3a7250402c.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
14
src/scenes/game.tscn
Normal file
14
src/scenes/game.tscn
Normal file
@@ -0,0 +1,14 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://b3cyy67wp6hd0"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/scripts/game.gd" id="1_vl3q8"]
|
||||
[ext_resource type="Script" path="res://src/scripts/ui.gd" id="2_dljv7"]
|
||||
|
||||
[node name="Game" type="Node2D"]
|
||||
script = ExtResource("1_vl3q8")
|
||||
|
||||
[node name="UI" type="Control" parent="."]
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
script = ExtResource("2_dljv7")
|
||||
389
src/scripts/game.gd
Normal file
389
src/scripts/game.gd
Normal file
@@ -0,0 +1,389 @@
|
||||
extends Node2D
|
||||
|
||||
enum Suits {HEARTS, DIAMONDS, CLUBS, SPADES}
|
||||
enum Values {SIX = 6, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE}
|
||||
enum GameState {SETUP, ATTACK, DEFEND, DRAW_PHASE, GAME_OVER}
|
||||
enum JokerType {DOUBLE_ATTACK, TRUMP_CHANGE, DRAW_BLOCK}
|
||||
|
||||
class Player:
|
||||
var hand: Array = []
|
||||
var jokers_hand: Array = []
|
||||
var coins: int = 100
|
||||
var is_attacker: bool = false
|
||||
|
||||
func add_joker_to_hand(joker_type: JokerType):
|
||||
jokers_hand.append(joker_type)
|
||||
|
||||
func has_jokers_in_hand() -> bool:
|
||||
return jokers_hand.size() > 0
|
||||
|
||||
func remove_card(card):
|
||||
hand.erase(card)
|
||||
|
||||
func has_card_value(value) -> bool:
|
||||
for card in hand:
|
||||
if card.value == value:
|
||||
return true
|
||||
return false
|
||||
|
||||
# Game Variables
|
||||
var deck: Array = []
|
||||
var trump_suit: Suits
|
||||
var player1: Player
|
||||
var player2: Player
|
||||
var current_attacker: Player
|
||||
var current_defender: Player
|
||||
var game_phase: GameState = GameState.SETUP
|
||||
var active_jokers: Array = []
|
||||
var MAX_JOKERS: int = 3
|
||||
|
||||
# Battle Variables
|
||||
var attacking_cards: Array = []
|
||||
var defending_cards: Array = []
|
||||
var table_pairs: Array = [] # [{attack: card, defend: card}]
|
||||
var can_add_attack: bool = false
|
||||
|
||||
# UI Reference
|
||||
@onready var ui = $UI
|
||||
|
||||
func _ready():
|
||||
player1 = Player.new()
|
||||
player2 = Player.new()
|
||||
current_attacker = player1
|
||||
current_defender = player2
|
||||
player1.is_attacker = true
|
||||
|
||||
if ui:
|
||||
ui.card_played.connect(_on_card_played)
|
||||
ui.joker_played.connect(_on_joker_played)
|
||||
ui.booster_bought.connect(_on_booster_bought)
|
||||
ui.defend_button_pressed.connect(_on_defend_complete)
|
||||
ui.take_cards_pressed.connect(_on_take_cards)
|
||||
|
||||
start_new_game()
|
||||
|
||||
func start_new_game():
|
||||
create_deck()
|
||||
setup_initial_jokers()
|
||||
shuffle_deck()
|
||||
deal_cards()
|
||||
set_trump()
|
||||
game_phase = GameState.ATTACK
|
||||
update_ui()
|
||||
print("Spiel gestartet! ", current_attacker, " greift an")
|
||||
|
||||
func create_deck():
|
||||
deck.clear()
|
||||
for suit in [Suits.HEARTS, Suits.DIAMONDS, Suits.CLUBS, Suits.SPADES]:
|
||||
for value in [Values.SIX, Values.SEVEN, Values.EIGHT, Values.NINE, Values.TEN, Values.JACK, Values.QUEEN, Values.KING, Values.ACE]:
|
||||
deck.append({"suit": suit, "value": value, "is_joker": false})
|
||||
|
||||
func setup_initial_jokers():
|
||||
pass
|
||||
|
||||
func shuffle_deck():
|
||||
deck.shuffle()
|
||||
|
||||
func deal_cards():
|
||||
for i in range(6):
|
||||
if deck.size() > 0:
|
||||
var card = deck.pop_back()
|
||||
if card.is_joker:
|
||||
player1.add_joker_to_hand(card.joker_type)
|
||||
else:
|
||||
player1.hand.append(card)
|
||||
|
||||
if deck.size() > 0:
|
||||
var card = deck.pop_back()
|
||||
if card.is_joker:
|
||||
player2.add_joker_to_hand(card.joker_type)
|
||||
else:
|
||||
player2.hand.append(card)
|
||||
|
||||
func set_trump():
|
||||
if deck.size() > 0:
|
||||
var trump_card = deck[0]
|
||||
if not trump_card.is_joker:
|
||||
trump_suit = trump_card.suit
|
||||
|
||||
# MAIN GAME LOGIC
|
||||
func _on_card_played(card):
|
||||
match game_phase:
|
||||
GameState.ATTACK:
|
||||
handle_attack(card)
|
||||
GameState.DEFEND:
|
||||
handle_defense(card)
|
||||
|
||||
func handle_attack(card):
|
||||
# Prüfen ob Angriff erlaubt
|
||||
if not can_attack_with_card(card):
|
||||
print("Angriff mit dieser Karte nicht möglich!")
|
||||
return
|
||||
|
||||
# Karte vom Angreifer entfernen
|
||||
current_attacker.remove_card(card)
|
||||
attacking_cards.append(card)
|
||||
|
||||
print("Angriff mit: ", card)
|
||||
|
||||
# Wechsel zu Verteidigung
|
||||
game_phase = GameState.DEFEND
|
||||
update_ui()
|
||||
|
||||
func handle_defense(card):
|
||||
if attacking_cards.size() == 0:
|
||||
print("Nichts zu verteidigen!")
|
||||
return
|
||||
|
||||
var attack_card = attacking_cards[-1] # Letzte Angriffskarte
|
||||
|
||||
if can_beat_card(attack_card, card):
|
||||
# Erfolgreiche Verteidigung
|
||||
current_defender.remove_card(card)
|
||||
defending_cards.append(card)
|
||||
|
||||
# Paar erstellen
|
||||
table_pairs.append({"attack": attack_card, "defend": card})
|
||||
attacking_cards.pop_back()
|
||||
|
||||
print("Verteidigt: ", attack_card, " mit ", card)
|
||||
|
||||
# Prüfen ob weitere Angriffe möglich
|
||||
can_add_attack = true
|
||||
check_round_end()
|
||||
else:
|
||||
print("Karte kann nicht verteidigen!")
|
||||
|
||||
func can_attack_with_card(card) -> bool:
|
||||
# Erster Angriff: Jede Karte erlaubt
|
||||
if attacking_cards.size() == 0 and table_pairs.size() == 0:
|
||||
return true
|
||||
|
||||
# Weitere Angriffe: Nur Werte die schon auf dem Tisch liegen
|
||||
var table_values = []
|
||||
for pair in table_pairs:
|
||||
table_values.append(pair.attack.value)
|
||||
table_values.append(pair.defend.value)
|
||||
for attack in attacking_cards:
|
||||
table_values.append(attack.value)
|
||||
|
||||
return card.value in table_values
|
||||
|
||||
func can_beat_card(attacking_card, defending_card) -> bool:
|
||||
# Trump schlägt alles außer höheren Trump
|
||||
if defending_card.suit == trump_suit and attacking_card.suit != trump_suit:
|
||||
return true
|
||||
|
||||
# Gleiche Farbe: höherer Wert gewinnt
|
||||
if attacking_card.suit == defending_card.suit:
|
||||
return defending_card.value > attacking_card.value
|
||||
|
||||
return false
|
||||
|
||||
func check_round_end():
|
||||
# Alle Angriffe verteidigt
|
||||
if attacking_cards.size() == 0:
|
||||
if can_add_attack and current_attacker.hand.size() > 0:
|
||||
# Angreifer kann weitere Karten spielen
|
||||
game_phase = GameState.ATTACK
|
||||
print("Weitere Angriffe möglich")
|
||||
else:
|
||||
# Runde erfolgreich verteidigt
|
||||
end_round_defended()
|
||||
else:
|
||||
# Warten auf weitere Verteidigung
|
||||
print("Warte auf Verteidigung...")
|
||||
|
||||
func end_round_defended():
|
||||
print("Runde erfolgreich verteidigt!")
|
||||
|
||||
# Karten vom Tisch entfernen
|
||||
table_pairs.clear()
|
||||
attacking_cards.clear()
|
||||
defending_cards.clear()
|
||||
can_add_attack = false
|
||||
|
||||
# Rollen tauschen: Verteidiger wird Angreifer
|
||||
swap_roles()
|
||||
|
||||
# Karten nachziehen
|
||||
draw_phase()
|
||||
|
||||
func end_round_taken():
|
||||
print("Verteidiger nimmt alle Karten!")
|
||||
|
||||
# Alle Karten an Verteidiger geben
|
||||
for pair in table_pairs:
|
||||
current_defender.hand.append(pair.attack)
|
||||
current_defender.hand.append(pair.defend)
|
||||
|
||||
for card in attacking_cards:
|
||||
current_defender.hand.append(card)
|
||||
|
||||
# Aufräumen
|
||||
table_pairs.clear()
|
||||
attacking_cards.clear()
|
||||
defending_cards.clear()
|
||||
can_add_attack = false
|
||||
|
||||
# Angreifer bleibt Angreifer (Verteidiger nimmt)
|
||||
# Karten nachziehen
|
||||
draw_phase()
|
||||
|
||||
func swap_roles():
|
||||
var temp = current_attacker
|
||||
current_attacker = current_defender
|
||||
current_defender = temp
|
||||
|
||||
current_attacker.is_attacker = true
|
||||
current_defender.is_attacker = false
|
||||
|
||||
print("Rollen getauscht: ", current_attacker, " greift an")
|
||||
|
||||
func draw_phase():
|
||||
game_phase = GameState.DRAW_PHASE
|
||||
|
||||
# Angreifer zieht zuerst auf 6 Karten
|
||||
draw_cards_to_six(current_attacker)
|
||||
|
||||
# Dann Verteidiger
|
||||
draw_cards_to_six(current_defender)
|
||||
|
||||
# Spielende prüfen
|
||||
if check_game_end():
|
||||
return
|
||||
|
||||
# Nächste Runde starten
|
||||
game_phase = GameState.ATTACK
|
||||
update_ui()
|
||||
|
||||
func draw_cards_to_six(player: Player):
|
||||
while player.hand.size() < 6 and deck.size() > 0:
|
||||
var card = deck.pop_back()
|
||||
if card.is_joker:
|
||||
player.add_joker_to_hand(card.joker_type)
|
||||
# UI: Joker-Dialog anzeigen
|
||||
show_joker_dialog(player, card.joker_type)
|
||||
else:
|
||||
player.hand.append(card)
|
||||
|
||||
func show_joker_dialog(player: Player, joker_type: JokerType):
|
||||
if player == player1: # Nur für menschlichen Spieler
|
||||
ui.show_joker_choice(joker_type)
|
||||
|
||||
func _on_joker_choice(play_immediately: bool, joker_type: JokerType):
|
||||
if play_immediately:
|
||||
play_joker_to_field(player1, joker_type)
|
||||
# Sonst bleibt Joker auf der Hand (Risiko!)
|
||||
|
||||
# UI Event Handlers
|
||||
func _on_defend_complete():
|
||||
if game_phase == GameState.DEFEND:
|
||||
# Verteidiger gibt auf, nimmt alle Karten
|
||||
end_round_taken()
|
||||
|
||||
func _on_take_cards():
|
||||
if game_phase == GameState.DEFEND:
|
||||
end_round_taken()
|
||||
|
||||
func _on_joker_played(joker_type):
|
||||
if play_joker_to_field(player1, joker_type):
|
||||
update_ui()
|
||||
|
||||
func _on_booster_bought():
|
||||
var booster = buy_booster(player1)
|
||||
for item in booster:
|
||||
if item.type == "joker":
|
||||
player1.add_joker_to_hand(item.joker_type)
|
||||
elif item.type == "coins":
|
||||
player1.coins += item.amount
|
||||
update_ui()
|
||||
|
||||
# Game End Logic
|
||||
func check_game_end() -> bool:
|
||||
if deck.size() == 0:
|
||||
# Kein Deck mehr, prüfe wer keine Karten hat
|
||||
if player1.hand.size() == 0:
|
||||
if player1.has_jokers_in_hand():
|
||||
print("Spieler 1 verliert - Joker auf der Hand!")
|
||||
game_phase = GameState.GAME_OVER
|
||||
return true
|
||||
else:
|
||||
print("Spieler 1 gewinnt!")
|
||||
game_phase = GameState.GAME_OVER
|
||||
return true
|
||||
|
||||
if player2.hand.size() == 0:
|
||||
if player2.has_jokers_in_hand():
|
||||
print("Spieler 2 verliert - Joker auf der Hand!")
|
||||
game_phase = GameState.GAME_OVER
|
||||
return true
|
||||
else:
|
||||
print("Spieler 2 gewinnt!")
|
||||
game_phase = GameState.GAME_OVER
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
# Joker System
|
||||
func play_joker_to_field(player: Player, joker_type: JokerType) -> bool:
|
||||
if active_jokers.size() >= MAX_JOKERS:
|
||||
print("Maximale Joker-Anzahl erreicht!")
|
||||
return false
|
||||
|
||||
if joker_type in player.jokers_hand:
|
||||
player.jokers_hand.erase(joker_type)
|
||||
active_jokers.append(joker_type)
|
||||
print("Joker aktiviert: ", joker_type)
|
||||
apply_joker_effect(joker_type)
|
||||
return true
|
||||
return false
|
||||
|
||||
func apply_joker_effect(joker_type: JokerType):
|
||||
match joker_type:
|
||||
JokerType.TRUMP_CHANGE:
|
||||
print("Trump-Wechsel verfügbar!")
|
||||
# UI für Trump-Auswahl
|
||||
JokerType.DOUBLE_ATTACK:
|
||||
print("Doppel-Angriff möglich!")
|
||||
# Ermöglicht 2 Karten gleichzeitig
|
||||
JokerType.DRAW_BLOCK:
|
||||
print("Zieh-Block aktiv!")
|
||||
# Gegner kann nicht ziehen
|
||||
|
||||
func buy_booster(player: Player, cost: int = 50) -> Array:
|
||||
if player.coins >= cost:
|
||||
player.coins -= cost
|
||||
var booster_content = []
|
||||
|
||||
for i in range(3):
|
||||
if randf() < 0.3:
|
||||
var joker_types = [JokerType.DOUBLE_ATTACK, JokerType.TRUMP_CHANGE, JokerType.DRAW_BLOCK]
|
||||
var random_joker = joker_types[randi() % joker_types.size()]
|
||||
booster_content.append({"type": "joker", "joker_type": random_joker})
|
||||
else:
|
||||
booster_content.append({"type": "coins", "amount": randi() % 20 + 10})
|
||||
|
||||
return booster_content
|
||||
return []
|
||||
|
||||
func update_ui():
|
||||
if ui:
|
||||
ui.update_player_hand(player1.hand)
|
||||
ui.update_opponent_hand(player2.hand.size())
|
||||
ui.update_joker_slots(active_jokers)
|
||||
ui.update_coins(player1.coins)
|
||||
ui.update_table(table_pairs, attacking_cards)
|
||||
ui.update_game_state(game_phase, current_attacker == player1)
|
||||
|
||||
if deck.size() > 0:
|
||||
ui.update_trump(deck[0])
|
||||
|
||||
# Input for testing
|
||||
func _input(event):
|
||||
if event.is_action_pressed("ui_accept"):
|
||||
_on_booster_bought()
|
||||
|
||||
if event.is_action_pressed("ui_cancel"):
|
||||
if game_phase == GameState.DEFEND:
|
||||
_on_take_cards()
|
||||
372
src/scripts/ui.gd
Normal file
372
src/scripts/ui.gd
Normal file
@@ -0,0 +1,372 @@
|
||||
extends Control
|
||||
|
||||
# Signals
|
||||
signal card_played(card)
|
||||
signal joker_played(joker_type)
|
||||
signal booster_bought
|
||||
signal defend_button_pressed
|
||||
signal take_cards_pressed
|
||||
|
||||
# UI Components
|
||||
var player_hand_container: HBoxContainer
|
||||
var opponent_hand_container: HBoxContainer
|
||||
var table_area: GridContainer
|
||||
var trump_display: Control
|
||||
var joker_slots_container: HBoxContainer
|
||||
var coins_label: Label
|
||||
var deck_display: Control
|
||||
var game_state_label: Label
|
||||
var action_buttons: HBoxContainer
|
||||
|
||||
# Card Settings
|
||||
var card_size: Vector2 = Vector2(80, 120)
|
||||
var card_atlas: Texture2D
|
||||
|
||||
func _ready():
|
||||
# Load card atlas if exists
|
||||
if FileAccess.file_exists("res://src/assets/card_atlas.png"):
|
||||
card_atlas = load("res://src/assets/card_atlas.png")
|
||||
|
||||
setup_ui()
|
||||
|
||||
func setup_ui():
|
||||
# Main layout
|
||||
var main_vbox = VBoxContainer.new()
|
||||
add_child(main_vbox)
|
||||
main_vbox.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
|
||||
# Game state info
|
||||
game_state_label = Label.new()
|
||||
game_state_label.text = "Spiel wird geladen..."
|
||||
game_state_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
main_vbox.add_child(game_state_label)
|
||||
|
||||
# Top area (Opponent + Jokers)
|
||||
var top_hbox = HBoxContainer.new()
|
||||
main_vbox.add_child(top_hbox)
|
||||
|
||||
setup_opponent_area(top_hbox)
|
||||
setup_joker_area(top_hbox)
|
||||
|
||||
# Middle area (Table + Trump)
|
||||
var middle_hbox = HBoxContainer.new()
|
||||
main_vbox.add_child(middle_hbox)
|
||||
|
||||
setup_table_area(middle_hbox)
|
||||
setup_trump_area(middle_hbox)
|
||||
|
||||
# Action buttons
|
||||
action_buttons = HBoxContainer.new()
|
||||
action_buttons.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
main_vbox.add_child(action_buttons)
|
||||
|
||||
setup_action_buttons()
|
||||
|
||||
# Bottom area (Player hand + Controls)
|
||||
var bottom_vbox = VBoxContainer.new()
|
||||
main_vbox.add_child(bottom_vbox)
|
||||
|
||||
setup_controls(bottom_vbox)
|
||||
setup_player_area(bottom_vbox)
|
||||
|
||||
func setup_opponent_area(parent):
|
||||
var area = VBoxContainer.new()
|
||||
parent.add_child(area)
|
||||
|
||||
var label = Label.new()
|
||||
label.text = "Gegner"
|
||||
area.add_child(label)
|
||||
|
||||
opponent_hand_container = HBoxContainer.new()
|
||||
area.add_child(opponent_hand_container)
|
||||
|
||||
func setup_joker_area(parent):
|
||||
var area = VBoxContainer.new()
|
||||
parent.add_child(area)
|
||||
|
||||
var label = Label.new()
|
||||
label.text = "Joker (0/3)"
|
||||
label.name = "JokerLabel"
|
||||
area.add_child(label)
|
||||
|
||||
joker_slots_container = HBoxContainer.new()
|
||||
area.add_child(joker_slots_container)
|
||||
|
||||
# Create 3 joker slots
|
||||
for i in range(3):
|
||||
var slot = create_joker_slot()
|
||||
joker_slots_container.add_child(slot)
|
||||
|
||||
func setup_table_area(parent):
|
||||
var table_container = VBoxContainer.new()
|
||||
parent.add_child(table_container)
|
||||
|
||||
var label = Label.new()
|
||||
label.text = "Spielfeld"
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
table_container.add_child(label)
|
||||
|
||||
table_area = GridContainer.new()
|
||||
table_area.columns = 4 # Angriff/Verteidigung Paare
|
||||
table_container.add_child(table_area)
|
||||
|
||||
func setup_trump_area(parent):
|
||||
var area = VBoxContainer.new()
|
||||
parent.add_child(area)
|
||||
|
||||
var label = Label.new()
|
||||
label.text = "Trump"
|
||||
area.add_child(label)
|
||||
|
||||
trump_display = create_card_back()
|
||||
area.add_child(trump_display)
|
||||
|
||||
var deck_label = Label.new()
|
||||
deck_label.text = "Deck"
|
||||
area.add_child(deck_label)
|
||||
|
||||
deck_display = create_card_back()
|
||||
area.add_child(deck_display)
|
||||
|
||||
func setup_action_buttons():
|
||||
var defend_btn = Button.new()
|
||||
defend_btn.text = "Verteidigung beenden"
|
||||
defend_btn.pressed.connect(_on_defend_button_pressed)
|
||||
action_buttons.add_child(defend_btn)
|
||||
|
||||
var take_btn = Button.new()
|
||||
take_btn.text = "Karten nehmen"
|
||||
take_btn.pressed.connect(_on_take_cards_pressed)
|
||||
action_buttons.add_child(take_btn)
|
||||
|
||||
# Initially hidden
|
||||
action_buttons.visible = false
|
||||
|
||||
func setup_controls(parent):
|
||||
var controls = HBoxContainer.new()
|
||||
parent.add_child(controls)
|
||||
|
||||
coins_label = Label.new()
|
||||
coins_label.text = "Münzen: 100"
|
||||
controls.add_child(coins_label)
|
||||
|
||||
var booster_btn = Button.new()
|
||||
booster_btn.text = "Booster kaufen (50)"
|
||||
booster_btn.pressed.connect(_on_booster_button_pressed)
|
||||
controls.add_child(booster_btn)
|
||||
|
||||
func setup_player_area(parent):
|
||||
var area = VBoxContainer.new()
|
||||
parent.add_child(area)
|
||||
|
||||
var label = Label.new()
|
||||
label.text = "Deine Karten"
|
||||
area.add_child(label)
|
||||
|
||||
player_hand_container = HBoxContainer.new()
|
||||
area.add_child(player_hand_container)
|
||||
|
||||
# Card Creation Functions
|
||||
func create_card_ui(card, clickable: bool = true) -> Control:
|
||||
var card_button = Button.new()
|
||||
card_button.custom_minimum_size = card_size
|
||||
card_button.disabled = not clickable
|
||||
|
||||
if card.is_joker:
|
||||
card_button.text = "JOKER\n" + str(card.joker_type)
|
||||
card_button.modulate = Color.GOLD
|
||||
else:
|
||||
var suit_symbols = ["♥", "♦", "♣", "♠"]
|
||||
var suit_text = suit_symbols[card.suit]
|
||||
card_button.text = str(card.value) + "\n" + suit_text
|
||||
|
||||
# Color coding
|
||||
if card.suit in [0, 1]: # Hearts, Diamonds
|
||||
card_button.modulate = Color.RED
|
||||
else: # Clubs, Spades
|
||||
card_button.modulate = Color.BLACK
|
||||
|
||||
if clickable:
|
||||
card_button.pressed.connect(_on_card_clicked.bind(card))
|
||||
|
||||
return card_button
|
||||
|
||||
func create_card_back() -> Control:
|
||||
var card_back = ColorRect.new()
|
||||
card_back.color = Color.BLUE
|
||||
card_back.custom_minimum_size = card_size
|
||||
|
||||
var label = Label.new()
|
||||
label.text = "🂠"
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
card_back.add_child(label)
|
||||
|
||||
return card_back
|
||||
|
||||
func create_joker_slot() -> Control:
|
||||
var slot = Panel.new()
|
||||
slot.custom_minimum_size = card_size * 0.7
|
||||
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color.GRAY
|
||||
style.border_width_left = 2
|
||||
style.border_width_right = 2
|
||||
style.border_width_top = 2
|
||||
style.border_width_bottom = 2
|
||||
style.border_color = Color.WHITE
|
||||
slot.add_theme_stylebox_override("panel", style)
|
||||
|
||||
return slot
|
||||
|
||||
# Update Functions
|
||||
func update_player_hand(cards: Array):
|
||||
# Clear existing cards
|
||||
for child in player_hand_container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
# Add new cards
|
||||
for card in cards:
|
||||
var card_ui = create_card_ui(card, true)
|
||||
player_hand_container.add_child(card_ui)
|
||||
|
||||
func update_opponent_hand(card_count: int):
|
||||
# Clear existing cards
|
||||
for child in opponent_hand_container.get_children():
|
||||
child.queue_free()
|
||||
|
||||
# Add card backs
|
||||
for i in range(card_count):
|
||||
var card_back = create_card_back()
|
||||
card_back.custom_minimum_size = card_size * 0.8
|
||||
opponent_hand_container.add_child(card_back)
|
||||
|
||||
func update_table(table_pairs: Array, attacking_cards: Array):
|
||||
# Clear existing table
|
||||
for child in table_area.get_children():
|
||||
child.queue_free()
|
||||
|
||||
# Show completed pairs
|
||||
for pair in table_pairs:
|
||||
var attack_card = create_card_ui(pair.attack, false)
|
||||
attack_card.modulate = Color.ORANGE # Angriff
|
||||
table_area.add_child(attack_card)
|
||||
|
||||
var defend_card = create_card_ui(pair.defend, false)
|
||||
defend_card.modulate = Color.GREEN # Verteidigung
|
||||
table_area.add_child(defend_card)
|
||||
|
||||
# Show undefended attacks
|
||||
for card in attacking_cards:
|
||||
var attack_card = create_card_ui(card, false)
|
||||
attack_card.modulate = Color.RED # Unverteidigt
|
||||
table_area.add_child(attack_card)
|
||||
|
||||
# Empty slot for defense
|
||||
var empty_slot = Panel.new()
|
||||
empty_slot.custom_minimum_size = card_size
|
||||
var style = StyleBoxFlat.new()
|
||||
style.bg_color = Color.DARK_GRAY
|
||||
empty_slot.add_theme_stylebox_override("panel", style)
|
||||
table_area.add_child(empty_slot)
|
||||
|
||||
func update_joker_slots(active_jokers: Array):
|
||||
var label = joker_slots_container.get_parent().get_node("JokerLabel")
|
||||
label.text = "Joker (" + str(active_jokers.size()) + "/3)"
|
||||
|
||||
# Update slots
|
||||
for i in range(3):
|
||||
var slot = joker_slots_container.get_child(i)
|
||||
if i < active_jokers.size():
|
||||
slot.modulate = Color.GREEN
|
||||
|
||||
# Add joker label
|
||||
var joker_label = Label.new()
|
||||
joker_label.text = str(active_jokers[i])
|
||||
joker_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
joker_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
slot.add_child(joker_label)
|
||||
else:
|
||||
slot.modulate = Color.WHITE
|
||||
|
||||
func update_coins(amount: int):
|
||||
coins_label.text = "Münzen: " + str(amount)
|
||||
|
||||
func update_trump(trump_card):
|
||||
if trump_card and trump_display:
|
||||
# Replace with actual card
|
||||
trump_display.queue_free()
|
||||
trump_display = create_card_ui(trump_card, false)
|
||||
trump_display.modulate = Color.YELLOW
|
||||
trump_display.get_parent().add_child(trump_display)
|
||||
|
||||
func update_game_state(state, is_player_turn: bool):
|
||||
match state:
|
||||
0: # GameState.SETUP
|
||||
game_state_label.text = "Spiel wird vorbereitet..."
|
||||
action_buttons.visible = false
|
||||
1: # GameState.ATTACK
|
||||
if is_player_turn:
|
||||
game_state_label.text = "Du bist am Zug - Wähle eine Angriffskarte"
|
||||
else:
|
||||
game_state_label.text = "Gegner greift an..."
|
||||
action_buttons.visible = false
|
||||
2: # GameState.DEFEND
|
||||
if is_player_turn:
|
||||
game_state_label.text = "Gegner greift an - Verteidige oder nimm die Karten"
|
||||
action_buttons.visible = true
|
||||
else:
|
||||
game_state_label.text = "Du greifst an - Gegner verteidigt..."
|
||||
action_buttons.visible = false
|
||||
3: # GameState.DRAW_PHASE
|
||||
game_state_label.text = "Karten werden nachgezogen..."
|
||||
action_buttons.visible = false
|
||||
4: # GameState.GAME_OVER
|
||||
game_state_label.text = "Spiel beendet!"
|
||||
action_buttons.visible = false
|
||||
|
||||
# Joker Dialog
|
||||
func show_joker_choice(joker_type):
|
||||
var dialog = AcceptDialog.new()
|
||||
dialog.title = "Joker gezogen!"
|
||||
|
||||
var vbox = VBoxContainer.new()
|
||||
|
||||
var info_label = Label.new()
|
||||
info_label.text = "Du hast einen " + str(joker_type) + " Joker gezogen!"
|
||||
vbox.add_child(info_label)
|
||||
|
||||
var play_btn = Button.new()
|
||||
play_btn.text = "Sofort spielen"
|
||||
play_btn.pressed.connect(_on_joker_play_immediate.bind(joker_type, dialog))
|
||||
vbox.add_child(play_btn)
|
||||
|
||||
var keep_btn = Button.new()
|
||||
keep_btn.text = "Behalten (Risiko!)"
|
||||
keep_btn.pressed.connect(_on_joker_keep.bind(dialog))
|
||||
vbox.add_child(keep_btn)
|
||||
|
||||
dialog.add_child(vbox)
|
||||
add_child(dialog)
|
||||
dialog.popup_centered()
|
||||
|
||||
func _on_joker_play_immediate(joker_type, dialog):
|
||||
joker_played.emit(joker_type)
|
||||
dialog.queue_free()
|
||||
|
||||
func _on_joker_keep(dialog):
|
||||
print("Joker behalten - Vorsicht!")
|
||||
dialog.queue_free()
|
||||
|
||||
# Event Handlers
|
||||
func _on_card_clicked(card):
|
||||
card_played.emit(card)
|
||||
|
||||
func _on_booster_button_pressed():
|
||||
booster_bought.emit()
|
||||
|
||||
func _on_defend_button_pressed():
|
||||
defend_button_pressed.emit()
|
||||
|
||||
func _on_take_cards_pressed():
|
||||
take_cards_pressed.emit()
|
||||
Reference in New Issue
Block a user