online multiplayer chess game (note server currently down)
Diffstat (limited to 'ui/board/Board.gd')
| -rw-r--r-- | ui/board/Board.gd | 471 |
1 files changed, 0 insertions, 471 deletions
diff --git a/ui/board/Board.gd b/ui/board/Board.gd deleted file mode 100644 index a72b4f3..0000000 --- a/ui/board/Board.gd +++ /dev/null @@ -1,471 +0,0 @@ -extends Control -class_name Grid - -const PieceScene := preload("res://piece/Piece.tscn") -const Square := preload("res://Square.tscn") - -### for the sandisplay -signal add_to_pgn(move) -signal clear_pgn -signal load_pgn(moves) -signal remove_last - -var move_indicators: PoolIntArray = [] - -var rot: float = 0 -var piece_size: Vector2 - -export(Color) var overlay_color: Color -export(Color) var premove_color: Color -export(Color) var last_move_indicator_color: Color -export(Color) var last_move_take_indicator: Color -export(Color) var clockrunning_color: Color -export(Color) var clockrunninglow: Color -export(Color) var clocklow: Color - -var board = [] # has `get_piece(algebraic position)` and `set_piece(algebraic position)` for ease of use - - -func get_piece(alg: String) -> Piece: - return board[Chess.SQUARE_MAP[alg]] - - -func set_piece(alg: String, p: Piece) -> void: - board[Chess.SQUARE_MAP[alg]] = p - - -var flipped := false -var labels := {numbers = [], letters = []} -var background_array := [] -var last_clicked: Piece -var premove: Dictionary = {} -var check_circle: GradientTexture2D = load("res://piece/check-circle.tres") -var take_circle: GradientTexture2D = load("res://piece/takeable-circle.tres") -var move_circle: GradientTexture2D = load("res://piece/move-circle.tres") - -onready var game: GameUI = owner if owner is GameUI else null -onready var sidebar := game.get_node_or_null("%Sidebar") if game else null -onready var darken = $Darken -onready var foreground := $Foreground -onready var background := $Background -onready var pieces := $Pieces -onready var arrows := $"%Arrows" - -var chess := Chess.new() -var local := false -var spectating := false -var team: String -var auto_change_team := false - - -func _init(): - Globals.grid = self - - -func _exit_tree(): - Globals.grid = null - - -func _process(_delta): - rect_rotation = rot - foreground.rect_rotation = rot - if Input.is_action_just_pressed("debug") and Debug.debug: - print(chess.ascii()) - - -func _resized(): - var old_pc = piece_size - piece_size = rect_size / 8 - piece_size.x = clamp(piece_size.x, 0, piece_size.y) - piece_size.y = clamp(piece_size.y, 0, piece_size.x) - check_circle.width = piece_size.x - check_circle.height = piece_size.y - take_circle.width = piece_size.x - take_circle.height = piece_size.y - move_circle.width = piece_size.x - move_circle.height = piece_size.y - if foreground: - rect_pivot_offset = (piece_size * 8) / 2 - foreground.rect_pivot_offset = rect_pivot_offset - if not (board.empty() && background_array.empty()) && piece_size != old_pc: - resize_board() - Log.debug("Resizing board") - - -func set_take_move_circle_color( - color: Color = Color(overlay_color.r, overlay_color.g, overlay_color.b, .65) -) -> void: - take_circle.gradient.colors[1] = color - move_circle.gradient.colors[0] = color - - -func _ready(): - set_take_move_circle_color() - _resized() - Events.connect("turn_over", self, "_on_turn_over") - PacketHandler.connect("move_data", self, "move", [false, false]) - create_pieces() - create_squares() - create_labels() - yield(get_tree(), "idle_frame") - if !team: - team = chess.turn - auto_change_team = true - Log.debug("board: ready") - - -func resize_board(): - resize_pieces() - - -func create_squares() -> void: # create the board - background_array.resize(128) - for i in Chess.SQUARE_MAP.values(): - var alg := Chess.algebraic(i) - var square := Square.instance() # create a square - square.name = alg - square.b = self - square.square = alg - square.hint_tooltip = alg - square.color = (Globals.board_color1 if Chess.square_color(alg) == "light" else Globals.board_color2) # set the color - background.add_child(square) # add the square to the background - square.connect("clicked", self, "square_clicked", [square]) # connect the clicked event - background_array[i] = square # add the square to the background array - arrows._setup(self) # initialize the arrows - - -func create_labels() -> void: - var font: DynamicFont = load("res://ui/ubuntu-bold-regular.tres").duplicate() - font.size = 15 - for k in Chess.SQUARE_MAP: - if k == "h1": - var l = init_label(font, k, k[0], VALIGN_BOTTOM, 0, false) - var n = init_label(font, k, k[1], 0, VALIGN_BOTTOM, false) - var h = HBoxContainer.new() - h.mouse_filter = MOUSE_FILTER_IGNORE - for i in [l, n]: - var ic = create_margin_container() - ic.add_child(i) - h.add_child(ic) - labels.numbers.append(n) - labels.letters.append(l) - foreground.add_child(h) - elif k[0] == "h": # file h contains numbers - labels.numbers.append(init_label(font, k, k[1], 0, VALIGN_BOTTOM)) - elif k[1] == "1": # rank 1 contains letters - labels.letters.append(init_label(font, k, k[0], VALIGN_BOTTOM)) - else: - var spacer = Control.new() - spacer.mouse_filter = MOUSE_FILTER_IGNORE - spacer.name = k + "_space" - spacer.size_flags_horizontal = SIZE_EXPAND_FILL - spacer.size_flags_vertical = SIZE_EXPAND_FILL - foreground.add_child(spacer) - - -func init_label(font: DynamicFont, alg: String, text: String, valign := 0, align := 0, add := true) -> Label: - var label := Label.new() - label.align = align - label.valign = valign - label.size_flags_horizontal = SIZE_EXPAND_FILL - label.size_flags_vertical = SIZE_EXPAND_FILL - label.mouse_filter = MOUSE_FILTER_IGNORE - label.name = text - label.text = text - label.add_color_override( - "font_color", Globals.board_color1 if Chess.square_color(alg) == "dark" else Globals.board_color2 - ) - label.add_font_override("font", font) - if add: - var container := create_margin_container() - container.add_child(label) - foreground.add_child(container) - return label - - -func create_margin_container(margin := 5) -> MarginContainer: - var c := MarginContainer.new() - c.add_constant_override("margin_top", margin) - c.add_constant_override("margin_left", margin) - c.add_constant_override("margin_right", margin) - c.add_constant_override("margin_bottom", margin) - c.size_flags_horizontal = SIZE_EXPAND_FILL - c.size_flags_vertical = SIZE_EXPAND_FILL - c.mouse_filter = MOUSE_FILTER_IGNORE - return c - - -func clear_pieces() -> void: - for i in Chess.SQUARE_MAP.values(): - var p: Piece = board[i] - if p: - p.queue_free() - board[i] = null - - -func resize_pieces(): - for i in Chess.SQUARE_MAP.values(): - var p: Piece = board[i] - if p: - p.size() - - -func create_pieces(): - board.resize(128) - for k in Chess.SQUARE_MAP: - var piece = chess.get(k) - if piece: - make_piece(k, piece.type, piece.color) - - -func make_piece(algebraic: String, piece_type: String, color := "w") -> void: # make peace - var piece := PieceScene.instance() # create a piece - piece.name = "%s-%s" % [piece_type, algebraic] - piece.b = self - piece.position = algebraic - piece.type = piece_type - piece.color = color - pieces.add_child(piece) # add the piece to the grid - set_piece(algebraic, piece) - - -func flip_pieces() -> void: - for i in Chess.SQUARE_MAP.values(): - var spot: Piece = board[i] - if spot: - spot.sprite.flip_v = flipped - spot.sprite.flip_h = flipped - - -func flip_labels() -> void: - for i in range(8): - var numlabel: Label = labels.numbers[i] - var letlabel: Label = labels.letters[i] - var number := i + 1 if flipped else 8 - i - numlabel.text = str(number) - letlabel.text = "hgfedcba"[number - 1] - - -func flip_board() -> void: - rot = 0 if rot == 180 else 180 - flipped = rot == 180 - Log.debug(["Flipped the board, now", "flipped" if flipped else "not flipped"]) - if sidebar: - sidebar.flip_panels() - flip_pieces() - flip_labels() - - -func is_my_turn() -> bool: - return team == chess.turn - - -func square_clicked(clicked_square: BackgroundSquare) -> void: - if Globals.spectating: - return - - var p := get_piece(clicked_square.square) - - if not is_my_turn() and is_instance_valid(last_clicked): - # PREMOVE AREA - var p_sq: int = Chess.SQUARE_MAP[clicked_square.square] - for m in chess.piece_moves(last_clicked.position, last_clicked.type, team): - if m.to == p_sq && m.from == Chess.SQUARE_MAP[last_clicked.position]: - if "from" in premove and "to" in premove: - background_array[premove.from].premove_indicator.hide() # hide premove indicators - background_array[premove.to].premove_indicator.hide() - if premove && premove.from == m.from && premove.to == m.to: - premove = {} - Log.debug("De-selected premove") - else: - premove = m - background_array[premove.from].premove_indicator.show() - background_array[premove.to].premove_indicator.show() - if premove.flags & Chess.BITS.PROMOTION: - p.open_promotion_previews(darken) - premove.promotion = yield(p, "promotion_decided") - Log.debug("Selected premove: %s" % premove) - clear_last_clicked() - return - elif (!p or p.color != team) and is_instance_valid(last_clicked): - # Attempt to make the move (NORMAL MOVE AREA) - for m in chess.moves({square = last_clicked.position, verbose = true}): - if m.to == clicked_square.square && m.from == last_clicked.position: - move(m.san) - clear_last_clicked() - return - - clear_last_clicked() - - if p and p.color == team: - if chess.turn != team: - if !Globals.premoves: - return - clicked_square.show_premove_indicators() - else: - clicked_square.show_move_indicators() - last_clicked = p - - -func move(san: String, send := true, create_promotion_input := true) -> void: - var sound_handled = false - var move_0x88 = chess.__move_from_san(san, true) - var valid_moves = chess.moves({square = chess.algebraic(move_0x88.from), stripped = true}) - if valid_moves.find(chess.stripped_san(san)) == -1: - Log.err("Invalid move " + san) - return - Log.debug("Making move " + san) - chess.__make_move(move_0x88) - if move_0x88.flags & Chess.BITS.CAPTURE: - board[move_0x88.to].took() - SoundFx.play("Capture") - sound_handled = true - elif move_0x88.flags & Chess.BITS.EP_CAPTURE: - var to_take := Chess.offset(move_0x88.to, Vector2(0, 1 * -1 if chess.turn == Chess.WHITE else 1)) - get_piece(to_take).took() - SoundFx.play("Capture") - sound_handled = true - elif move_0x88.flags & Chess.BITS.KSIDE_CASTLE: # kingside castling - var rook_pos := Chess.offset(move_0x88.to, Vector2(1, 0)) - get_piece(rook_pos).move(Chess.offset(move_0x88.to, Vector2(-1, 0))) - elif move_0x88.flags & Chess.BITS.QSIDE_CASTLE: # queenside - var rook_pos := Chess.offset(move_0x88.to, Vector2(-2, 0)) - get_piece(rook_pos).move(Chess.offset(move_0x88.to, Vector2(1, 0))) - if move_0x88.flags & Chess.BITS.PROMOTION: #promotion wow - var p: Piece = yield(board[move_0x88.from].move(Chess.algebraic(move_0x88.to), true), "completed") - if create_promotion_input: - p.open_promotion_previews(darken) - move_0x88.promotion = yield(p, "promotion_decided") - san = chess.__move_to_san(move_0x88) # update the san with new promotion data - p.queue_free() - else: # was opponents turn, this is opponents move: promotion is already chosen - p.queue_free() - make_piece(p.position, move_0x88.promotion, p.color) - SoundFx.play("Move" if move_0x88.flags & Chess.BITS.NORMAL else "Capture") - sound_handled = true - else: # not promotion: from **always** moves to `to` - var _p = board[move_0x88.from].move(Chess.algebraic(move_0x88.to)) - if send && !local: - PacketHandler.send_mov(san) - if !sound_handled: - SoundFx.play("Move") - emit_signal("add_to_pgn", san) - Events.emit_signal("turn_over") - - -func clear_last_clicked(): - last_clicked = null - - -func draw(reason := "") -> void: - var string = "draw by " + reason - game.set_status(string, 0) - SoundFx.play("Victory") - Events.emit_signal("game_over", string) - - -func win(winner: String, reason := "") -> void: - var string = "%s won the game by %s" % [Utils.expand_color(winner), reason] - game.set_status(string, 0) #: black won the game by checkmate - Events.emit_signal("game_over", string) - SoundFx.play("Victory") - - -func load_pgn(pgn: String) -> void: - chess.load_pgn(pgn, {sloppy = true}) - clear_pieces() - create_pieces() - emit_signal("clear_pgn") - var pgn_parser := PGN.new() - var movs: PoolStringArray = pgn_parser.parse(pgn).moves - emit_signal("load_pgn", movs) - Log.info("load pgn " + pgn) - Events.emit_signal("turn_over") - - -func undo(two: bool = false) -> void: - Globals.chat.server("undid move %s" % chess.undo().san) - emit_signal("remove_last") - if two: - Globals.chat.server("undid move %s" % chess.undo().san) - emit_signal("remove_last") - clear_pieces() - clear_last_clicked() - create_pieces() - Events.emit_signal("turn_over") - - -func auto_flip(): - if team == Chess.WHITE and flipped: - flip_board() - elif team == Chess.BLACK and not flipped: - flip_board() - - -func _on_turn_over(): - # if auto_change_team: - # team = chess.turn - # auto_flip() - - if is_my_turn(): - set_take_move_circle_color() - # use the premove if possible - if premove: - background_array[premove.from].premove_indicator.hide() - background_array[premove.to].premove_indicator.hide() - if board[premove.from]: # see if its valid - if premove.flags & (Chess.BITS.CAPTURE | Chess.BITS.EP_CAPTURE) && not board[premove.to]: - return - var san = chess.__move_to_san(premove, chess.__generate_moves({legal = true}), false) - if san: - var legal_moves = chess.moves({square = chess.algebraic(premove.from), stripped = true}) - var is_possible_move = legal_moves.find(chess.stripped_san(san)) != -1 - if chess.__move_from_san(san, true) and (is_possible_move): # it is valid - Log.debug(["Executing premove:", san]) - move(san, true, false) # make the move, send it to the opponent, dont prompt for premoves - premove = {} - elif Globals.premoves: - set_take_move_circle_color(premove_color) - SaveLoad.save("user://game.json", {pgn = chess.pgn(), fen = chess.fen()}) - clear_last_clicked() - check_game_over() - create_last_move_indicators() - - -func check_game_over(): - if chess.in_checkmate(): - # they won if its my turn, i won if its their turn. - win(team if is_my_turn() else Chess.__swap_color(team), "checkmate") - elif chess.half_moves >= 50: - draw("fifty move rule") - elif chess.in_stalemate(): - draw("stalemate") - elif chess.insufficient_material(): - draw("insufficient material") - elif chess.in_threefold_repetition(): - draw("threefold repetition") - - -func create_last_move_indicators(): - for i in move_indicators: - background_array[i].move_indicator.color = last_move_indicator_color - background_array[i].move_indicator.hide() - if !chess.__history: - return - var m: Dictionary = chess.__history[-1].move - background_array[m.from].move_indicator.show() - if m.flags & Chess.BITS.CAPTURE: - background_array[m.to].move_indicator.color = last_move_take_indicator - background_array[m.to].move_indicator.show() - move_indicators = [m.from, m.to] - - -func reload(): - premove = {} - chess = Chess.new() - clear_last_clicked() - clear_pieces() - create_pieces() - create_last_move_indicators() # it hides the indicators :/ - Events.emit_signal("turn_over") - emit_signal("clear_pgn") # clears the san displays |