2026-07-02 20:58:21 +02:00
|
|
|
extends Control
|
|
|
|
|
|
|
|
|
|
# Spieler-Definitionen
|
|
|
|
|
const PLAYER_X = "X"
|
|
|
|
|
const PLAYER_O = "O"
|
|
|
|
|
const EMPTY = ""
|
|
|
|
|
|
|
|
|
|
var current_player = PLAYER_X
|
|
|
|
|
var game_over = false
|
|
|
|
|
|
|
|
|
|
# Referenzen zu den Nodes
|
|
|
|
|
@onready var board_grid = $Board
|
|
|
|
|
@onready var status_label = $StatusLabel
|
2026-07-03 21:35:29 +02:00
|
|
|
@onready var win_line = $WinLine
|
2026-07-02 20:58:21 +02:00
|
|
|
|
|
|
|
|
# Mapping von Numpad-Aktionen zu den Array-Indizes (0 bis 8)
|
|
|
|
|
var numpad_mapping = {
|
|
|
|
|
"pad_7": 0, "pad_8": 1, "pad_9": 2,
|
|
|
|
|
"pad_4": 3, "pad_5": 4, "pad_6": 5,
|
|
|
|
|
"pad_1": 6, "pad_2": 7, "pad_3": 8
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var buttons = []
|
|
|
|
|
|
2026-07-03 21:35:29 +02:00
|
|
|
# --- NEU: Beide Textur-Referenzen geladen ---
|
|
|
|
|
var circle_texture = load("res://res/Dornenkrone.png") # Textur für O
|
|
|
|
|
var cross_texture = load("res://res/Rankenkreuz.png") # Textur für X (Hier Pfad anpassen!)
|
|
|
|
|
# --------------------------------------------
|
|
|
|
|
|
2026-07-02 20:58:21 +02:00
|
|
|
var win_conditions = [
|
2026-07-03 21:35:29 +02:00
|
|
|
[0, 1, 2], [3, 4, 5], [6, 7, 8],
|
|
|
|
|
[0, 3, 6], [1, 4, 7], [2, 5, 8],
|
|
|
|
|
[0, 4, 8], [2, 4, 6]
|
2026-07-02 20:58:21 +02:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
func _ready():
|
|
|
|
|
board_grid.custom_minimum_size = Vector2(500, 500)
|
|
|
|
|
buttons = board_grid.get_children()
|
|
|
|
|
|
|
|
|
|
win_line.clear_points()
|
|
|
|
|
|
|
|
|
|
for i in range(buttons.size()):
|
|
|
|
|
buttons[i].size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
|
|
|
buttons[i].size_flags_vertical = Control.SIZE_EXPAND_FILL
|
2026-07-03 21:35:29 +02:00
|
|
|
|
|
|
|
|
# Buttons komplett leeren (kein störender Text mehr)
|
|
|
|
|
buttons[i].text = EMPTY
|
|
|
|
|
|
|
|
|
|
# Finde die TextureRect-Node im Button und leere sie für den Start
|
|
|
|
|
var piece_texture = buttons[i].get_node("PieceTexture")
|
|
|
|
|
piece_texture.texture = null
|
|
|
|
|
|
2026-07-02 20:58:21 +02:00
|
|
|
buttons[i].pressed.connect(_on_button_pressed.bind(i))
|
|
|
|
|
|
|
|
|
|
update_status_label()
|
|
|
|
|
|
|
|
|
|
func _input(event):
|
|
|
|
|
if game_over:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
for action in numpad_mapping.keys():
|
|
|
|
|
if event.is_action_pressed(action):
|
|
|
|
|
var index = numpad_mapping[action]
|
2026-07-03 21:35:29 +02:00
|
|
|
|
|
|
|
|
var piece_texture = buttons[index].get_node("PieceTexture")
|
|
|
|
|
if piece_texture.texture == null:
|
2026-07-02 20:58:21 +02:00
|
|
|
make_move(index)
|
|
|
|
|
|
|
|
|
|
func _on_button_pressed(index):
|
2026-07-03 21:35:29 +02:00
|
|
|
var piece_texture = buttons[index].get_node("PieceTexture")
|
|
|
|
|
if game_over or piece_texture.texture != null:
|
2026-07-02 20:58:21 +02:00
|
|
|
return
|
|
|
|
|
make_move(index)
|
|
|
|
|
|
|
|
|
|
func make_move(index):
|
2026-07-03 21:35:29 +02:00
|
|
|
var piece_texture = buttons[index].get_node("PieceTexture")
|
|
|
|
|
|
|
|
|
|
# Weist jetzt je nach Spieler die korrekte Bild-Textur zu
|
|
|
|
|
if current_player == PLAYER_O:
|
|
|
|
|
piece_texture.texture = circle_texture
|
|
|
|
|
else:
|
|
|
|
|
piece_texture.texture = cross_texture
|
2026-07-02 20:58:21 +02:00
|
|
|
|
|
|
|
|
var winning_combo = check_win()
|
|
|
|
|
if winning_combo != null:
|
|
|
|
|
status_label.text = "Spieler " + current_player + " hat gewonnen!"
|
|
|
|
|
game_over = true
|
2026-07-03 21:35:29 +02:00
|
|
|
animate_win_line(winning_combo)
|
2026-07-02 20:58:21 +02:00
|
|
|
elif check_draw():
|
|
|
|
|
status_label.text = "Unentschieden!"
|
|
|
|
|
game_over = true
|
|
|
|
|
else:
|
|
|
|
|
current_player = PLAYER_O if current_player == PLAYER_X else PLAYER_X
|
|
|
|
|
update_status_label()
|
|
|
|
|
|
|
|
|
|
func update_status_label():
|
|
|
|
|
status_label.text = "Spieler " + current_player + " ist am Zug."
|
|
|
|
|
|
2026-07-03 21:35:29 +02:00
|
|
|
# Prüft den Gewinn vollautomatisch über die gesetzten Texturen
|
2026-07-02 20:58:21 +02:00
|
|
|
func check_win():
|
|
|
|
|
for cond in win_conditions:
|
2026-07-03 21:35:29 +02:00
|
|
|
var p0 = buttons[cond[0]].get_node("PieceTexture").texture
|
|
|
|
|
var p1 = buttons[cond[1]].get_node("PieceTexture").texture
|
|
|
|
|
var p2 = buttons[cond[2]].get_node("PieceTexture").texture
|
|
|
|
|
|
|
|
|
|
if current_player == PLAYER_O:
|
|
|
|
|
if p0 == circle_texture and p1 == circle_texture and p2 == circle_texture:
|
|
|
|
|
return cond
|
|
|
|
|
else:
|
|
|
|
|
if p0 == cross_texture and p1 == cross_texture and p2 == cross_texture:
|
|
|
|
|
return cond
|
2026-07-02 20:58:21 +02:00
|
|
|
return null
|
|
|
|
|
|
2026-07-03 21:35:29 +02:00
|
|
|
# Prüft auf Unentschieden: Wenn alle Felder eine Textur haben, ist das Feld voll
|
2026-07-02 20:58:21 +02:00
|
|
|
func check_draw() -> bool:
|
|
|
|
|
for button in buttons:
|
2026-07-03 21:35:29 +02:00
|
|
|
var piece_texture = button.get_node("PieceTexture")
|
|
|
|
|
if piece_texture.texture == null:
|
2026-07-02 20:58:21 +02:00
|
|
|
return false
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
|
|
func get_button_center(button: Button) -> Vector2:
|
|
|
|
|
return button.global_position + (button.size / 2)
|
|
|
|
|
|
|
|
|
|
func animate_win_line(combo: Array):
|
|
|
|
|
var start_pos = get_button_center(buttons[combo[0]])
|
|
|
|
|
var end_pos = get_button_center(buttons[combo[2]])
|
|
|
|
|
var direction = (end_pos - start_pos).normalized()
|
2026-07-03 21:35:29 +02:00
|
|
|
var padding = 40.0
|
2026-07-02 20:58:21 +02:00
|
|
|
var extended_start = start_pos - (direction * padding)
|
|
|
|
|
var extended_end = end_pos + (direction * padding)
|
|
|
|
|
|
|
|
|
|
win_line.clear_points()
|
|
|
|
|
win_line.add_point(extended_start)
|
2026-07-03 21:35:29 +02:00
|
|
|
win_line.add_point(extended_start)
|
2026-07-02 20:58:21 +02:00
|
|
|
|
|
|
|
|
var tween = create_tween()
|
|
|
|
|
tween.tween_method(
|
|
|
|
|
func(pos): win_line.set_point_position(1, pos),
|
|
|
|
|
extended_start,
|
2026-07-03 21:35:29 +02:00
|
|
|
extended_end,
|
2026-07-02 20:58:21 +02:00
|
|
|
0.4
|
|
|
|
|
).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_OUT)
|