online multiplayer chess game (note server currently down)
createfen system -> void
| -rw-r--r-- | ClickableSprite.gd | 8 | ||||
| -rw-r--r-- | Globals.gd | 33 | ||||
| -rw-r--r-- | Grid.gd | 68 | ||||
| -rw-r--r-- | Square.gd | 8 | ||||
| -rw-r--r-- | Utils.gd | 82 | ||||
| -rw-r--r-- | World.gd | 4 | ||||
| -rw-r--r-- | World.tscn | 4 | ||||
| -rw-r--r-- | pieces/Bishop.gd | 4 | ||||
| -rw-r--r-- | pieces/King.gd | 22 | ||||
| -rw-r--r-- | pieces/Knight.gd | 2 | ||||
| -rw-r--r-- | pieces/Pawn.gd | 33 | ||||
| -rw-r--r-- | pieces/Piece.gd | 53 | ||||
| -rw-r--r-- | pieces/Queen.gd | 2 | ||||
| -rw-r--r-- | pieces/Rook.gd | 4 | ||||
| -rw-r--r-- | project.godot | 6 | ||||
| -rw-r--r-- | saveload.gd | 11 | ||||
| -rw-r--r-- | sounds/SoundFX.gd | 2 | ||||
| -rw-r--r-- | ui/FENlabel.gd | 11 | ||||
| -rw-r--r-- | ui/GameUI.tscn | 43 | ||||
| -rw-r--r-- | ui/Lobby.gd | 28 | ||||
| -rw-r--r-- | ui/MovesList.gd | 10 | ||||
| -rw-r--r-- | ui/Settings.gd | 22 | ||||
| -rw-r--r-- | ui/StartMenu.gd | 14 | ||||
| -rw-r--r-- | ui/Status.gd | 2 | ||||
| -rw-r--r-- | ui/Timer.gd | 6 | ||||
| -rw-r--r-- | ui/TimerLabels.gd | 8 |
26 files changed, 301 insertions, 189 deletions
diff --git a/ClickableSprite.gd b/ClickableSprite.gd index 929c700..c99c396 100644 --- a/ClickableSprite.gd +++ b/ClickableSprite.gd @@ -7,20 +7,20 @@ var c = 0 onready var sprite = $Sprite -func _ready(): +func _ready() -> void: $Area2D/CollisionShape2D.shape.extents = Globals.grid.piece_size / 2 -func _on_Area2D_input_event(_viewport: Node, _event: InputEvent, _shape_idx: int): +func _on_Area2D_input_event(_viewport: Node, _event: InputEvent, _shape_idx: int) -> void: if visible and Input.is_action_just_released("click"): c += 1 if c >= 1: emit_signal("clicked", self) -func _on_Area2D_mouse_entered(): +func _on_Area2D_mouse_entered() -> void: sprite.scale = Vector2(1, 1) -func _on_Area2D_mouse_exited(): +func _on_Area2D_mouse_exited() -> void: sprite.scale = Vector2(1.2, 1.2) @@ -1,9 +1,12 @@ extends Node +var __nosethalfmove = false + +var pawns = [] # PoolPawnArray var grid: Grid = null var piece_set := "california" -var white_turns := 0 -var black_turns := 0 +var fullmove := 1 +var halfmove := 0 var in_check := false var checking_piece: Piece = null var white_king: King @@ -12,19 +15,23 @@ var turn := true # true for white, false for black # true cuz white goes first -func turns(winner): - if winner == "white": - return white_turns - elif winner == "black": - return black_turns +func turns(_winner) -> int: + return fullmove + + +func reset_halfmove() -> void: + halfmove = 0 + __nosethalfmove = true -func add_turn(): - if turn: - white_turns += 1 - else: - black_turns += 1 +func add_turn() -> void: + if !turn: + fullmove += 1 + if __nosethalfmove: + __nosethalfmove = false + return + halfmove += 1 -func _ready(): +func _ready() -> void: VisualServer.set_default_clear_color(Color.black) @@ -43,7 +43,7 @@ onready var pieces := $Pieces onready var status_label := $"../UI/Holder/Back/VBox/Status" -func _ready(): +func _ready() -> void: Globals.grid = self # tell the globals that this is the grid init_board() # create the tile squares init_matrix() # create the pieces @@ -52,11 +52,11 @@ func _ready(): Events.connect("outoftime", self, "_on_outoftime") # listen for timeout events -func _exit_tree(): +func _exit_tree() -> void: Globals.grid = null # reset the globals grid when leaving tree -func _input(event): # input +func _input(event) -> void: # input if event.is_action_released("debug"): # if debug print_matrix_pretty() # print the matrix if event.is_action_released("kill"): @@ -66,7 +66,7 @@ func _input(event): # input clear_fx() # clear the circles -func print_matrix_pretty(mat = matrix): # print the matrix +static func print_matrix_pretty(mat = matrix) -> void: # print the matrix for j in range(8): # for each row var r: Array = mat[j] # get the row if j == 0: @@ -84,14 +84,14 @@ func print_matrix_pretty(mat = matrix): # print the matrix print("%s\n%s\n%s" % [middish_heads, letter_header, smaller_heads]) -func reload_sprites(): +func reload_sprites() -> void: for i in range(8): for j in range(8): if matrix[i][j]: matrix[i][j].load_texture() -func init_labels(): +func init_labels() -> void: for i in range(8): var letterslabel := BottomLeftLabel.instance() letterslabel.rect_position.x = i * piece_size.x @@ -109,19 +109,19 @@ func init_labels(): foreground.add_child(numberslabel) -func size_label(label, i): +func size_label(label, i) -> void: label.rect_size = piece_size label.get_node("Label").add_color_override("font_color", board_color1 if i % 2 == 0 else board_color2) -func threefoldrepetition(): +func threefoldrepetition() -> bool: for i in history_matrixes.values(): if i >= 3: return true return false -func mat2str(mat = matrix): +func mat2str(mat = matrix) -> String: var string := "" for y in range(8): for x in range(8): @@ -136,24 +136,22 @@ func mat2str(mat = matrix): return string -func drawed(): +func drawed() -> void: Events.emit_signal("game_over") SoundFx.play("Draw") yield(get_tree().create_timer(5), "timeout") - get_parent().emit_signal("game_over") SoundFx.play("Victory") -func win(winner): +func win(winner) -> void: Events.emit_signal("game_over") print(winner, " won the game in ", Globals.turns(winner), " turns!") SoundFx.play("Victory") yield(get_tree().create_timer(5), "timeout") - get_parent().emit_signal("game_over") SoundFx.play("Victory") -func check_in_check(prin = false): # check if in_check +func check_in_check(prin = false) -> bool: # check if in_check for i in range(0, 8): # for each row for j in range(0, 8): # for each column var spot = matrix[i][j] # get the square @@ -167,7 +165,7 @@ func check_in_check(prin = false): # check if in_check return false -func can_move(): +func can_move() -> bool: for i in range(0, 8): # for each row for j in range(0, 8): # for each column var spot = matrix[i][j] # get the square @@ -177,7 +175,7 @@ func can_move(): return false -func init_matrix(): # create the matrix +func init_matrix() -> void: # create the matrix for i in range(8): # for each row matrix.append([]) # add a row for _j in range(8): # for each column @@ -186,7 +184,7 @@ func init_matrix(): # create the matrix add_pieces() # add the pieces -func make_piece(position: Vector2, script: String, white: bool = true): # make peace +func make_piece(position: Vector2, script: String, white: bool = true) -> void: # make peace var piece := Piece.instance() # create a piece piece.script = load(script) # set the script piece.real_position = position # set the real position @@ -196,7 +194,7 @@ func make_piece(position: Vector2, script: String, white: bool = true): # make matrix[position.y][position.x] = piece -func init_board(): # create the board +func init_board() -> void: # create the board for i in range(8): # for each row background_matrix.append([]) # add a row for j in range(8): # for each column @@ -210,7 +208,7 @@ func init_board(): # create the board background_matrix[i].append(square) # add the square to the background matrix -func add_pieces(): # add the pieces +func add_pieces() -> void: # add the pieces add_pawns() add_rooks() add_knights() @@ -219,56 +217,56 @@ func add_pieces(): # add the pieces add_kings() -func add_pawns(): +func add_pawns() -> void: for i in range(8): make_piece(Vector2(i, 1), "res://pieces/Pawn.gd", false) make_piece(Vector2(i, 6), "res://pieces/Pawn.gd", true) -func add_rooks(): +func add_rooks() -> void: make_piece(Vector2(0, 0), "res://pieces/Rook.gd", false) make_piece(Vector2(7, 0), "res://pieces/Rook.gd", false) make_piece(Vector2(0, 7), "res://pieces/Rook.gd", true) make_piece(Vector2(7, 7), "res://pieces/Rook.gd", true) -func add_knights(): +func add_knights() -> void: make_piece(Vector2(1, 0), "res://pieces/Knight.gd", false) make_piece(Vector2(6, 0), "res://pieces/Knight.gd", false) make_piece(Vector2(1, 7), "res://pieces/Knight.gd", true) make_piece(Vector2(6, 7), "res://pieces/Knight.gd", true) -func add_bishops(): +func add_bishops() -> void: make_piece(Vector2(2, 0), "res://pieces/Bishop.gd", false) make_piece(Vector2(5, 0), "res://pieces/Bishop.gd", false) make_piece(Vector2(2, 7), "res://pieces/Bishop.gd", true) make_piece(Vector2(5, 7), "res://pieces/Bishop.gd", true) -func add_queens(): +func add_queens() -> void: make_piece(Vector2(3, 0), "res://pieces/Queen.gd", false) make_piece(Vector2(3, 7), "res://pieces/Queen.gd", true) -func add_kings(): +func add_kings() -> void: make_piece(Vector2(4, 0), "res://pieces/King.gd", false) make_piece(Vector2(4, 7), "res://pieces/King.gd", true) Globals.white_king = matrix[7][4] # set the white king Globals.black_king = matrix[0][4] # set the black king -func check_for_circle(position: Vector2): # check for a circle, validating movement +func check_for_circle(position: Vector2) -> bool: # check for a circle, validating movement return background_matrix[position.x][position.y].circle_on -func check_for_frame(position: Vector2): # check for a frame, validating taking +func check_for_frame(position: Vector2) -> bool: # check for a frame, validating taking if !matrix[position.y][position.x]: # if there is no piece return false # return false return matrix[position.y][position.x].frameon # return if the frame is on -func square_clicked(position: Vector2): # square clicked +func square_clicked(position: Vector2) -> void: # square clicked if promoting != null: return var spot = matrix[position.y][position.x] # get the spot @@ -288,7 +286,7 @@ func square_clicked(position: Vector2): # square clicked spot.clicked() # tell the piece shit happeend -func handle_take(position): +func handle_take(position) -> void: if last_clicked is Pawn: var pawn = last_clicked if check_promote(pawn, position, "take"): @@ -297,7 +295,7 @@ func handle_take(position): turn_over() -func handle_move(position): +func handle_move(position) -> void: if last_clicked is King and last_clicked.can_castle: for i in range(len(last_clicked.can_castle)): var castle_data = last_clicked.can_castle[i] @@ -324,7 +322,7 @@ func handle_move(position): turn_over() -func check_promote(pawn, position, calltype: String = "move"): +func check_promote(pawn, position, calltype: String = "move") -> bool: if pawn.can_promote(position): pawn.promote(position, calltype) promoting = position @@ -332,7 +330,7 @@ func check_promote(pawn, position, calltype: String = "move"): return false -func turn_over(): +func turn_over() -> void: promoting = null Events.emit_signal("just_before_turn_over") Globals.add_turn() @@ -340,7 +338,7 @@ func turn_over(): Events.emit_signal("turn_over") -func clear_fx(): # clear the circles +func clear_fx() -> void: # clear the circles for i in range(8): # for each row for j in range(8): # for each column var square = background_matrix[i][j] # get the square @@ -350,14 +348,14 @@ func clear_fx(): # clear the circles piece.set_frame(false) # clear the frame -func _on_outoftime(who): +func _on_outoftime(who) -> void: if who == "white": win("black") else: win("white") -func _on_turn_over(): +func _on_turn_over() -> void: var matstr: String = mat2str() if !history_matrixes.has(matstr): history_matrixes[matstr] = 1 @@ -11,7 +11,7 @@ onready var circle := $Circle signal clicked -func _ready(): +func _ready() -> void: circle.position = Globals.grid.piece_size / 2 circle.material.set_shader_param("color", Globals.grid.overlay_color) circle.visible = false @@ -20,16 +20,16 @@ func _ready(): algebraic_string = Utils.calculate_algebraic_position(real_position) -func _on_Squarea_input_event(_viewport: Node, _event: InputEvent, _shape_idx: int): +func _on_Squarea_input_event(_viewport: Node, _event: InputEvent, _shape_idx: int) -> void: if Input.is_action_just_pressed("click"): emit_signal("clicked", real_position) -func get_string(): +func get_string() -> String: return algebraic_string -func set_circle(boolean: bool, real = true): +func set_circle(boolean: bool, real = true) -> void: circle_on = boolean if real: circle.visible = boolean @@ -1,33 +1,35 @@ -signal newmove extends Node +signal newmove(move) +signal newfen(fen) + var turn_moves: PoolStringArray = [] -var turns_moves := [] +var turns_moves: PoolStringArray = [] var counter := 0 -func _ready(): +func _ready() -> void: Events.connect("turn_over", self, "_on_turn_over") -func is_pawn(inode): +func is_pawn(inode) -> bool: return inode is Pawn -func add_move(move): +func add_move(move) -> void: if turn_moves.size() == 0: - turn_moves.append(str(Globals.white_turns + 1) + ". " + move) + turn_moves.append(str(Globals.fullmove) + ". " + move) else: turn_moves.append(move) emit_signal("newmove", move) -func calculate_algebraic_position(real_position): +func calculate_algebraic_position(real_position) -> String: return char(65 + (real_position.x)).to_lower() + str(8 - real_position.y) -func get_node_name(node): +func get_node_name(node) -> Array: if is_pawn(node): return ["♙", "p"] if node.white else ["♟", "p"] elif node is King: @@ -44,8 +46,8 @@ func get_node_name(node): return ["", ""] -func walk_dir(path = "res://assets/pieces"): # walk the directory, finding the asset packs - var folders := [] # init the folders +func walk_dir(path = "res://assets/pieces") -> PoolStringArray: # walk the directory, finding the asset packs + var folders: PoolStringArray = [] # init the folders var dir := Directory.new() # init the directory if dir.open(path) == OK: # open the directory dir.list_dir_begin() # list the directory @@ -62,21 +64,69 @@ func walk_dir(path = "res://assets/pieces"): # walk the directory, finding the return folders # return the folders -func format_seconds(time: float, use_milliseconds: bool = false): +func format_seconds(time: float, use_milliseconds: bool = false) -> String: var minutes := time / 60 var seconds := fmod(time, 60) if not use_milliseconds: return "%02d:%02d" % [minutes, seconds] + return "%02d:%04.1f" % [minutes, seconds] -func round_place(num, places): - return round(num * pow(10, places)) / pow(10, places) - - -func _on_turn_over(): +func _on_turn_over() -> void: + var fen = fen() + emit_signal("newfen", fen) counter += 1 if counter >= 2: counter = 0 turns_moves.append(turn_moves.join(" ")) turn_moves.resize(0) + var pgn = turns_moves.join(" ") + print(pgn) + + +func fen() -> String: + var pieces = "" + for rank in range(8): + var empty = 0 + for file in range(8): + var spot = Globals.grid.matrix[rank][file] + if spot == null: + empty += 1 + if str(empty - 1) == pieces[-1]: + pieces[-1] = str(empty) + else: + pieces += str(empty) + else: + pieces += spot.shortname[0].to_upper() if spot.white else spot.shortname[0].to_lower() + empty = 0 + if rank != 7: + pieces += "/" + # handle castling checks + var whitecastling = PoolStringArray(Globals.white_king.castleing(true)).join(" ") + var blackcastling = PoolStringArray(Globals.black_king.castleing(true)).join(" ") + var castlingrights = "" + if blackcastling and whitecastling: + castlingrights += "K" if "K" in whitecastling else "" + castlingrights += "Q" if "Q" in whitecastling else "" + castlingrights += "k" if "K" in blackcastling else "" + castlingrights += "q" if "Q" in blackcastling else "" + else: + castlingrights = "-" + + var enpassants = "" + for pawn in Globals.pawns: + if pawn.twostepfirstmove and pawn.just_set: + enpassants += calculate_algebraic_position(pawn.real_position + (Vector2.DOWN * pawn.whiteint)) + var fen = ( + "%s %s %s %s %s %s" + % [ + pieces, + "w" if Globals.turn else "b", + castlingrights, + enpassants if enpassants else "-", + Globals.halfmove, + Globals.fullmove, + ] + ) # pos # turn # castling # enpassant # halfmove # fullmove + return fen diff --git a/World.gd b/World.gd deleted file mode 100644 index 6abfb7f..0000000 --- a/World.gd +++ /dev/null @@ -1,4 +0,0 @@ -extends Node2D - -# warning-ignore-all:unused_signal -signal game_over @@ -1,11 +1,9 @@ -[gd_scene load_steps=4 format=2] +[gd_scene load_steps=3 format=2] [ext_resource path="res://Grid.gd" type="Script" id=1] [ext_resource path="res://ui/GameUI.tscn" type="PackedScene" id=2] -[ext_resource path="res://World.gd" type="Script" id=3] [node name="World" type="Node2D"] -script = ExtResource( 3 ) [node name="Grid" type="Node2D" parent="."] script = ExtResource( 1 ) diff --git a/pieces/Bishop.gd b/pieces/Bishop.gd index 5394c81..91b5b7d 100644 --- a/pieces/Bishop.gd +++ b/pieces/Bishop.gd @@ -2,5 +2,5 @@ extends Piece class_name Bishop, "res://assets/pieces/california/wB.png" -func get_moves(): - return .traverse(.all_dirs().slice(4, 8)) +func get_moves() -> Array: + return traverse(all_dirs().slice(4, 8)) diff --git a/pieces/King.gd b/pieces/King.gd index cfc7ade..68b1183 100644 --- a/pieces/King.gd +++ b/pieces/King.gd @@ -5,11 +5,11 @@ var castle_check := true var can_castle := [] -func _ready(): +func _ready() -> void: Events.connect("just_before_turn_over", self, "just_before_over") -func get_moves(): +func get_moves() -> Array: var moves := [] for i in all_dirs(): var spot: Vector2 = pos_around(i) @@ -19,12 +19,12 @@ func get_moves(): if check_spots_check and checkcheck(spot): continue moves.append(spot) - if castle_check and !has_moved and !Globals.in_check: # make sure this is only called when clicking + if castle_check and !Globals.in_check: # make sure this is only called when clicking moves.append_array(castleing()) return moves -func just_before_over(): # assign metadata for threefold repetition draw check +func just_before_over() -> void: # assign metadata for threefold repetition draw check castleing() if can_castle.size() > 0: for i in can_castle: @@ -40,9 +40,12 @@ func just_before_over(): # assign metadata for threefold repetition draw check Globals.grid.matrix[8].bccr = true -func castleing(): +func castleing(justcheckrooks = false) -> Array: + if has_moved: + return [] var moves := [] var rooks := [pos_around(Vector2.RIGHT * 3), pos_around(Vector2.LEFT * 4)] + var labels = ["Q", "K"] var rook_motion := [pos_around(Vector2.RIGHT), pos_around(Vector2.LEFT)] var king_moveto_spots := [Vector2.RIGHT, Vector2.LEFT] # O-O and O-O-O respectivel for i in range(len(rooks)): @@ -53,6 +56,9 @@ func castleing(): continue if rook.has_moved: continue + if justcheckrooks: + moves.append(labels[i]) + continue var direction: Vector2 = king_moveto_spots[i] var posx2: Vector2 = pos_around(direction * 2) var pos: Vector2 = pos_around(direction) @@ -65,7 +71,7 @@ func castleing(): return moves -func castle(position): +func castle(position) -> String: var return_string = "" if can_castle.size() == 1: return_string = can_castle[0][3] @@ -81,14 +87,14 @@ func castle(position): return return_string -func can_move(): # checks if you can legally move +func can_move() -> bool: # checks if you can legally move castle_check = false var can: bool = .can_move() castle_check = true return can -func get_attacks(): +func get_attacks() -> Array: castle_check = false var final: Array = .get_attacks() castle_check = true diff --git a/pieces/Knight.gd b/pieces/Knight.gd index 2efe40d..d8c47b6 100644 --- a/pieces/Knight.gd +++ b/pieces/Knight.gd @@ -2,7 +2,7 @@ extends Piece class_name Knight, "res://assets/pieces/california/wN.png" -func get_moves(): +func get_moves() -> Array: var moves := [ pos_around(Vector2(-2, -1)), pos_around(Vector2(-2, 1)), diff --git a/pieces/Pawn.gd b/pieces/Pawn.gd index 42ed456..812c862 100644 --- a/pieces/Pawn.gd +++ b/pieces/Pawn.gd @@ -12,7 +12,8 @@ onready var sprites := [] onready var darken = get_node("../../Darken") -func _ready(): +func _ready() -> void: + Globals.pawns.append(self) Events.connect("turn_over", self, "_on_turn_over") Events.connect("just_before_turn_over", self, "_just_before_turn_over") sprite.position = Globals.grid.piece_size / 2 @@ -27,7 +28,11 @@ func _ready(): sprites.append(newsprite) -func moveto(position, real = true, take = false): +func _exit_tree() -> void: + Globals.pawns.remove(Globals.pawns.find(self)) + + +func moveto(position, real = true, take = false) -> void: # check if 2 step if real and !twostepfirstmove and !has_moved: if white and real_position.y - position.y == 2: @@ -37,9 +42,11 @@ func moveto(position, real = true, take = false): twostepfirstmove = true just_set = true .moveto(position, real, take) + if real: + Globals.reset_halfmove() -func get_moves(): +func get_moves() -> Array: var points := [Vector2.UP, Vector2.UP * 2] var moves := [] for i in range(len(points)): @@ -57,18 +64,18 @@ func get_moves(): return moves -func can_promote(position): +static func can_promote(position) -> bool: if position.y >= 7 or position.y <= 0: return true return false -func passant(position): +func passant(position) -> void: enpassant.clear() moveto(position) -func get_attacks(): +func get_attacks() -> Array: var points := [Vector2.UP + Vector2.RIGHT, Vector2.UP + Vector2.LEFT] var moves := [] for i in range(len(points)): @@ -85,7 +92,7 @@ func get_attacks(): return moves -func en_passant(turncheck = true): # in passing +func en_passant(turncheck = true) -> Array: # in passing var passants := [pos_around(Vector2.LEFT), pos_around(Vector2.RIGHT)] var moves := [] for i in passants: @@ -107,7 +114,7 @@ func en_passant(turncheck = true): # in passing return moves -func promote(position, type): +func promote(position, type) -> void: if type == "take": take(at_pos(position)) else: @@ -118,7 +125,7 @@ func promote(position, type): sprites[i].show() -func handle_sprite_input_event(node): +func handle_sprite_input_event(node) -> void: darken.hide() var script = piece(promotables[sprites.find(node)][0]) Globals.grid.make_piece(real_position, script, white) @@ -127,7 +134,7 @@ func handle_sprite_input_event(node): queue_free() -func piece(string): +func piece(string) -> String: match string: "Q": return "res://pieces/Queen.gd" @@ -137,9 +144,11 @@ func piece(string): return "res://pieces/Rook.gd" "B": return "res://pieces/Bishop.gd" + _: + return "res://pieces/Piece.gd" -func _on_turn_over(): +func _on_turn_over() -> void: if just_set: just_set = false return @@ -147,7 +156,7 @@ func _on_turn_over(): twostepfirstmove = false -func _just_before_turn_over(): +func _just_before_turn_over() -> void: var had_a_enpassant := len(enpassant) > 0 enpassant.clear() if !had_a_enpassant: # scuffed method to check if enpassant is possible diff --git a/pieces/Piece.gd b/pieces/Piece.gd index 7c9d207..22e548a 100644 --- a/pieces/Piece.gd +++ b/pieces/Piece.gd @@ -19,7 +19,7 @@ onready var colorrect := $ColorRect onready var frame := $Frame -func _ready(): +func _ready() -> void: team = "w" if white else "b" sprite.position = Globals.grid.piece_size / 2 var tmp: Array = Utils.get_node_name(self) @@ -32,36 +32,36 @@ func _ready(): load_texture() -func load_texture(path := "%s%s%s.png" % [Globals.grid.ASSETS_PATH, team.to_lower(), shortname.to_upper()]): +func load_texture(path := "%s%s%s.png" % [Globals.grid.ASSETS_PATH, team, shortname.to_upper()]) -> void: sprite.texture = load(path) -func clicked(): +func clicked() -> void: colorrect.show() set_circle(get_moves()) set_circle(get_attacks(), "take") -func clear_clicked(): +func clear_clicked() -> void: colorrect.hide() Globals.grid.clear_fx() -func algebraic_take_notation(position): +func algebraic_take_notation(position) -> String: var starter := shortname if shortname != "p" else to_algebraic(real_position)[0] return starter + "x" + to_algebraic(position) -func algebraic_move_notation(position): +func algebraic_move_notation(position) -> String: var starter := shortname if shortname != "p" else "" return starter + to_algebraic(position) -func to_algebraic(position): +static func to_algebraic(position) -> String: return Globals.grid.background_matrix[position.x][position.y].get_string() -func move(newpos: Vector2): # dont use directly; use moveto +func move(newpos: Vector2) -> void: # dont use directly; use moveto tween.interpolate_property( self, "global_position", @@ -75,7 +75,7 @@ func move(newpos: Vector2): # dont use directly; use moveto tween.start() -func moveto(position, real := true, take := false): +func moveto(position, real := true, take := false) -> void: Globals.grid.matrix[real_position.y][real_position.x] = null Globals.grid.matrix[position.y][position.x] = self if real: @@ -90,11 +90,11 @@ func moveto(position, real := true, take := false): has_moved = true -func pos_around(around_vector): +func pos_around(around_vector) -> Vector2: return real_position + around_vector -func all_dirs(): +static func all_dirs() -> Array: return [ Vector2.UP, Vector2.DOWN, @@ -107,7 +107,7 @@ func all_dirs(): ] -func traverse(arr := [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT]): +func traverse(arr := [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT]) -> Array: var circle_array := [] for i in arr: var pos := real_position @@ -126,21 +126,21 @@ func traverse(arr := [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT]): return circle_array -func at_pos(vector: Vector2): +static func at_pos(vector: Vector2): return Globals.grid.matrix[vector.y][vector.x] -func can_move(): # checks if you can legally move +func can_move() -> bool: # checks if you can legally move if get_moves().size() != 0 or get_attacks().size() != 0: return true return false -func get_moves(): # @Override - pass +func get_moves() -> Array: # @Override + return [] -func get_attacks(): # @Override +func get_attacks() -> Array: # @Override no_enemys = false var moves: Array = get_moves() # assumes the attacks are same as moves var final := [] @@ -154,7 +154,7 @@ func get_attacks(): # @Override return final -func can_attack_piece(piece): +func can_attack_piece(piece) -> bool: check_spots_check = false for pos in get_attacks(): if at_pos(pos) == piece: @@ -164,15 +164,15 @@ func can_attack_piece(piece): return false -func create_move_circles(pos): +static func create_move_circles(pos) -> void: Globals.grid.background_matrix[pos.x][pos.y].set_circle(true) # make the move circle -func create_take_circles(spot): # create take circles +static func create_take_circles(spot) -> void: # create take circles spot.set_frame() # turn on the little take frame on the piece, to show its takeable -func set_circle(positions: Array, type := "move"): +static func set_circle(positions: Array, type := "move") -> void: for pos in positions: var spot = at_pos(pos) # get the piece at the position if type == "move": @@ -181,7 +181,7 @@ func set_circle(positions: Array, type := "move"): create_take_circles(spot) # if the king is in check, return true -func checkcheck(pos): # moves to position, then checks if your king is in check +func checkcheck(pos) -> bool: # moves to position, then checks if your king is in check var mat: Array = Globals.grid.matrix.duplicate(true) # make a copy of the matrix moveto(pos, false) # move to the position if Globals.grid.check_in_check(): # if you are still in check @@ -191,24 +191,25 @@ func checkcheck(pos): # moves to position, then checks if your king is in check return false -func is_on_board(vector: Vector2): # limit the vector to the board +static func is_on_board(vector: Vector2) -> bool: # limit the vector to the board if vector.y < 0 or vector.y > 7 or vector.x < 0 or vector.x > 7: return false return true -func take(piece: Piece): +func take(piece: Piece) -> void: clear_clicked() piece.took() moveto(piece.real_position, true, true) + Globals.reset_halfmove() -func took(): # called when piece is taken +func took() -> void: # called when piece is taken SoundFx.play("Capture") Globals.grid.matrix[real_position.y][real_position.x] = null anim.play("Take") -func set_frame(value = true): +func set_frame(value = true) -> void: frameon = value frame.visible = value diff --git a/pieces/Queen.gd b/pieces/Queen.gd index d4a29ec..77c4da5 100644 --- a/pieces/Queen.gd +++ b/pieces/Queen.gd @@ -2,5 +2,5 @@ extends Piece class_name Queen, "res://assets/pieces/california/wQ.png" -func get_moves(): +func get_moves() -> Array: return traverse(all_dirs()) diff --git a/pieces/Rook.gd b/pieces/Rook.gd index 92d2728..90aaca0 100644 --- a/pieces/Rook.gd +++ b/pieces/Rook.gd @@ -2,5 +2,5 @@ extends Piece class_name Rook, "res://assets/pieces/california/wR.png" -func get_moves(): - return .traverse() +func get_moves() -> Array: + return traverse() diff --git a/project.godot b/project.godot index bad9727..18e9f39 100644 --- a/project.godot +++ b/project.godot @@ -14,6 +14,11 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://pieces/Bishop.gd" }, { +"base": "LineEdit", +"class": "FENLabel", +"language": "GDScript", +"path": "res://ui/FENlabel.gd" +}, { "base": "Node2D", "class": "Grid", "language": "GDScript", @@ -51,6 +56,7 @@ _global_script_classes=[ { } ] _global_script_class_icons={ "Bishop": "res://assets/pieces/california/wB.png", +"FENLabel": "", "Grid": "", "King": "res://assets/pieces/california/wK.png", "Knight": "res://assets/pieces/california/wN.png", diff --git a/saveload.gd b/saveload.gd index e0ea473..d5a264d 100644 --- a/saveload.gd +++ b/saveload.gd @@ -1,6 +1,6 @@ extends Node -const settings_file = "user://chess.settings" +const settings_file := "user://chess.settings" var files := { "settings": @@ -17,17 +17,17 @@ var files := { } -func _ready(): +func _ready() -> void: load_data("settings") -func save(type): +func save(type) -> void: var file = File.new() file.open(files[type]["file"], File.WRITE) file.store_string(var2str(files[type]["data"])) -func load_data(type: String): +func load_data(type: String) -> Dictionary: if check_file(type): var file = File.new() file.open(files[type]["file"], File.READ) @@ -36,8 +36,9 @@ func load_data(type: String): if files[type]["data"].size() == read_dictionary.size(): files[type]["data"] = read_dictionary file.close() + return files[type]["data"] -func check_file(type): +func check_file(type) -> bool: var file = File.new() return file.file_exists(files[type]["file"]) diff --git a/sounds/SoundFX.gd b/sounds/SoundFX.gd index a0aace5..d86d61f 100644 --- a/sounds/SoundFX.gd +++ b/sounds/SoundFX.gd @@ -15,7 +15,7 @@ var sounds := { onready var sound_players := get_children() -func play(sound_string, pitch_scale = 1, volume_db = 0): +func play(sound_string, pitch_scale = 1, volume_db = 0) -> void: for soundPlayer in sound_players: if not soundPlayer.playing: soundPlayer.pitch_scale = pitch_scale diff --git a/ui/FENlabel.gd b/ui/FENlabel.gd new file mode 100644 index 0000000..1240d25 --- /dev/null +++ b/ui/FENlabel.gd @@ -0,0 +1,11 @@ +extends LineEdit +class_name FENLabel + + +func _ready() -> void: + Utils.connect("newfen", self, "on_new_fen") + context_menu_enabled = false + + +func on_new_fen(fen) -> void: + text = fen diff --git a/ui/GameUI.tscn b/ui/GameUI.tscn index 0e2afc5..b12c656 100644 --- a/ui/GameUI.tscn +++ b/ui/GameUI.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=22 format=2] +[gd_scene load_steps=25 format=2] [ext_resource path="res://ui/main.tres" type="Theme" id=1] [ext_resource path="res://ui/roboto.tres" type="DynamicFont" id=2] @@ -14,6 +14,7 @@ [ext_resource path="res://ui/flatblack.tres" type="StyleBox" id=12] [ext_resource path="res://ui/button.tres" type="StyleBox" id=13] [ext_resource path="res://ui/buttonhover.tres" type="StyleBox" id=14] +[ext_resource path="res://ui/FENlabel.gd" type="Script" id=15] [sub_resource type="DynamicFont" id=1] size = 25 @@ -84,6 +85,14 @@ PopupMenu/styles/panel = ExtResource( 12 ) VBoxContainer/constants/separation = 15 VScrollBar/styles/scroll = SubResource( 5 ) +[sub_resource type="DynamicFont" id=8] +size = 15 +font_data = ExtResource( 3 ) + +[sub_resource type="DynamicFont" id=9] +size = 40 +font_data = ExtResource( 3 ) + [node name="UI" type="CanvasLayer"] [node name="Holder" type="Control" parent="."] @@ -114,9 +123,9 @@ __meta__ = { } [node name="BlackTime" type="Label" parent="Holder/Back/VBox"] -margin_top = 167.0 +margin_top = 105.0 margin_right = 400.0 -margin_bottom = 250.0 +margin_bottom = 188.0 custom_fonts/font = ExtResource( 2 ) text = "00:00.0" align = 1 @@ -147,9 +156,9 @@ autowrap = true script = ExtResource( 7 ) [node name="MovesList" type="ScrollContainer" parent="Holder/Back/VBox"] -margin_top = 300.0 +margin_top = 238.0 margin_right = 400.0 -margin_bottom = 500.0 +margin_bottom = 438.0 rect_min_size = Vector2( 0, 200 ) theme = SubResource( 6 ) scroll_horizontal_enabled = false @@ -160,9 +169,9 @@ custom_constants/vseparation = 0 columns = 3 [node name="WhiteTime" type="Label" parent="Holder/Back/VBox"] -margin_top = 550.0 +margin_top = 488.0 margin_right = 400.0 -margin_bottom = 633.0 +margin_bottom = 571.0 custom_fonts/font = ExtResource( 2 ) text = "00:00.0" align = 1 @@ -185,6 +194,26 @@ color = Color( 0, 0, 0, 1 ) [node name="Timer" type="Node" parent="Holder/Back/VBox"] script = ExtResource( 5 ) +[node name="FENlabel" type="LineEdit" parent="Holder/Back/VBox"] +margin_top = 621.0 +margin_right = 400.0 +margin_bottom = 695.0 +custom_colors/font_color_uneditable = Color( 1, 1, 1, 1 ) +custom_fonts/font = SubResource( 8 ) +text = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" +editable = false +placeholder_text = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" +script = ExtResource( 15 ) + +[node name="Label" type="Label" parent="Holder/Back/VBox/FENlabel"] +margin_top = -49.0 +margin_right = 400.0 +margin_bottom = 11.0 +custom_fonts/font = SubResource( 9 ) +text = "fen" +align = 1 +valign = 2 + [node name="Darken" type="ColorRect" parent="."] visible = false anchor_right = 1.0 diff --git a/ui/Lobby.gd b/ui/Lobby.gd index 0637e84..b7332e8 100644 --- a/ui/Lobby.gd +++ b/ui/Lobby.gd @@ -19,12 +19,12 @@ onready var port_forward_label = $Back/Center/HBox/VBox2/Portforward onready var find_public_ip_button = $Back/Center/HBox/VBox2/FindPublicIP -func toggle(onoff): +func toggle(onoff) -> void: visible = onoff -func _ready(): - address.get_menu() +func _ready() -> void: + address.context_menu_enabled = false # Connect all the callbacks related to networking. get_tree().connect("network_peer_connected", self, "_player_connected") get_tree().connect("network_peer_disconnected", self, "_player_disconnected") @@ -37,16 +37,16 @@ func _ready(): # Callback from SceneTree. -func _player_connected(_id): +func _player_connected(_id) -> void: # Someone connected, start the game! var chess = load("res://World.tscn").instance() # Connect deferred so we can safely erase it from the callback. - chess.connect("game_over", self, "_end_game", [], CONNECT_DEFERRED) + Utils.connect("game_over", self, "_end_game", [], CONNECT_DEFERRED) get_tree().get_root().add_child(chess) -func _player_disconnected(_id): +func _player_disconnected(_id) -> void: if get_tree().is_network_server(): _end_game("Client disconnected") else: @@ -54,12 +54,12 @@ func _player_disconnected(_id): # Callback from SceneTree, only for clients (not server). -func _connected_ok(): +func _connected_ok() -> void: pass # This function is not needed for this project. # Callback from SceneTree, only for clients (not server). -func _connected_fail(): +func _connected_fail() -> void: _set_status("Couldn't connect", false) get_tree().set_network_peer(null) # Remove peer. @@ -67,14 +67,14 @@ func _connected_fail(): address.editable = true -func _server_disconnected(): +func _server_disconnected() -> void: _end_game("Server disconnected") ##### Game creation functions ###### -func _end_game(_with_error = ""): +func _end_game(_with_error = "") -> void: if has_node("/root/World"): # Erase immediately, otherwise network might show # errors (this is why we connected deferred above). @@ -87,7 +87,7 @@ func _end_game(_with_error = ""): address.editable = true -func _set_status(text, isok): +func _set_status(text, isok) -> void: # Simple way to show status. if isok: status_ok.set_text(text) @@ -101,7 +101,7 @@ func _set_status(text, isok): status_ok.visible = len(status_ok.text) > 0 -func _on_host_pressed(): +func _on_host_pressed() -> void: peer = NetworkedMultiplayerENet.new() peer.set_compression_mode(NetworkedMultiplayerENet.COMPRESS_RANGE_CODER) var err = peer.create_server(DEFAULT_PORT, 1) # Maximum of 1 peer, since it's a 2-player game. @@ -119,7 +119,7 @@ func _on_host_pressed(): # Only show hosting instructions when relevant. -func _on_join_pressed(): +func _on_join_pressed() -> void: var ip = address.get_text() if not ip.is_valid_ip_address(): _set_status("IP address is invalid", false) @@ -131,5 +131,5 @@ func _on_join_pressed(): get_tree().set_network_peer(peer) -func _on_find_public_ip_pressed(): +func _on_find_public_ip_pressed() -> void: OS.shell_open("https://icanhazip.com/") diff --git a/ui/MovesList.gd b/ui/MovesList.gd index 1cf697d..fffbeb3 100644 --- a/ui/MovesList.gd +++ b/ui/MovesList.gd @@ -9,13 +9,13 @@ onready var scroll_bar = get_v_scrollbar() onready var sans = $sans -func _ready(): +func _ready() -> void: tween = Tween.new() add_child(tween) Utils.connect("newmove", self, "on_new_move") -func create_number_label(num): +func create_number_label(num) -> void: var clr = ColorRect.new() clr.color = Color(1, 1, 1, 0.13) clr.rect_min_size = Vector2(70, 30) @@ -28,7 +28,7 @@ func create_number_label(num): sans.add_child(clr) -func create_san_label(text, alignment = Label.ALIGN_RIGHT): +func create_san_label(text, alignment = Label.ALIGN_RIGHT) -> void: var label = Label.new() label.text = text label.valign = Label.VALIGN_CENTER @@ -37,11 +37,11 @@ func create_san_label(text, alignment = Label.ALIGN_RIGHT): sans.add_child(label) -func on_new_move(move): +func on_new_move(move) -> void: var alignment = Label.ALIGN_RIGHT if Globals.turn: # white just moved alignment = Label.ALIGN_LEFT - create_number_label(Globals.white_turns + 1) + create_number_label(Globals.fullmove) number = 0 create_san_label(move, alignment) tween.interpolate_property( # scrolldown diff --git a/ui/Settings.gd b/ui/Settings.gd index fa12d2f..8c1270b 100644 --- a/ui/Settings.gd +++ b/ui/Settings.gd @@ -9,54 +9,54 @@ onready var borderlessbutton := $ColorRect/HBoxContainer/VBoxContainer2/Borderle onready var settings: Dictionary = SaveLoad.files["settings"]["data"] setget set_settings -func set_settings(new_settings): +func set_settings(new_settings) -> void: toggle_button_visuals(new_settings) settings = new_settings SaveLoad.files["settings"]["data"] = settings SaveLoad.save("settings") -func toggle(onoff): +func toggle(onoff) -> void: visible = onoff -func toggle_button_visuals(set = settings): +func toggle_button_visuals(set = settings) -> void: vsyncbutton.pressed = set["vsync"] fullscreenbutton.pressed = set["fullscreen"] borderlessbutton.pressed = !set["borderless"] -func _ready(): +func _ready() -> void: toggle_button_visuals() for i in piece_sets: piece_set_button.add_icon_item(load("res://assets/pieces/" + i + "/wP.png"), i) piece_set_button.selected = piece_sets.find(settings.piece_set) -func _input(event): - if event.is_action_pressed("ui_cancel"): +func _input(event) -> void: + if visible and event.is_action_pressed("ui_cancel"): toggle(false) -func _on_BackButton_pressed(): +func _on_BackButton_pressed() -> void: toggle(false) -func _on_PieceSet_item_selected(index): +func _on_PieceSet_item_selected(index) -> void: Globals.piece_set = piece_sets[index] self.settings.piece_set = piece_sets[index] -func _on_VsyncButton_toggled(button_pressed: bool): +func _on_VsyncButton_toggled(button_pressed: bool) -> void: OS.vsync_enabled = button_pressed self.settings.vsync = button_pressed -func _on_FullscreenButton_toggled(button_pressed: bool): +func _on_FullscreenButton_toggled(button_pressed: bool) -> void: OS.window_fullscreen = button_pressed self.settings.fullscreen = button_pressed -func _on_Borderless_toggled(button_pressed: bool): +func _on_Borderless_toggled(button_pressed: bool) -> void: self.settings.borderless = !button_pressed OS.window_borderless = !button_pressed diff --git a/ui/StartMenu.gd b/ui/StartMenu.gd index fa47bbc..f39cde1 100644 --- a/ui/StartMenu.gd +++ b/ui/StartMenu.gd @@ -13,30 +13,30 @@ onready var timer := $Timer onready var lobby := $ColorRect/Lobby -func _ready(): +func _ready() -> void: randomize() colorrect.color = nice_colors[randi() % nice_colors.size()] timer.start(timer_length) _on_Timer_timeout() -func rand(clr): +func rand(clr) -> float: return clamp(clr + rand_range(0, .1) if randi() % 2 else clr - rand_range(0, .1), 0, 1) -func _on_local_pressed(): +func _on_local_pressed() -> void: get_tree().change_scene_to(world) -func _on_quit_pressed(): +func _on_quit_pressed() -> void: get_tree().quit() -func _on_settings_pressed(): +func _on_settings_pressed() -> void: settings.toggle(true) -func _on_Timer_timeout(): +func _on_Timer_timeout() -> void: var clr = nice_colors[randi() % nice_colors.size()] clr.r = rand(clr.r) clr.b = rand(clr.b) @@ -46,5 +46,5 @@ func _on_Timer_timeout(): timer.start(timer_length) -func _on_multiplayer_pressed(): +func _on_multiplayer_pressed() -> void: lobby.toggle(true) diff --git a/ui/Status.gd b/ui/Status.gd index aa49be3..3988d53 100644 --- a/ui/Status.gd +++ b/ui/Status.gd @@ -1,6 +1,6 @@ extends Label -func text(newtext): +func text(newtext) -> void: show() text = newtext diff --git a/ui/Timer.gd b/ui/Timer.gd index de71415..41b60a1 100644 --- a/ui/Timer.gd +++ b/ui/Timer.gd @@ -9,13 +9,13 @@ onready var whitelabel := $"../WhiteTime" onready var blacklabel := $"../BlackTime" -func _ready(): +func _ready() -> void: whitelabel.time = 300 blacklabel.time = 300 Events.connect("turn_over", self, "turn_over") -func _process(delta): +func _process(delta) -> void: if !enabled: return if Globals.turn: @@ -26,7 +26,7 @@ func _process(delta): enabled = false -func turn_over(): +func turn_over() -> void: time_elapsed = 0.0 count += 1 enabled = count >= 2 diff --git a/ui/TimerLabels.gd b/ui/TimerLabels.gd index 764c827..75cba88 100644 --- a/ui/TimerLabels.gd +++ b/ui/TimerLabels.gd @@ -8,7 +8,7 @@ export(bool) var white := false onready var colorrect := $ColorRect -func set_time(newtime): +func set_time(newtime) -> bool: if stop: return false time = newtime @@ -22,7 +22,7 @@ func set_time(newtime): return true -func _ready(): +func _ready() -> void: _on_turn_over() colorrect.show_behind_parent = true colorrect.color = Globals.grid.overlay_color @@ -30,9 +30,9 @@ func _ready(): Events.connect("game_over", self, "_on_game_over") -func _on_game_over(): +func _on_game_over() -> void: stop = true -func _on_turn_over(): +func _on_turn_over() -> void: colorrect.visible = Globals.turn == white |