online multiplayer chess game (note server currently down)
createfen system -> void
bendn 2022-05-13
parent 9c334bd · commit 06a92f3
-rw-r--r--ClickableSprite.gd8
-rw-r--r--Globals.gd33
-rw-r--r--Grid.gd68
-rw-r--r--Square.gd8
-rw-r--r--Utils.gd82
-rw-r--r--World.gd4
-rw-r--r--World.tscn4
-rw-r--r--pieces/Bishop.gd4
-rw-r--r--pieces/King.gd22
-rw-r--r--pieces/Knight.gd2
-rw-r--r--pieces/Pawn.gd33
-rw-r--r--pieces/Piece.gd53
-rw-r--r--pieces/Queen.gd2
-rw-r--r--pieces/Rook.gd4
-rw-r--r--project.godot6
-rw-r--r--saveload.gd11
-rw-r--r--sounds/SoundFX.gd2
-rw-r--r--ui/FENlabel.gd11
-rw-r--r--ui/GameUI.tscn43
-rw-r--r--ui/Lobby.gd28
-rw-r--r--ui/MovesList.gd10
-rw-r--r--ui/Settings.gd22
-rw-r--r--ui/StartMenu.gd14
-rw-r--r--ui/Status.gd2
-rw-r--r--ui/Timer.gd6
-rw-r--r--ui/TimerLabels.gd8
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)
diff --git a/Globals.gd b/Globals.gd
index d6433d0..43f0c67 100644
--- a/Globals.gd
+++ b/Globals.gd
@@ -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)
diff --git a/Grid.gd b/Grid.gd
index 0647337..130b723 100644
--- a/Grid.gd
+++ b/Grid.gd
@@ -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
diff --git a/Square.gd b/Square.gd
index 2d52155..60a67d0 100644
--- a/Square.gd
+++ b/Square.gd
@@ -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
diff --git a/Utils.gd b/Utils.gd
index 114fc67..811f322 100644
--- a/Utils.gd
+++ b/Utils.gd
@@ -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
diff --git a/World.tscn b/World.tscn
index ef7994c..9b820e2 100644
--- a/World.tscn
+++ b/World.tscn
@@ -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