diff --git a/Systems/Game/ChessGame.gd b/Systems/Game/ChessGame.gd index 7e477b2..39154dc 100644 --- a/Systems/Game/ChessGame.gd +++ b/Systems/Game/ChessGame.gd @@ -1,11 +1,17 @@ class_name ChessGame extends Control +# Constants const PieceContainer = preload("res://Systems/PieceContainer.gd") const WHITE = "white" const BLACK = "black" +const StockfishController = preload("res://Systems/FairyStockfish/StockfishClient.gd") + +# Signals signal tile_pressed(location: String) signal send_location(location: String) signal turn_changed + +# Game state variables var currentPlayer: String = WHITE var board: Array var isWhiteToMove: bool = true @@ -26,9 +32,15 @@ var currentlyMovingPiece = null var p1Points: int = 0 var p2Points: int = 0 var Turn: int = 0 -const StockfishController = preload("res://Systems/FairyStockfish/StockfishClient.gd") var stockfishController: StockfishController var stockfishPath = "res://Assets/ChessEngines/stockfish/stockfish.exe" +var currentFen = "" +var lightStyle = null +var darkStyle = null +var highlightStyle = null +var cpuElo = 1500 + +# Node references @onready var turnIndicator: ColorRect = $TurnIndicator @onready var p1String: RichTextLabel = $Player1Points @onready var p2String: RichTextLabel = $Player2Points @@ -37,7 +49,10 @@ var stockfishPath = "res://Assets/ChessEngines/stockfish/stockfish.exe" @onready var cameraController: CameraController @onready var cardDisplay: CardDisplay @onready var cardPreview: CardPreview +@onready var boardContainer: FlowContainer = $Flow +@onready var stateMachine: StateMachine = $StateMachine +# Export parameters @export var boardXSize = 12 @export var boardYSize = 12 @export var tileXSize: int = 50 @@ -45,21 +60,16 @@ var stockfishPath = "res://Assets/ChessEngines/stockfish/stockfish.exe" @export var windowXSize: int = 1280 @export var windowYSize: int = 720 @export var FEN: String = "2rnbqkbnr2/2pppppppp2/6u5/12/2********U1/12/2u9/12/7U4/12/2PPPPPPPP2/2RNBQKBNR2 w KQkq - 0 1" -# "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" -# "2rnbqkbnr2/2pppppppp2/12/12/2********2/12/12/12/12/12/2PPPPPPPP2/2RNBQKBNR2 w KQkq - 0 1" +# Standard chess FEN: "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" +# Modified FEN: "2rnbqkbnr2/2pppppppp2/12/12/2********2/12/12/12/12/12/2PPPPPPPP2/2RNBQKBNR2 w KQkq - 0 1" -@onready var boardContainer: FlowContainer = $Flow -@onready var stateMachine: StateMachine = $StateMachine - -var currentFen = "" -var lightStyle = null - -var darkStyle = null -var highlightStyle = null -var cpuElo = 1500 +# =========================================================================== +# INITIALIZATION FUNCTIONS +# =========================================================================== func _ready() -> void: - print("Initi Local Stockfish") + print("Initialize Local Stockfish") + # Set up the stockfish path based on OS if OS.get_name() == "Windows": stockfishPath = "res://Assets/ChessEngines/stockfish/stockfish.exe" else: @@ -67,9 +77,12 @@ func _ready() -> void: add_to_group("ChessGame") currentFen = FEN DisplayServer.window_set_size(Vector2i(windowXSize, windowYSize)) + # Initialize the game components initializeGame() initializeTiles() + # Start the state machine stateMachine.transitionToNextState(Constants.WHITE_TURN) + # Initialize Stockfish controller stockfishController = StockfishController.new() add_child(stockfishController) stockfishController.connect_to_engine(stockfishPath, self) @@ -78,35 +91,12 @@ func _ready() -> void: func _exit_tree(): + # Clean up the Stockfish controller when exiting stockfishController.disconnect_engine() -func initializeTiles() -> void: - tileManager = TileManager.new($Flow, self) - add_child(tileManager) - await get_tree().process_frame - tileManager.initialize(boardContainer) - setupTilesFromFEN() - # tileManager.place_random_game_tiles() - -func get_base_style(is_white: bool) -> StyleBoxFlat: - return lightStyle if is_white else darkStyle - -func _unhandled_input(event: InputEvent) -> void: - stateMachine.unhandledInput(event) - if event is InputEventKey: - if event.pressed: - if event.keycode == KEY_EQUAL or event.keycode == KEY_PLUS: # Zoom in with + key - cameraController.zoom_in(get_viewport().get_mouse_position()) - elif event.keycode == KEY_MINUS: # Zoom out with - key - cameraController.zoom_out(get_viewport().get_mouse_position()) - elif event.keycode == KEY_R: # Reset view with R key - cameraController.reset_view() - - -func _process(delta: float) -> void: - stateMachine.process(delta) func initializeGame() -> void: + # Initialize all game components in the correct order setupStyles() initializeDeckSystem() initializeCardPreview() @@ -114,13 +104,72 @@ func initializeGame() -> void: setupCameraController() setupUI() +func initializeTiles() -> void: + # Initialize tile manager and setup tiles + tileManager = TileManager.new($Flow, self) + add_child(tileManager) + await get_tree().process_frame + tileManager.initialize(boardContainer) + setupTilesFromFEN() + # tileManager.place_random_game_tiles() + + +func initializeBoard() -> void: + # Parse FEN to get board dimensions + var fen_parts = FEN.split(" ") + var rows = fen_parts[0].split("/") + boardYSize = rows.size() + boardXSize = 0 + + # Calculate width from first row by counting both pieces and numbers + for c in rows[0]: + if c.is_valid_int(): + boardXSize += int(c) # Add the number of empty squares + else: + boardXSize += 1 + + # Initialize board array + board = [] + for i in range(boardYSize): + var row = [] + for j in range(boardXSize): + row.append(null) + board.append(row) + + # Create the visual board and setup pieces + createBoard() + setupPiecesFromFEN() + # Set up signals for board interaction + if !boardContainer.has_user_signal("tile_pressed"): + boardContainer.add_user_signal("tile_pressed") + if !boardContainer.has_user_signal("send_location"): + boardContainer.add_user_signal("send_location") + func initializeCardPreview() -> void: + # Initialize the card preview component cardPreview = CardPreview.new() add_child(cardPreview) +func initializeDeckSystem() -> void: + # Initialize the deck manager and card display + deckManager = DeckManager.new() + cardDisplay = CardDisplay.new() + add_child(deckManager) + add_child(cardDisplay) + # Set up signals for card interaction + if !deckManager.has_user_signal("card_pressed"): + deckManager.add_user_signal("card_pressed") + + if !deckManager.has_user_signal("hand_updated"): + deckManager.add_user_signal("hand_updated") + + # Update card display with initial hand + cardDisplay.update_hand(deckManager.hand) + deckManager.connect("hand_updated", func(hand): cardDisplay.update_hand(hand)) func setupCameraController() -> void: + # Set up the camera controller for board navigation cameraController = CameraController.new(boardContainer) cameraController.name = "CameraController" add_child(cameraController) @@ -133,45 +182,476 @@ func _on_zoom_changed(zoom_level: float) -> void: pass -func getSpecialTilesFen() -> String: - if tileManager.active_tiles.is_empty(): - return "" - - var special_fen = " moves" - var portal_pairs = {} + +func setupStyles() -> void: + lightStyle = StyleBoxFlat.new() + lightStyle.bg_color = Utils.LIGHT_CELL + darkStyle = StyleBoxFlat.new() + darkStyle.bg_color = Utils.DARK_CELL - for pos in tileManager.active_tiles: - var tile = tileManager.active_tiles[pos] - if tile is PortalTile: - var pair_id = tile.pair_id - if !portal_pairs.has(pair_id): - portal_pairs[pair_id] = [] - portal_pairs[pair_id].append(pos) + highlightStyle = StyleBoxFlat.new() + highlightStyle.bg_color = Color(0, 0.3, 0, 1) + +func setupUI() -> void: + p1String.text = "0" + p2String.text = "0" + updateTurnIndicator() - # Second pass: Add walls and portal pairs to FEN - for pos in tileManager.active_tiles: - var tile = tileManager.active_tiles[pos] - var algebraic = Utils.location_to_algebraic(pos) + # Create control buttons + var zoom_in_button = Button.new() + zoom_in_button.text = "+" + zoom_in_button.size = Vector2(30, 30) + zoom_in_button.pressed.connect(func(): cameraController.zoom_in(zoom_in_button.global_position)) + + var zoom_out_button = Button.new() + zoom_out_button.text = "-" + zoom_out_button.size = Vector2(30, 30) + zoom_out_button.pressed.connect(func(): cameraController.zoom_out(zoom_out_button.global_position)) + + var reset_button = Button.new() + reset_button.text = "Reset View" + reset_button.size = Vector2(100, 30) + reset_button.pressed.connect(func(): cameraController.reset_view()) + + var control_container = VBoxContainer.new() + control_container.name = "ZoomControls" + + control_container.position = Vector2(windowXSize - 350, windowYSize - 120) + + var h_container = HBoxContainer.new() + h_container.add_child(zoom_out_button) + h_container.add_child(zoom_in_button) + + control_container.add_child(h_container) + control_container.add_child(reset_button) + + # Add the container to the scene + add_child(control_container) + + +func get_base_style(is_white: bool) -> StyleBoxFlat: + return lightStyle if is_white else darkStyle + + +# =========================================================================== +# BOARD CREATION AND SETUP +# =========================================================================== + +func createBoard() -> void: + boardContainer.add_to_group("Flow") + var numberX = 0 + var numberY = 0 + var isWhite = true + + while numberY != boardYSize: + boardContainer.size.y += tileYSize + 5 + boardContainer.size.x += tileXSize + 5 - if tile is WallTile or tile is DoubleWallTile: - if tile is DoubleWallTile: - special_fen += " @%s" % algebraic + while numberX != boardXSize: + createTile(numberX, numberY, isWhite) + isWhite = !isWhite + numberX += 1 + + isWhite = !isWhite + numberY += 1 + numberX = 0 + +func createTile(x: int, y: int, isWhite: bool) -> void: + # Create a single tile for the chess board + var tile = PieceContainer.new(str(x) + "-" + str(y)) + tile.set_custom_minimum_size(Vector2(tileXSize, tileYSize)) + tile.add_theme_stylebox_override("normal", lightStyle if isWhite else darkStyle) + + tile.set_name(str(x) + "-" + str(y)) + # tile.pressed.connect(func(): handleTileSelection(tile.name)) + tile.pressed.connect(func(): + # tile_pressed.emit(tile.name) + boardContainer.emit_signal("tile_pressed", tile.name) + ) + + boardContainer.add_child(tile) + +func setupPiecesFromFEN() -> void: + # Set up chess pieces from the FEN string + var fen_parts = FEN.split(" ") + var rows = fen_parts[0].split("/") + + # Iterate through rows in reverse to place black pieces at top + for y in range(rows.size()): + var x = 0 + # Convert y coordinate to flip the board (boardY-y puts white at bottom) + var board_y = (boardYSize - 1) - y + for c in rows[y]: + if c.is_valid_int(): + # Skip empty squares + x += int(c) else: - special_fen += " @%s" % algebraic - # elif tile is PortalTile: - # # Only process each portal pair once - # var pair_id = tile.pair_id - # if portal_pairs.has(pair_id): - # var pair = portal_pairs[pair_id] - # if pair.size() == 2: - # var alg1 = Utils.location_to_algebraic(pair[0]) - # var alg2 = Utils.location_to_algebraic(pair[1]) - # special_fen += " portal %s=p1 portal %s=p2" % [alg1, alg2] - # portal_pairs.erase(pair_id) # Process each pair only once + var piece_info = getFENPieceInfo(c) + if piece_info.name != "": + placePiece(str(x) + "-" + str(board_y), piece_info.name, piece_info.color) + x += 1 + +func setupTilesFromFEN() -> void: + # Set up special tiles from the FEN string + var fen_parts = FEN.split(" ") + var rows = fen_parts[0].split("/") + var matchedPortals = {} + var portalCnt = 0; - return special_fen + # Iterate through rows in reverse to place black pieces at top + for y in range(rows.size()): + var x = 0 + # Convert y coordinate to flip the board (7-y puts white at bottom) + var board_y = (boardYSize - 1) - y # For an 8x8 board + for c in rows[y]: + if c.is_valid_int(): + # Skip empty squares + x += int(c) + else: + var loc = str(x) + "-" + str(board_y); + if tileManager.portalString.find(c) > -1: + var char = c + # shjould we lowercase? + if char in matchedPortals: + if !("p2" in matchedPortals[char]): + matchedPortals[char].p2 = loc + tileManager.place_portal_pair(matchedPortals[char].p1, matchedPortals[char].p2, portalCnt) + portalCnt += 1; + else: + matchedPortals[char] = { "p1": loc } + x += 1 + else: + var tile = getFENTile(c, loc) + if tile != null: + tileManager.add_tile(loc, tile) + x += 1 + +func placePiece(position: String, pieceName: String, color: int) -> void: + var piece = summonPiece(pieceName, color) + var container = boardContainer.get_node(position) as PieceContainer + await container.set_piece(piece, false) + container.remove_piece(true) + container.set_piece(piece, false) + + var coords = position.split("-") + board[int(coords[1])][int(coords[0])] = piece + + +func summonPiece(pieceName: String, color: int) -> Node: + # Create a chess piece based on its name and color + var piece + match pieceName: + "Pawn": + piece = Pawn.new(self) + piece.name = "Pawn" + "King": + piece = King.new(self) + piece.name = "King" + "Queen": + piece = Queen.new(self) + piece.name = "Queen" + "Knight": + piece = Knight.new(self) + piece.name = "Knight" + "Rook": + piece = Rook.new(self) + piece.name = "Rook" + "Bishop": + piece = Bishop.new(self) + piece.name = "Bishop" + + piece.Item_Color = color + piece.position = Vector2(tileXSize / 2, tileYSize / 2) + return piece + +# =========================================================================== +# GAME FLOW AND STATE MANAGEMENT +# =========================================================================== + +func isPlayerTurn() -> bool: + return currentPlayer == WHITE + +func _process(delta: float) -> void: + # Process the state machine + stateMachine.process(delta) + +func _unhandled_input(event: InputEvent) -> void: + # Handle input events not handled by other nodes + stateMachine.unhandledInput(event) + if event is InputEventKey: + if event.pressed: + if event.keycode == KEY_EQUAL or event.keycode == KEY_PLUS: # Zoom in with + key + cameraController.zoom_in(get_viewport().get_mouse_position()) + elif event.keycode == KEY_MINUS: # Zoom out with - key + cameraController.zoom_out(get_viewport().get_mouse_position()) + elif event.keycode == KEY_R: # Reset view with R key + cameraController.reset_view() + + +func resetBoard() -> void: + clearSelection() + clearBoard() + setupPiecesFromFEN() + Turn = 0 + currentPlayer = WHITE + p1Points = 0 + p1String.text = str(p1Points) + p2Points = 0 + p2String.text = str(p2Points) + gamecheckMate = false; + gamedraw = false; + deckManager.initializeStartingDeck() + + areas.clear() + specialArea.clear() + updateTurnIndicator() + + + +func clearBoard() -> void: + # Clear all pieces from the board + for child in boardContainer.get_children(): + if child is PieceContainer: + child.remove_piece() + +func updateTurnIndicator(): + if Turn == 0: # White's turn + turnIndicator.color = Color(1, 1, 1, 1) # White + else: # Black's turn + turnIndicator.color = Color(0, 0, 0, 1) # Black + +func cleanupPhase() -> void: + pass + + +func updateEffectDurations() -> void: + # Update the duration of active effects + deckManager.updateCardDurations() + tileManager.update_tile_durations() + +func applyTileEffects() -> void: + # Apply effects from active tiles + tileManager.apply_alltile_effects() + + +func applyCardEffects() -> void: + pass + +func evaluatePosition() -> Dictionary: + var status = { + "checkmate": isCheckmate(), + "draw": isDraw(), + } + return status + + +func isCheckmate() -> bool: + return gamecheckMate + +func isDraw() -> bool: + return gamedraw + +func endGame(reason: String) -> void: + pass + + + + + +# =========================================================================== +# MOVE EXECUTION AND VALIDATION +# =========================================================================== + + +func executeMove(targetLocation: String) -> void: + print("executeMove ", targetLocation) + var targetContainer = get_node("Flow/" + targetLocation) as PieceContainer + var sourceContainer = get_node("Flow/" + selectedNode) as PieceContainer + var piece = sourceContainer.get_piece() + var old_location = selectedNode + + # Handle capture if there's a piece in target location + if targetContainer.has_piece(): + var capturedPiece = targetContainer.get_piece() + if Turn == 0: + p1Points += capturedPiece.Points + p1String.text = str(p1Points) + else: + p2Points += capturedPiece.Points + p2String.text = str(p2Points) + targetContainer.remove_piece() # This handles freeing the captured piece + + # Move piece to new location + sourceContainer.remove_piece(true) + targetContainer.set_piece(piece) + + hasMoved = true + currentlyMovingPiece = piece + resetHighlights() + + +func resolveMoveEffects() -> void: + # Resolve effects after a move is made + print("resolveMoveEffects", currentlyMovingPiece) + togglePieceChessEffect() + + selectedNode = "" + Turn = 1 if Turn == 0 else 0 + updateTurnIndicator() + resetHighlights() + hasMoved = false + currentlyMovingPiece = null + emit_signal("turn_changed") + + +func togglePieceChessEffect() -> void: + # Update piece-specific effects after a move + var piece = currentlyMovingPiece + if piece.name == "Pawn": + if piece.Double_Start: + piece.En_Passant = true + piece.Double_Start = false + elif piece.name == "King": + piece.Castling = false + elif piece.name == "Rook": + piece.Castling = false + + + +func isValidMove(location: String) -> bool: + # Check if a move to the given location is valid + var node = get_node("Flow/" + location) as PieceContainer + var piece = node.get_piece() + if piece == null || piece.Item_Color != Turn: + for area in areas: + if area == node.name: + return true + return false + + +func clearSelection() : + # Clear the current selection + resetHighlights() + selectedNode = "" + cardPreview.hide_preview() + return + + + +func getMovableAreas() -> void: + # Get all valid moves for the selected piece + print("HIGHLIGHTING getMovableAreas 1", selectedNode) + resetHighlights() + areas.clear() + specialArea.clear() + var container = get_node("Flow/" + selectedNode) as PieceContainer + var piece = container.get_piece() + if piece == null: + return + var piece_id = piece.get_instance_id() + # print("HIGHLIGHTING getMovableAreas 2") + if deckManager.attached_cards.has(piece_id): + var card = deckManager.attached_cards[piece_id] + cardPreview.show_card_preview(card) + if stateMachine.state.name == Constants.MOVEMENT: + var movement_state = stateMachine.state + if piece_id in movement_state.moves_remaining: + var moves_left = movement_state.moves_remaining[piece_id] - 1 + cardPreview.update_moves_remaining(moves_left) + else: + cardPreview.hide_preview() + # print("HIGHLIGHTING getMovableAreas 3") + var moves = piece.getValidMoves(boardContainer, selectedNode) + areas = moves.regular_moves + specialArea = moves.special_moves + # print("HIGHLIGHTING getMovableAreas 4") + highlightValidMoves() + +func highlightValidMoves() -> void: + for move in areas: + var button = boardContainer.get_node(move) + + # If there's an active tile effect, combine with its current style instead + if tileManager && tileManager.get_tile(move): + var current_style = button.get_theme_stylebox("normal") + var highlightedStyle = StyleBoxFlat.new() + highlightedStyle.bg_color = current_style.bg_color + highlightStyle.bg_color + button.add_theme_stylebox_override("normal", highlightedStyle) + else: + # Default chess pattern highlighting + var isWhiteSquare = (int(move.split("-")[0]) + int(move.split("-")[1])) % 2 == 0 + var baseStyle = lightStyle if isWhiteSquare else darkStyle + var combinedStyle = StyleBoxFlat.new() + combinedStyle.bg_color = baseStyle.bg_color + highlightStyle.bg_color + button.add_theme_stylebox_override("normal", combinedStyle) + + + +func resetHighlights(): + # Reset all highlights on the board + for button in boardContainer.get_children(): + if !button.name.contains("-"): + continue + + # Skip if this tile has an active effect + if tileManager && tileManager.get_tile(button.name): + continue + + var coord = button.name.split("-") + var isWhiteSquare = (int(coord[0]) + int(coord[1])) % 2 == 0 + if isWhiteSquare: + button.add_theme_stylebox_override("normal", lightStyle) + else: + button.add_theme_stylebox_override("normal", darkStyle) + + +# =========================================================================== +# CAPTURING AND PIECE MANAGEMENT +# =========================================================================== + + +func isNull(location: String) -> bool: + return get_node_or_null("Flow/" + location) == null + + +func updatePointsAndCapture(capturedPiece: Pawn, animate: bool = true) -> void: + # Update points and handle piece capture + if Turn == 0: + p1Points += capturedPiece.Points + p1String.text = str(p1Points) + else: + p2Points += capturedPiece.Points + p2String.text = str(p2Points) + if animate: + animatePieceCapture(capturedPiece) + + + if capturedPiece.name == "King": + print("Game Over!") + gamecheckMate = true + +func animatePieceCapture(capturedPiece: Pawn) -> void: + # Animate the capture of a piece + var container = capturedPiece.get_parent() as PieceContainer + await capturedPiece.animate_capture() + container.remove_piece() + +# =========================================================================== +# FEN NOTATION AND POSITION TRACKING +# =========================================================================== + + +func parseLocation(location: String) -> void: + # Parse a location string into X and Y coordinates + var number = 0 + locationX = "" + while location.substr(number, 1) != "-": + locationX += location.substr(number, 1) + number += 1 + locationY = location.substr(number + 1) func getCurrentFen() -> String: + # Generate FEN string from the current board position var fen = "" # For a standard chess board, we want to generate FEN from top (black side, rank 8) @@ -228,250 +708,13 @@ func getCurrentFen() -> String: halfMoveClock, moveCount ] - var specialChars = getSpecialTilesFen(); - print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") - print(specialChars) - print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@") # fen += specialChars; currentFen = fen; return fen -func initializeBoard() -> void: - # Parse FEN to get board dimensions - # rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 - var fen_parts = FEN.split(" ") - var rows = fen_parts[0].split("/") - boardYSize = rows.size() - boardXSize = 0 - # Calculate width from first row by counting both pieces and numbers - for c in rows[0]: - if c.is_valid_int(): - boardXSize += int(c) # Add the number of empty squares - else: - boardXSize += 1 - - # Calculate board width by counting pieces and empty squares in first row - # Initialize board array - board = [] - for i in range(boardYSize): - var row = [] - for j in range(boardXSize): - row.append(null) - board.append(row) - - createBoard() - setupPiecesFromFEN() - - if !boardContainer.has_user_signal("tile_pressed"): - boardContainer.add_user_signal("tile_pressed") - if !boardContainer.has_user_signal("send_location"): - boardContainer.add_user_signal("send_location") - -func setupUI() -> void: - p1String.text = "0" - p2String.text = "0" - updateTurnIndicator() - - # Create control buttons - var zoom_in_button = Button.new() - zoom_in_button.text = "+" - zoom_in_button.size = Vector2(30, 30) - zoom_in_button.pressed.connect(func(): cameraController.zoom_in(zoom_in_button.global_position)) - - var zoom_out_button = Button.new() - zoom_out_button.text = "-" - zoom_out_button.size = Vector2(30, 30) - zoom_out_button.pressed.connect(func(): cameraController.zoom_out(zoom_out_button.global_position)) - - var reset_button = Button.new() - reset_button.text = "Reset View" - reset_button.size = Vector2(100, 30) - reset_button.pressed.connect(func(): cameraController.reset_view()) - - var control_container = VBoxContainer.new() - control_container.name = "ZoomControls" - - control_container.position = Vector2(windowXSize - 350, windowYSize - 120) - - var h_container = HBoxContainer.new() - h_container.add_child(zoom_out_button) - h_container.add_child(zoom_in_button) - - control_container.add_child(h_container) - control_container.add_child(reset_button) - - # Add the container to the scene - add_child(control_container) - - -func initializeDeckSystem() -> void: - deckManager = DeckManager.new() - cardDisplay = CardDisplay.new() - add_child(deckManager) - add_child(cardDisplay) - - if !deckManager.has_user_signal("card_pressed"): - deckManager.add_user_signal("card_pressed") - - if !deckManager.has_user_signal("hand_updated"): - deckManager.add_user_signal("hand_updated") - - cardDisplay.update_hand(deckManager.hand) - deckManager.connect("hand_updated", func(hand): cardDisplay.update_hand(hand)) - -func createBoard() -> void: - boardContainer.add_to_group("Flow") - var numberX = 0 - var numberY = 0 - var isWhite = true - - while numberY != boardYSize: - boardContainer.size.y += tileYSize + 5 - boardContainer.size.x += tileXSize + 5 - - while numberX != boardXSize: - createTile(numberX, numberY, isWhite) - isWhite = !isWhite - numberX += 1 - - isWhite = !isWhite - numberY += 1 - numberX = 0 - - -func createTile(x: int, y: int, isWhite: bool) -> void: - # print("CreateTile x ", x, " y ", y); - var tile = PieceContainer.new(str(x) + "-" + str(y)) - tile.set_custom_minimum_size(Vector2(tileXSize, tileYSize)) - tile.add_theme_stylebox_override("normal", lightStyle if isWhite else darkStyle) - - tile.set_name(str(x) + "-" + str(y)) - # tile.pressed.connect(func(): handleTileSelection(tile.name)) - tile.pressed.connect(func(): - # tile_pressed.emit(tile.name) - boardContainer.emit_signal("tile_pressed", tile.name) - ) - - boardContainer.add_child(tile) - -func clearSelection() : - resetHighlights() - selectedNode = "" - cardPreview.hide_preview() - return - - - -func updatePointsAndCapture(capturedPiece: Pawn, animate: bool = true) -> void: - if Turn == 0: - p1Points += capturedPiece.Points - p1String.text = str(p1Points) - else: - p2Points += capturedPiece.Points - p2String.text = str(p2Points) - if animate: - animatePieceCapture(capturedPiece) - - - if capturedPiece.name == "King": - print("Game Over!") - gamecheckMate = true - -func animatePieceCapture(capturedPiece: Pawn) -> void: - var container = capturedPiece.get_parent() as PieceContainer - await capturedPiece.animate_capture() - container.remove_piece() - -func parseLocation(location: String) -> void: - var number = 0 - locationX = "" - while location.substr(number, 1) != "-": - locationX += location.substr(number, 1) - number += 1 - locationY = location.substr(number + 1) - - -func setupStyles() -> void: - lightStyle = StyleBoxFlat.new() - lightStyle.bg_color = Utils.LIGHT_CELL - darkStyle = StyleBoxFlat.new() - darkStyle.bg_color = Utils.DARK_CELL - - highlightStyle = StyleBoxFlat.new() - highlightStyle.bg_color = Color(0, 0.3, 0, 1) - -func updateTurnIndicator(): - if Turn == 0: # White's turn - turnIndicator.color = Color(1, 1, 1, 1) # White - else: # Black's turn - turnIndicator.color = Color(0, 0, 0, 1) # Black - - -func setupPiecesFromFEN() -> void: - var fen_parts = FEN.split(" ") - var rows = fen_parts[0].split("/") - - # Iterate through rows in reverse to place black pieces at top - for y in range(rows.size()): - var x = 0 - # Convert y coordinate to flip the board (7-y puts white at bottom) - var board_y = (boardYSize - 1) - y # For an 8x8 board - for c in rows[y]: - if c.is_valid_int(): - # Skip empty squares - x += int(c) - else: - var piece_info = getFENPieceInfo(c) - if piece_info.name != "": - placePiece(str(x) + "-" + str(board_y), piece_info.name, piece_info.color) - x += 1 - -func setupTilesFromFEN() -> void: - var fen_parts = FEN.split(" ") - var rows = fen_parts[0].split("/") - var matchedPortals = {} - var portalCnt = 0; - - # Iterate through rows in reverse to place black pieces at top - for y in range(rows.size()): - var x = 0 - # Convert y coordinate to flip the board (7-y puts white at bottom) - var board_y = (boardYSize - 1) - y # For an 8x8 board - for c in rows[y]: - if c.is_valid_int(): - # Skip empty squares - x += int(c) - else: - var loc = str(x) + "-" + str(board_y); - if tileManager.portalString.find(c) > -1: - var char = c - # shjould we lowercase? - if char in matchedPortals: - if !("p2" in matchedPortals[char]): - matchedPortals[char].p2 = loc - tileManager.place_portal_pair(matchedPortals[char].p1, matchedPortals[char].p2, portalCnt) - portalCnt += 1; - else: - matchedPortals[char] = { "p1": loc } - x += 1 - else: - var tile = getFENTile(c, loc) - if tile != null: - tileManager.add_tile(loc, tile) - x += 1 - -func getFENTile(fen_char: String, location: String) -> Tile: - var tile = null - var container = boardContainer.get_node(location) as PieceContainer - - var is_white = (int(location.split("-")[0]) + int(location.split("-")[1])) % 2 == 0 - # Map FEN characters to piece names - match fen_char.to_upper(): - "*": tile = WallTile.new(container, is_white, -1) - - return tile func getFENPieceInfo(fen_char: String) -> Dictionary: + # Get piece information from a FEN character var piece_info = { "name": "", "color": 0 # Black @@ -492,232 +735,23 @@ func getFENPieceInfo(fen_char: String) -> Dictionary: return piece_info -func placePiece(position: String, pieceName: String, color: int) -> void: - var piece = summonPiece(pieceName, color) - var container = boardContainer.get_node(position) as PieceContainer - await container.set_piece(piece, false) - container.remove_piece(true) - container.set_piece(piece, false) - - var coords = position.split("-") - board[int(coords[1])][int(coords[0])] = piece -func summonPiece(pieceName: String, color: int) -> Node: - var piece - match pieceName: - "Pawn": - piece = Pawn.new(self) - piece.name = "Pawn" - "King": - piece = King.new(self) - piece.name = "King" - "Queen": - piece = Queen.new(self) - piece.name = "Queen" - "Knight": - piece = Knight.new(self) - piece.name = "Knight" - "Rook": - piece = Rook.new(self) - piece.name = "Rook" - "Bishop": - piece = Bishop.new(self) - piece.name = "Bishop" - - piece.Item_Color = color - piece.position = Vector2(tileXSize / 2, tileYSize / 2) - return piece - -func clearBoard() -> void: - for child in boardContainer.get_children(): - if child is PieceContainer: - child.remove_piece() - - -# func prepareHand() -> void: -# if deckManager.hand.size() < 5: -# deckManager.drawCard() -# pass - -func updateEffectDurations() -> void: - deckManager.updateCardDurations() - tileManager.update_tile_durations() - -func applyTileEffects() -> void: - tileManager.apply_alltile_effects() - - -func applyCardEffects() -> void: - pass - -func evaluatePosition() -> Dictionary: - var status = { - "checkmate": isCheckmate(), - "draw": isDraw(), - } - return status - -func isValidMove(location: String) -> bool: - var node = get_node("Flow/" + location) as PieceContainer - var piece = node.get_piece() - if piece == null || piece.Item_Color != Turn: - for area in areas: - if area == node.name: - return true - return false - - -func resetBoard() -> void: - clearSelection() - clearBoard() - setupPiecesFromFEN() - Turn = 0 - currentPlayer = WHITE - p1Points = 0 - p1String.text = str(p1Points) - p2Points = 0 - p2String.text = str(p2Points) - gamecheckMate = false; - gamedraw = false; - deckManager.initializeStartingDeck() - - areas.clear() - specialArea.clear() - updateTurnIndicator() - -func getMovableAreas() -> void: - print("HIGHLIGHTING getMovableAreas 1", selectedNode) - resetHighlights() - areas.clear() - specialArea.clear() - var container = get_node("Flow/" + selectedNode) as PieceContainer - var piece = container.get_piece() - if piece == null: - return - var piece_id = piece.get_instance_id() - # print("HIGHLIGHTING getMovableAreas 2") - if deckManager.attached_cards.has(piece_id): - var card = deckManager.attached_cards[piece_id] - cardPreview.show_card_preview(card) - if stateMachine.state.name == Constants.MOVEMENT: - var movement_state = stateMachine.state - if piece_id in movement_state.moves_remaining: - var moves_left = movement_state.moves_remaining[piece_id] - 1 - cardPreview.update_moves_remaining(moves_left) - else: - cardPreview.hide_preview() - # print("HIGHLIGHTING getMovableAreas 3") - var moves = piece.getValidMoves(boardContainer, selectedNode) - areas = moves.regular_moves - specialArea = moves.special_moves - # print("HIGHLIGHTING getMovableAreas 4") - highlightValidMoves() - -func highlightValidMoves() -> void: - for move in areas: - var button = boardContainer.get_node(move) - - # If there's an active tile effect, combine with its current style instead - if tileManager && tileManager.get_tile(move): - var current_style = button.get_theme_stylebox("normal") - var highlightedStyle = StyleBoxFlat.new() - highlightedStyle.bg_color = current_style.bg_color + highlightStyle.bg_color - button.add_theme_stylebox_override("normal", highlightedStyle) - else: - # Default chess pattern highlighting - var isWhiteSquare = (int(move.split("-")[0]) + int(move.split("-")[1])) % 2 == 0 - var baseStyle = lightStyle if isWhiteSquare else darkStyle - var combinedStyle = StyleBoxFlat.new() - combinedStyle.bg_color = baseStyle.bg_color + highlightStyle.bg_color - button.add_theme_stylebox_override("normal", combinedStyle) - -func executeMove(targetLocation: String) -> void: - print("executeMove ", targetLocation) - var targetContainer = get_node("Flow/" + targetLocation) as PieceContainer - var sourceContainer = get_node("Flow/" + selectedNode) as PieceContainer - var piece = sourceContainer.get_piece() - var old_location = selectedNode +func getFENTile(fen_char: String, location: String) -> Tile: + # Get tile information from a FEN character + var tile = null + var container = boardContainer.get_node(location) as PieceContainer - # Handle capture if there's a piece in target location - if targetContainer.has_piece(): - var capturedPiece = targetContainer.get_piece() - if Turn == 0: - p1Points += capturedPiece.Points - p1String.text = str(p1Points) - else: - p2Points += capturedPiece.Points - p2String.text = str(p2Points) - targetContainer.remove_piece() # This handles freeing the captured piece + var is_white = (int(location.split("-")[0]) + int(location.split("-")[1])) % 2 == 0 + # Map FEN characters to piece names + match fen_char.to_upper(): + "*": tile = WallTile.new(container, is_white, -1) - # Move piece to new location - sourceContainer.remove_piece(true) - targetContainer.set_piece(piece) - - hasMoved = true - currentlyMovingPiece = piece - resetHighlights() - -func togglePieceChessEffect() -> void: - var piece = currentlyMovingPiece - if piece.name == "Pawn": - if piece.Double_Start: - piece.En_Passant = true - piece.Double_Start = false - elif piece.name == "King": - piece.Castling = false - elif piece.name == "Rook": - piece.Castling = false - -func resolveMoveEffects() -> void: - print("resolveMoveEffects", currentlyMovingPiece) - togglePieceChessEffect() - - selectedNode = "" - Turn = 1 if Turn == 0 else 0 - updateTurnIndicator() - resetHighlights() - hasMoved = false - currentlyMovingPiece = null - emit_signal("turn_changed") - -func isPlayerTurn() -> bool: - return currentPlayer == WHITE - -func resetHighlights(): - for button in boardContainer.get_children(): - if !button.name.contains("-"): - continue - - # Skip if this tile has an active effect - if tileManager && tileManager.get_tile(button.name): - continue - - var coord = button.name.split("-") - var isWhiteSquare = (int(coord[0]) + int(coord[1])) % 2 == 0 - if isWhiteSquare: - button.add_theme_stylebox_override("normal", lightStyle) - else: - button.add_theme_stylebox_override("normal", darkStyle) - - -func isCheckmate() -> bool: - return gamecheckMate - -func isDraw() -> bool: - return gamedraw - -func endGame(reason: String) -> void: - pass - -func cleanupPhase() -> void: - pass - -func isNull(location: String) -> bool: - return get_node_or_null("Flow/" + location) == null + return tile func getPieceFenChar(piece: Pawn) -> String: + # Get FEN character from a piece if piece == null: return "" var fenChar = "" @@ -732,6 +766,7 @@ func getPieceFenChar(piece: Pawn) -> String: # In our system, Item_Color == 1 is white, 0 is black return fenChar.to_upper() if piece.Item_Color == 0 else fenChar +# Unused func updateStateFromMove(fromIdx: int, toIdx: int) -> void: # Update game state based on the move isWhiteToMove = !isWhiteToMove @@ -796,18 +831,3 @@ func updateHalfMoveClock(fromIdx: int, toIdx: int) -> void: else: halfMoveClock += 1 - -func _on_ai_move_generated(move: String) -> void: - if Turn == 1: # Only process AI moves during black's turn - # The move will be automatically handled by the Movement state - # which is listening for the moveGenerated signal - pass - -func executeAiMove(fromLocation: String, toLocation: String) -> void: - selectedNode = fromLocation - executeMove(toLocation) - -func convertNotationToLocation(notation: String) -> String: - var file = notation[0].unicode_at(0) - 'a'.unicode_at(0) - var rank = 8 - int(notation[1]) - return str(file) + "-" + str(rank)