online multiplayer chess game (note server currently down)
multiplayer
bendn 2022-05-18
parent adf6716 · commit aefb58f
-rw-r--r--Events.gd1
-rw-r--r--Globals.gd41
-rw-r--r--Grid.gd60
-rw-r--r--Square.gd2
-rw-r--r--Utils.gd26
-rw-r--r--networking/Network.gd105
-rw-r--r--pieces/Pawn.gd5
-rw-r--r--pieces/Piece.gd6
-rw-r--r--project.godot7
-rw-r--r--ui/Lobby.gd228
-rw-r--r--ui/Lobby.tscn117
-rw-r--r--ui/MovesList.gd2
-rw-r--r--ui/Settings.tscn20
-rw-r--r--ui/StartMenu.gd2
-rw-r--r--ui/StartMenu.tscn23
-rw-r--r--ui/Timer.gd2
-rw-r--r--ui/TimerLabels.gd4
17 files changed, 412 insertions, 239 deletions
diff --git a/Events.gd b/Events.gd
index 1a7cfa9..2c6af0d 100644
--- a/Events.gd
+++ b/Events.gd
@@ -5,3 +5,4 @@ signal turn_over
signal just_before_turn_over # called just before turn over
signal outoftime # called when the time is up
signal game_over # called when the game is over
+signal go_back # called when the game is over, and were ready to go back
diff --git a/Globals.gd b/Globals.gd
index 43f0c67..132e892 100644
--- a/Globals.gd
+++ b/Globals.gd
@@ -1,9 +1,12 @@
extends Node
-var __nosethalfmove = false
+const pieces = "NKQRBP"
+var __nosethalfmove = false
var pawns = [] # PoolPawnArray
+var team = true
var grid: Grid = null
+var network: Network = null
var piece_set := "california"
var fullmove := 1
var halfmove := 0
@@ -15,7 +18,34 @@ var turn := true # true for white, false for black
# true cuz white goes first
-func turns(_winner) -> int:
+func reset_vars() -> void:
+ __nosethalfmove = false
+ pawns = []
+ team = true
+ grid = null
+ fullmove = 1
+ halfmove = 0
+ in_check = false
+ checking_piece = null
+ white_king = null
+ black_king = null
+ turn = true
+ Utils.reset_vars()
+
+
+func pack_vars() -> Dictionary:
+ return {
+ "fullmove": fullmove,
+ "halfmove": halfmove,
+ "turn": turn,
+ }
+
+
+func get_var(key):
+ return self.get(key)
+
+
+func turns() -> int:
return fullmove
@@ -25,12 +55,15 @@ func reset_halfmove() -> void:
func add_turn() -> void:
+ Events.emit_signal("just_before_turn_over")
if !turn:
fullmove += 1
if __nosethalfmove:
__nosethalfmove = false
- return
- halfmove += 1
+ else:
+ halfmove += 1
+ turn = not turn
+ Events.emit_signal("turn_over")
func _ready() -> void:
diff --git a/Grid.gd b/Grid.gd
index 695eedd..5496daa 100644
--- a/Grid.gd
+++ b/Grid.gd
@@ -36,7 +36,7 @@ var matrix := []
var promoting = null
var background_matrix := []
var history_matrixes := {}
-var last_clicked = null
+var last_clicked: Piece = null
onready var PIECE_SET: String = Globals.piece_set
@@ -47,8 +47,11 @@ onready var pieces := $Pieces
onready var status_label := $"../UI/Holder/Back/VBox/Status"
+func _init():
+ Globals.grid = self
+
+
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
init_labels() # add the labels
@@ -101,9 +104,7 @@ func init_labels() -> void:
letterslabel.rect_position.x = i * piece_size.x
letterslabel.rect_position.y = piece_size.y * 7
size_label(letterslabel, i)
- letterslabel.get_node("Label").text = Utils.calculate_algebraic_position(
- letterslabel.rect_position / piece_size
- )[0]
+ letterslabel.get_node("Label").text = Utils.to_algebraic(letterslabel.rect_position / piece_size)[0]
foreground.add_child(letterslabel)
var numberslabel := TopRightLabel.instance()
numberslabel.rect_position.y = i * piece_size.x
@@ -144,14 +145,16 @@ func drawed() -> void:
Events.emit_signal("game_over")
SoundFx.play("Draw")
yield(get_tree().create_timer(5), "timeout")
+ Events.emit_signal("go_back")
SoundFx.play("Victory")
func win(winner) -> void:
Events.emit_signal("game_over")
- print(winner, " won the game in ", Globals.turns(winner), " turns!")
+ print(winner, " won the game in ", Globals.turns(), " turns!")
SoundFx.play("Victory")
yield(get_tree().create_timer(5), "timeout")
+ Events.emit_signal("go_back")
SoundFx.play("Victory")
@@ -159,8 +162,8 @@ 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
- if spot and spot.white != Globals.turn: # enemie
- if spot.can_attack_piece(Globals.white_king if Globals.turn else Globals.black_king): # if it can take the king
+ if spot and spot.white != Globals.team: # enemie
+ if spot.can_attack_piece(Globals.white_king if Globals.team else Globals.black_king): # if it can take the king
if prin:
Globals.in_check = true # set in_check
Globals.checking_piece = spot # set checking_piece
@@ -173,7 +176,9 @@ 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
- if spot and spot.white == Globals.turn: # fren
+ if Input.is_action_pressed("ui_down"):
+ breakpoint
+ if spot and spot.white != Globals.team: # enemie: checking for our enemys
if spot.can_move():
return true
return false
@@ -265,7 +270,7 @@ func check_for_circle(position: Vector2) -> bool: # check for a circle, validat
func check_for_frame(position: Vector2) -> bool: # check for a frame, validating taking
- if !matrix[position.y][position.x]: # if there is no piece
+ if !is_instance_valid(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
@@ -273,9 +278,11 @@ func check_for_frame(position: Vector2) -> bool: # check for a frame, validatin
func square_clicked(position: Vector2) -> void: # square clicked
if promoting != null:
return
+ if Globals.turn != Globals.team:
+ return
var spot = matrix[position.y][position.x] # get the spot
- if !spot or spot.white != Globals.turn: # spot is not a tile or spot is not turn color
- if !is_instance_valid(last_clicked): # last clicked is null, so this is pointless
+ if !spot or spot.white != Globals.team:
+ if !is_instance_valid(last_clicked):
return
if check_for_frame(position): # takeable
handle_take(position)
@@ -295,8 +302,8 @@ func handle_take(position) -> void:
var pawn = last_clicked
if check_promote(pawn, position, "take"):
return
- last_clicked.take(matrix[position.y][position.x]) # eat
turn_over()
+ Globals.network.send_move_packet([last_clicked.real_position, position], Network.MOVEHEADERS.take) # piece taking piece
func handle_move(position) -> void:
@@ -304,10 +311,16 @@ func handle_move(position) -> void:
for i in range(len(last_clicked.can_castle)):
var castle_data = last_clicked.can_castle[i]
if castle_data[0] == position:
- Utils.add_move(last_clicked.castle(castle_data[0]))
- castle_data[1].override_moveto = true
- castle_data[1].moveto(castle_data[2])
- castle_data[1].override_moveto = false
+ # send some packet
+ Globals.network.send_move_packet(
+ {
+ "king": last_clicked.real_position,
+ "rook": castle_data[1].real_position,
+ "rookdestination": castle_data[2],
+ "kingdestination": castle_data[0]
+ },
+ Network.MOVEHEADERS.castle
+ )
turn_over()
return
if last_clicked is Pawn:
@@ -316,14 +329,17 @@ func handle_move(position) -> void:
for i in range(len(pawn.enpassant)):
var en_passant_data = pawn.enpassant[i]
if en_passant_data[0] == position:
- en_passant_data[1].took() # kill the unfortunate
- pawn.passant(en_passant_data[0])
+ # send some packet
+ Globals.network.send_move_packet(
+ [pawn.real_position, position, en_passant_data[1].real_position],
+ Network.MOVEHEADERS.passant
+ )
turn_over()
return
if check_promote(pawn, position):
return
- last_clicked.moveto(position)
turn_over()
+ Globals.network.send_move_packet([last_clicked.real_position, position], Network.MOVEHEADERS.move) # piece moving
func check_promote(pawn, position, calltype: String = "move") -> bool:
@@ -336,10 +352,6 @@ func check_promote(pawn, position, calltype: String = "move") -> bool:
func turn_over() -> void:
promoting = null
- Events.emit_signal("just_before_turn_over")
- Globals.add_turn()
- Globals.turn = not Globals.turn
- Events.emit_signal("turn_over")
func clear_fx() -> void: # clear the circles
diff --git a/Square.gd b/Square.gd
index 60a67d0..53b8ef9 100644
--- a/Square.gd
+++ b/Square.gd
@@ -17,7 +17,7 @@ func _ready() -> void:
circle.visible = false
areacollisionshape.global_position += Globals.grid.piece_size / 2
areacollisionshape.shape.extents = Vector2(rect_size.x / 2, rect_size.y / 2)
- algebraic_string = Utils.calculate_algebraic_position(real_position)
+ algebraic_string = Utils.to_algebraic(real_position)
func _on_Squarea_input_event(_viewport: Node, _event: InputEvent, _shape_idx: int) -> void:
diff --git a/Utils.gd b/Utils.gd
index 84199af..94be0bb 100644
--- a/Utils.gd
+++ b/Utils.gd
@@ -25,10 +25,20 @@ func add_move(move) -> void:
emit_signal("newmove", move)
-func calculate_algebraic_position(real_position) -> String:
+func reset_vars() -> void:
+ turn_moves.resize(0)
+ turns_moves.resize(0)
+ counter = 0
+
+
+func to_algebraic(real_position) -> String:
return char(65 + (real_position.x)).to_lower() + str(8 - real_position.y)
+func from_algebraic(algebraic_position: String) -> Vector2:
+ return Vector2(ord(algebraic_position[0]) - ord("a"), 8 - int(algebraic_position[1]))
+
+
func get_node_name(node) -> Array:
if is_pawn(node):
return ["♙", "p"] if node.white else ["♟", "p"]
@@ -65,12 +75,8 @@ func walk_dir(path = "res://assets/pieces") -> PoolStringArray: # walk the dire
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]
+ var format_string = "%02d:%04.1f" if use_milliseconds else "%02d:%02d"
+ return format_string % [time / 60, fmod(time, 60)]
func _on_turn_over() -> void:
@@ -96,7 +102,7 @@ func fen() -> String:
else:
pieces += str(empty)
else:
- pieces += spot.shortname[0].to_upper() if spot.white else spot.shortname[0].to_lower()
+ pieces += (spot.shortname[0].to_upper() if spot.white else spot.shortname[0].to_lower())
empty = 0
if rank != 7:
pieces += "/"
@@ -115,12 +121,12 @@ func fen() -> String:
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))
+ enpassants += to_algebraic(pawn.real_position + (Vector2.DOWN * pawn.whiteint))
var fen = (
"%s %s %s %s %s %s"
% [
pieces,
- "w" if Globals.turn else "b",
+ "w" if Globals.team else "b",
castlingrights,
enpassants if enpassants else "-",
Globals.halfmove,
diff --git a/networking/Network.gd b/networking/Network.gd
new file mode 100644
index 0000000..6c8758c
--- /dev/null
+++ b/networking/Network.gd
@@ -0,0 +1,105 @@
+extends Node
+class_name Network
+
+var ws := WebSocketClient.new()
+var game_code := ""
+
+const HEADERS := {
+ "move": "M",
+ "joinrequest": "J",
+ "hostrequest": "H",
+ "stopgame": "K",
+ "ping": "P",
+ "startgame": "S",
+}
+
+const MOVEHEADERS := {
+ "take": "K",
+ "move": "M",
+ "castle": "C",
+ "passant": "P",
+ "promote": "Q",
+}
+
+var notation := ""
+
+signal start_game
+signal move_data(data)
+signal host_result(result)
+signal join_result(result)
+signal game_over(problem, isok)
+
+const url := "wss://gd-chess-server.herokuapp.com/"
+
+
+func _ready() -> void:
+ ws.connect("connection_established", self, "_connection_established")
+ ws.connect("connection_closed", self, "_connection_closed")
+ ws.connect("connection_error", self, "_connection_error")
+ ws.connect("data_received", self, "_data_recieved")
+ print("Connecting to server...") # maybe i shouldnt broadcast the server url
+ ws.connect_to_url(url)
+ var t = Timer.new()
+ add_child(t)
+ t.wait_time = 1
+ t.start(1)
+ t.connect("timeout", self, "ping")
+
+
+func ping() -> void:
+ send_packet("ping", HEADERS.ping)
+
+
+func _connection_established(_protocol) -> void:
+ print("Connection established")
+
+
+func _connection_closed(_was_clean_closed) -> void:
+ printerr("Connection closed")
+ emit_signal("game_over", "Connection closed", false)
+
+
+func _connection_error() -> void:
+ printerr("Connection error")
+ emit_signal("game_over", "Connection error", false)
+
+
+func send_move_packet(positions, header: String) -> void:
+ var packet := {"movetype": header, "gamecode": game_code, "positions": positions}
+ Globals.add_turn()
+ var globals = Globals.pack_vars()
+ for variable in globals:
+ packet[variable] = globals[variable]
+ send_packet(packet, HEADERS.move) # your move will wait till the server relays back :>
+
+
+func _data_recieved() -> void:
+ var recieve: Dictionary = ws.get_peer(1).get_var()
+ var header: String = recieve.header
+ var text = recieve.data
+ match header:
+ HEADERS.hostrequest:
+ emit_signal("host_result", text)
+ HEADERS.move:
+ emit_signal("move_data", text)
+ HEADERS.joinrequest:
+ emit_signal("join_result", text)
+ HEADERS.stopgame:
+ emit_signal("game_over", "your opponent requested stop", true)
+ HEADERS.startgame:
+ emit_signal("start_game")
+ HEADERS.ping:
+ pass
+
+
+func _process(_delta) -> void:
+ if (
+ ws.get_connection_status() == ws.CONNECTION_CONNECTING
+ or ws.get_connection_status() == ws.CONNECTION_CONNECTED
+ ):
+ ws.poll()
+
+
+func send_packet(variant, header: String) -> void:
+ if ws.get_peer(1).is_connected_to_host():
+ ws.get_peer(1).put_var({"header": header, "data": variant})
diff --git a/pieces/Pawn.gd b/pieces/Pawn.gd
index 80a180b..298e00a 100644
--- a/pieces/Pawn.gd
+++ b/pieces/Pawn.gd
@@ -33,7 +33,9 @@ func _ready() -> void:
func _exit_tree() -> void:
- Globals.pawns.remove(Globals.pawns.find(self))
+ var find = Globals.pawns.find(self)
+ if find != -1:
+ Globals.pawns.remove(find)
func moveto(position, real = true, take = false, override_moveto = false) -> void:
@@ -121,7 +123,6 @@ func en_passant(turncheck = true) -> Array: # in passing
func promote(position, type) -> void:
promote_prev_pos = real_position
if type == "take":
- print(real_position)
take(at_pos(position), true)
promotetake = true
else:
diff --git a/pieces/Piece.gd b/pieces/Piece.gd
index 38a63f7..c0a5aa0 100644
--- a/pieces/Piece.gd
+++ b/pieces/Piece.gd
@@ -48,7 +48,6 @@ func clear_clicked() -> void:
func algebraic_take_notation(position, startpos = real_position) -> String:
var starter := shortname if shortname != "p" else to_algebraic(startpos)[0]
- print(starter)
return starter + "x" + to_algebraic(position)
@@ -127,7 +126,9 @@ func traverse(arr := [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT]) ->
static func at_pos(vector: Vector2):
- return Globals.grid.matrix[vector.y][vector.x]
+ if is_instance_valid(Globals.grid):
+ return Globals.grid.matrix[vector.y][vector.x]
+ return null
func can_move() -> bool: # checks if you can legally move
@@ -182,6 +183,7 @@ static func set_circle(positions: Array, type := "move") -> void:
func checkcheck(pos) -> bool: # moves to position, then checks if your king is in check
+ # TODO: figure out why this function isnt working with can_move()
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
diff --git a/project.godot b/project.godot
index 18e9f39..72215d7 100644
--- a/project.godot
+++ b/project.godot
@@ -34,6 +34,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://pieces/Knight.gd"
}, {
+"base": "Node",
+"class": "Network",
+"language": "GDScript",
+"path": "res://networking/Network.gd"
+}, {
"base": "Piece",
"class": "Pawn",
"language": "GDScript",
@@ -60,6 +65,7 @@ _global_script_class_icons={
"Grid": "",
"King": "res://assets/pieces/california/wK.png",
"Knight": "res://assets/pieces/california/wN.png",
+"Network": "",
"Pawn": "res://assets/pieces/california/wP.png",
"Piece": "res://assets/pieces/california/wP.png",
"Queen": "res://assets/pieces/california/wQ.png",
@@ -76,6 +82,7 @@ config/custom_user_dir_name="chess"
boot_splash/image="res://icon.png"
boot_splash/bg_color=Color( 0.309804, 0.309804, 0.309804, 0.313726 )
config/icon="res://icon.png"
+config/quit_on_go_back=false
[autoload]
diff --git a/ui/Lobby.gd b/ui/Lobby.gd
index b7332e8..15e609f 100644
--- a/ui/Lobby.gd
+++ b/ui/Lobby.gd
@@ -1,94 +1,29 @@
-# Default game server port. Can be any number between 1024 and 49151.
-# Not on the list of registered or common ports as of November 2020:
-# https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
extends Control
-# Default game server port. Can be any number between 1024 and 49151.
-# Not on the list of registered or common ports as of November 2020:
-# https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
-const DEFAULT_PORT = 8910
-
-var peer = null
-
onready var address: LineEdit = $Back/Center/HBox/VBox/Address
-onready var host_button = $Back/Center/HBox/VBox/HBox/HostButton
-onready var join_button = $Back/Center/HBox/VBox/HBox/JoinButton
+onready var buttons = $Back/Center/HBox/VBox/buttons
onready var status_ok = $Back/Center/HBox/VBox/StatusOK
onready var status_fail = $Back/Center/HBox/VBox/StatusFail
-onready var port_forward_label = $Back/Center/HBox/VBox2/Portforward
-onready var find_public_ip_button = $Back/Center/HBox/VBox2/FindPublicIP
func toggle(onoff) -> void:
visible = onoff
-
-
-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")
- get_tree().connect("connected_to_server", self, "_connected_ok")
- get_tree().connect("connection_failed", self, "_connected_fail")
- get_tree().connect("server_disconnected", self, "_server_disconnected")
-
-
-#### Network callbacks from SceneTree ####
-
-
-# Callback from SceneTree.
-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.
- Utils.connect("game_over", self, "_end_game", [], CONNECT_DEFERRED)
-
- get_tree().get_root().add_child(chess)
-
-
-func _player_disconnected(_id) -> void:
- if get_tree().is_network_server():
- _end_game("Client disconnected")
+ if onoff:
+ for i in get_tree().get_nodes_in_group("control"):
+ i.mouse_filter = MOUSE_FILTER_STOP
else:
- _end_game("Server disconnected")
-
-
-# Callback from SceneTree, only for clients (not server).
-func _connected_ok() -> void:
- pass # This function is not needed for this project.
-
-
-# Callback from SceneTree, only for clients (not server).
-func _connected_fail() -> void:
- _set_status("Couldn't connect", false)
-
- get_tree().set_network_peer(null) # Remove peer.
- host_button.show()
- address.editable = true
-
-
-func _server_disconnected() -> void:
- _end_game("Server disconnected")
-
+ for i in get_tree().get_nodes_in_group("control"):
+ i.mouse_filter = MOUSE_FILTER_IGNORE
-##### Game creation functions ######
+func _handle_game_over(error, isok) -> void:
+ reset_buttons()
+ Globals.reset_vars()
+ end_game()
+ _set_status(error, isok)
-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).
- get_node("/root/World").free()
- show()
- get_tree().set_network_peer(null) # Remove peer.
- host_button.show()
- join_button.show()
- address.editable = true
-
-
-func _set_status(text, isok) -> void:
- # Simple way to show status.
+func _set_status(text, isok) -> void: # Simple way to show status.
if isok:
status_ok.set_text(text)
status_fail.set_text("")
@@ -101,35 +36,126 @@ func _set_status(text, isok) -> void:
status_ok.visible = len(status_ok.text) > 0
-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.
- if err != OK:
- # Is another server running?
- _set_status("Can't host, address in use.", false)
- return
+func _on_join_pressed() -> void:
+ Globals.network.game_code = validate_text()
+ Globals.network.send_packet(Globals.network.game_code, Globals.network.HEADERS.joinrequest)
+ address.editable = false
+ buttons.hide()
+
- get_tree().set_network_peer(peer)
- host_button.hide()
+func _on_HostButton_pressed() -> void:
+ Globals.network.game_code = validate_text()
+ Globals.network.send_packet(Globals.network.game_code, Globals.network.HEADERS.hostrequest)
address.editable = false
- join_button.hide()
- _set_status("Waiting for player...", true)
+ buttons.hide()
- # Only show hosting instructions when relevant.
+func validate_text(text = address.get_text()) -> String:
+ var pos = address.caret_position
+ text = text.strip_edges()
+ text = text.replace(" ", "_")
+ address.text = text
+ address.caret_position = pos
+ return text
-func _on_join_pressed() -> void:
- var ip = address.get_text()
- if not ip.is_valid_ip_address():
- _set_status("IP address is invalid", false)
- return
- peer = NetworkedMultiplayerENet.new()
- peer.set_compression_mode(NetworkedMultiplayerENet.COMPRESS_RANGE_CODER)
- peer.create_client(ip, DEFAULT_PORT)
- get_tree().set_network_peer(peer)
+func _ready() -> void:
+ get_tree().set_auto_accept_quit(false)
+ Events.connect("go_back", self, "end_game")
+ if !is_instance_valid(Globals.network):
+ Globals.network = Network.new()
+ Globals.network.connect("move_data", self, "_on_data")
+ Globals.network.connect("join_result", self, "_on_join_result")
+ Globals.network.connect("host_result", self, "_on_host_result")
+ Globals.network.connect("game_over", self, "_handle_game_over")
+ Globals.network.connect("start_game", self, "_start_game")
+ add_child(Globals.network)
+
+
+func end_game() -> void:
+ if get_tree().get_root().has_node("World"):
+ get_tree().get_root().get_node("World").queue_free()
+ toggle(true)
+
+func create_world() -> void:
+ var world = load("res://World.tscn").instance()
+ get_tree().get_root().add_child(world)
+ toggle(false)
-func _on_find_public_ip_pressed() -> void:
- OS.shell_open("https://icanhazip.com/")
+
+func _start_game() -> void:
+ create_world()
+
+
+func _on_join_result(accepted: String) -> void:
+ Globals.team = false
+ if accepted == "Y":
+ _set_status("Joined!", true)
+ Globals.network.send_packet("", Globals.network.HEADERS.startgame)
+ else:
+ _set_status(accepted, false)
+ reset_buttons()
+
+
+func reset_buttons() -> void:
+ buttons.show()
+ address.editable = true
+
+
+func _on_host_result(accepted: String) -> void:
+ Globals.team = true
+ if accepted == "Y":
+ _set_status("Hosted!", true)
+ else:
+ _set_status(accepted, false)
+ reset_buttons()
+
+
+func add_turn() -> void:
+ Events.emit_signal("just_before_turn_over")
+ Globals.add_turn()
+ Globals.turn = not Globals.turn
+ Events.emit_signal("turn_over")
+
+
+func _on_data(data: Dictionary) -> void:
+ Globals.fullmove = data["fullmove"]
+ Globals.turn = data["turn"]
+ Globals.halfmove = data["halfmove"]
+ match data["movetype"]:
+ Network.MOVEHEADERS.passant:
+ # en passant
+ var end_pos = Vector2(dict2vec(data["positions"][1]))
+ var start_piece = Piece.at_pos(Vector2(dict2vec(data["positions"][0])))
+ Piece.at_pos(Vector2(dict2vec(data["positions"][2]))).took() # kill the unfortunate
+ start_piece.passant(end_pos)
+ Network.MOVEHEADERS.take:
+ var start_piece = Piece.at_pos(Vector2(dict2vec(data["positions"][0])))
+ var end_piece = Piece.at_pos(Vector2(dict2vec(data["positions"][1])))
+ start_piece.take(end_piece)
+ Network.MOVEHEADERS.move:
+ var end_pos = Vector2(dict2vec(data["positions"][1]))
+ var start_piece = Piece.at_pos(Vector2(dict2vec(data["positions"][0])))
+ start_piece.moveto(end_pos)
+ Network.MOVEHEADERS.castle:
+ var king = Piece.at_pos(dict2vec(data["positions"]["king"]))
+ var rook = Piece.at_pos(dict2vec(data["positions"]["rook"]))
+ rook.moveto(dict2vec(data["positions"]["rookdestination"]), true, false, true)
+ Utils.add_move(king.castle(dict2vec(data["positions"]["kingdestination"])))
+
+
+func dict2vec(dict: Dictionary) -> Vector2:
+ return Vector2(dict["x"], dict["y"])
+
+
+func _notification(what: int) -> void:
+ if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST or what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST:
+ Globals.network.send_packet(Globals.network.game_code, Globals.network.HEADERS.stopgame)
+ yield(get_tree(), "idle_frame") # wait for the packet to send
+ get_tree().quit()
+
+
+func _on_Address_text_entered(new_text: String):
+ validate_text(new_text)
+ Globals.network.game_code = new_text
diff --git a/ui/Lobby.tscn b/ui/Lobby.tscn
index 02e0148..123bf11 100644
--- a/ui/Lobby.tscn
+++ b/ui/Lobby.tscn
@@ -1,12 +1,7 @@
-[gd_scene load_steps=5 format=2]
+[gd_scene load_steps=3 format=2]
[ext_resource path="res://ui/main.tres" type="Theme" id=1]
[ext_resource path="res://ui/Lobby.gd" type="Script" id=2]
-[ext_resource path="res://assets/ui/verdana-bold.ttf" type="DynamicFontData" id=3]
-
-[sub_resource type="DynamicFont" id=1]
-size = 30
-font_data = ExtResource( 3 )
[node name="Lobby" type="Control"]
anchor_right = 1.0
@@ -14,94 +9,76 @@ anchor_bottom = 1.0
theme = ExtResource( 1 )
script = ExtResource( 2 )
-[node name="Back" type="ColorRect" parent="."]
+[node name="Back" type="ColorRect" parent="." groups=["control"]]
anchor_right = 1.0
anchor_bottom = 1.0
color = Color( 0, 0, 0, 0.784314 )
-[node name="Center" type="CenterContainer" parent="Back"]
+[node name="Center" type="CenterContainer" parent="Back" groups=["control"]]
anchor_right = 1.0
anchor_bottom = 1.0
-[node name="HBox" type="HBoxContainer" parent="Back/Center"]
-margin_left = 437.0
-margin_top = 189.0
-margin_right = 763.0
-margin_bottom = 611.0
-
-[node name="VBox" type="VBoxContainer" parent="Back/Center/HBox"]
-margin_right = 311.0
-margin_bottom = 422.0
+[node name="HBox" type="HBoxContainer" parent="Back/Center" groups=["control"]]
+margin_left = 425.0
+margin_top = 286.0
+margin_right = 775.0
+margin_bottom = 513.0
-[node name="AddressLabel" type="Label" parent="Back/Center/HBox/VBox"]
-margin_right = 311.0
-margin_bottom = 50.0
-text = "address"
+[node name="VBox" type="VBoxContainer" parent="Back/Center/HBox" groups=["control"]]
+margin_right = 350.0
+margin_bottom = 227.0
-[node name="Address" type="LineEdit" parent="Back/Center/HBox/VBox"]
-margin_top = 65.0
-margin_right = 311.0
-margin_bottom = 171.0
+[node name="Address" type="LineEdit" parent="Back/Center/HBox/VBox" groups=["control"]]
+margin_right = 350.0
+margin_bottom = 106.0
+rect_min_size = Vector2( 350, 0 )
focus_mode = 1
-text = "127.0.0.1"
-placeholder_text = "127.0.0.1"
+text = "game_code"
+align = 1
+max_length = 10
+placeholder_text = "game_code"
caret_blink = true
caret_blink_speed = 0.35
-[node name="HBox" type="HBoxContainer" parent="Back/Center/HBox/VBox"]
-margin_top = 186.0
-margin_right = 311.0
-margin_bottom = 292.0
+[node name="buttons" type="HBoxContainer" parent="Back/Center/HBox/VBox" groups=["control"]]
+margin_top = 121.0
+margin_right = 350.0
+margin_bottom = 227.0
-[node name="HostButton" type="Button" parent="Back/Center/HBox/VBox/HBox"]
-margin_right = 154.0
+[node name="JoinButton" type="Button" parent="Back/Center/HBox/VBox/buttons" groups=["control"]]
+margin_right = 150.0
margin_bottom = 106.0
+rect_min_size = Vector2( 150, 0 )
focus_mode = 0
+size_flags_horizontal = 4
enabled_focus_mode = 0
-text = "host"
+text = "join"
-[node name="JoinButton" type="Button" parent="Back/Center/HBox/VBox/HBox"]
-margin_left = 169.0
-margin_right = 311.0
+[node name="HostButton" type="Button" parent="Back/Center/HBox/VBox/buttons" groups=["control"]]
+margin_left = 165.0
+margin_right = 350.0
margin_bottom = 106.0
focus_mode = 0
+size_flags_horizontal = 3
enabled_focus_mode = 0
-text = "join"
-
-[node name="StatusOK" type="Label" parent="Back/Center/HBox/VBox"]
-margin_top = 307.0
-margin_right = 311.0
-margin_bottom = 357.0
-custom_colors/font_color = Color( 1, 1, 1, 1 )
-
-[node name="StatusFail" type="Label" parent="Back/Center/HBox/VBox"]
-margin_top = 372.0
-margin_right = 311.0
-margin_bottom = 422.0
-custom_colors/font_color = Color( 0.698039, 0.415686, 0.415686, 1 )
-
-[node name="VBox2" type="VBoxContainer" parent="Back/Center/HBox"]
-margin_left = 326.0
-margin_right = 326.0
-margin_bottom = 422.0
+text = "host"
-[node name="Portforward" type="Label" parent="Back/Center/HBox/VBox2"]
+[node name="StatusOK" type="Label" parent="Back/Center/HBox/VBox" groups=["control"]]
visible = false
-margin_right = 600.0
-margin_bottom = 161.0
-rect_min_size = Vector2( 600, 0 )
-custom_fonts/font = SubResource( 1 )
-text = "If you want non-LAN clients to connect,
-make sure the port 8910 in UDP
-is forwarded on your router."
+margin_top = 242.0
+margin_right = 500.0
+margin_bottom = 292.0
+custom_colors/font_color = Color( 1, 1, 1, 1 )
autowrap = true
-[node name="FindPublicIP" type="LinkButton" parent="Back/Center/HBox/VBox2"]
+[node name="StatusFail" type="Label" parent="Back/Center/HBox/VBox" groups=["control"]]
visible = false
-margin_right = 295.0
-margin_bottom = 50.0
-text = "find public ip"
+margin_top = 307.0
+margin_right = 500.0
+margin_bottom = 357.0
+custom_colors/font_color = Color( 0.698039, 0.415686, 0.415686, 1 )
+autowrap = true
-[connection signal="pressed" from="Back/Center/HBox/VBox/HBox/HostButton" to="." method="_on_host_pressed"]
-[connection signal="pressed" from="Back/Center/HBox/VBox/HBox/JoinButton" to="." method="_on_join_pressed"]
-[connection signal="pressed" from="Back/Center/HBox/VBox2/FindPublicIP" to="." method="_on_find_public_ip_pressed"]
+[connection signal="text_entered" from="Back/Center/HBox/VBox/Address" to="." method="_on_Address_text_entered"]
+[connection signal="pressed" from="Back/Center/HBox/VBox/buttons/JoinButton" to="." method="_on_join_pressed"]
+[connection signal="pressed" from="Back/Center/HBox/VBox/buttons/HostButton" to="." method="_on_HostButton_pressed"]
diff --git a/ui/MovesList.gd b/ui/MovesList.gd
index 6da4285..d07be03 100644
--- a/ui/MovesList.gd
+++ b/ui/MovesList.gd
@@ -39,7 +39,7 @@ func create_san_label(text, alignment = Label.ALIGN_RIGHT) -> void:
func on_new_move(move) -> void:
var alignment = Label.ALIGN_RIGHT
- if Globals.turn: # black just moved
+ if !Globals.turn: # black just moved
alignment = Label.ALIGN_LEFT
create_number_label(Globals.fullmove)
number = 0
diff --git a/ui/Settings.tscn b/ui/Settings.tscn
index 5622ce3..dd74845 100644
--- a/ui/Settings.tscn
+++ b/ui/Settings.tscn
@@ -4,35 +4,35 @@
[ext_resource path="res://ui/Settings.gd" type="Script" id=2]
[ext_resource path="res://assets/pieces/california/wP.png" type="Texture" id=3]
-[node name="Settings" type="Control"]
+[node name="Settings" type="Control" groups=["control"]]
anchor_right = 1.0
anchor_bottom = 1.0
theme = ExtResource( 1 )
script = ExtResource( 2 )
-[node name="ColorRect" type="ColorRect" parent="."]
+[node name="ColorRect" type="ColorRect" parent="." groups=["control"]]
anchor_right = 1.0
anchor_bottom = 1.0
color = Color( 0, 0, 0, 0.862745 )
-[node name="HBoxContainer" type="HBoxContainer" parent="ColorRect"]
+[node name="HBoxContainer" type="HBoxContainer" parent="ColorRect" groups=["control"]]
anchor_right = 1.0
anchor_bottom = 1.0
alignment = 1
-[node name="VBoxContainer" type="VBoxContainer" parent="ColorRect/HBoxContainer"]
+[node name="VBoxContainer" type="VBoxContainer" parent="ColorRect/HBoxContainer" groups=["control"]]
margin_left = 225.0
margin_right = 591.0
margin_bottom = 800.0
alignment = 1
-[node name="BackButton" type="Button" parent="ColorRect/HBoxContainer/VBoxContainer"]
+[node name="BackButton" type="Button" parent="ColorRect/HBoxContainer/VBoxContainer" groups=["control"]]
margin_top = 261.0
margin_right = 366.0
margin_bottom = 367.0
text = "back"
-[node name="PieceSet" type="OptionButton" parent="ColorRect/HBoxContainer/VBoxContainer"]
+[node name="PieceSet" type="OptionButton" parent="ColorRect/HBoxContainer/VBoxContainer" groups=["control"]]
margin_top = 382.0
margin_right = 366.0
margin_bottom = 538.0
@@ -42,13 +42,13 @@ enabled_focus_mode = 0
text = "piece set"
icon = ExtResource( 3 )
-[node name="VBoxContainer2" type="VBoxContainer" parent="ColorRect/HBoxContainer"]
+[node name="VBoxContainer2" type="VBoxContainer" parent="ColorRect/HBoxContainer" groups=["control"]]
margin_left = 606.0
margin_right = 975.0
margin_bottom = 800.0
alignment = 1
-[node name="VsyncButton" type="CheckBox" parent="ColorRect/HBoxContainer/VBoxContainer2"]
+[node name="VsyncButton" type="CheckBox" parent="ColorRect/HBoxContainer/VBoxContainer2" groups=["control"]]
margin_top = 175.0
margin_right = 369.0
margin_bottom = 315.0
@@ -57,7 +57,7 @@ pressed = true
enabled_focus_mode = 0
text = "vsync"
-[node name="FullscreenButton" type="CheckBox" parent="ColorRect/HBoxContainer/VBoxContainer2"]
+[node name="FullscreenButton" type="CheckBox" parent="ColorRect/HBoxContainer/VBoxContainer2" groups=["control"]]
margin_top = 330.0
margin_right = 369.0
margin_bottom = 470.0
@@ -65,7 +65,7 @@ focus_mode = 0
enabled_focus_mode = 0
text = "fullscreen"
-[node name="Borderless" type="CheckBox" parent="ColorRect/HBoxContainer/VBoxContainer2"]
+[node name="Borderless" type="CheckBox" parent="ColorRect/HBoxContainer/VBoxContainer2" groups=["control"]]
margin_top = 485.0
margin_right = 369.0
margin_bottom = 625.0
diff --git a/ui/StartMenu.gd b/ui/StartMenu.gd
index f39cde1..6a11fc1 100644
--- a/ui/StartMenu.gd
+++ b/ui/StartMenu.gd
@@ -29,7 +29,7 @@ func _on_local_pressed() -> void:
func _on_quit_pressed() -> void:
- get_tree().quit()
+ get_tree().notification(MainLoop.NOTIFICATION_WM_QUIT_REQUEST)
func _on_settings_pressed() -> void:
diff --git a/ui/StartMenu.tscn b/ui/StartMenu.tscn
index 873b826..b05738c 100644
--- a/ui/StartMenu.tscn
+++ b/ui/StartMenu.tscn
@@ -5,20 +5,22 @@
[ext_resource path="res://ui/Settings.tscn" type="PackedScene" id=3]
[ext_resource path="res://ui/Lobby.tscn" type="PackedScene" id=4]
-[node name="StartMenu" type="Control"]
+[node name="StartMenu" type="Control" groups=["control"]]
anchor_right = 1.0
anchor_bottom = 1.0
+mouse_filter = 2
theme = ExtResource( 1 )
script = ExtResource( 2 )
timer_length = 2.8
nice_colors = [ Color( 0.784314, 0.427451, 0.427451, 1 ), Color( 0.913725, 0.847059, 0.403922, 1 ), Color( 0.380392, 0.741176, 0.647059, 1 ), Color( 0.321569, 0.368627, 0.858824, 1 ), Color( 0.843137, 0.133333, 0.133333, 1 ), Color( 0.109804, 0.160784, 0.564706, 1 ), Color( 0.376471, 0.796078, 0.317647, 1 ), Color( 0.8, 0.364706, 0.588235, 1 ), Color( 0.984314, 0.858824, 0.282353, 1 ), Color( 0.164706, 0.0862745, 0.247059, 1 ) ]
-[node name="ColorRect" type="ColorRect" parent="."]
+[node name="ColorRect" type="ColorRect" parent="." groups=["control"]]
anchor_right = 1.0
anchor_bottom = 1.0
+mouse_filter = 2
color = Color( 0, 0, 0, 1 )
-[node name="MainButtons" type="VBoxContainer" parent="ColorRect"]
+[node name="MainButtons" type="VBoxContainer" parent="ColorRect" groups=["control"]]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
@@ -28,7 +30,7 @@ margin_top = -224.0
margin_right = 196.0
margin_bottom = 412.0
-[node name="multiplayer" type="Button" parent="ColorRect/MainButtons"]
+[node name="multiplayer" type="Button" parent="ColorRect/MainButtons" groups=["control"]]
margin_left = 36.0
margin_right = 351.0
margin_bottom = 106.0
@@ -37,7 +39,7 @@ size_flags_horizontal = 4
enabled_focus_mode = 0
text = "multiplayer"
-[node name="settings" type="Button" parent="ColorRect/MainButtons"]
+[node name="settings" type="Button" parent="ColorRect/MainButtons" groups=["control"]]
margin_left = 75.0
margin_top = 121.0
margin_right = 313.0
@@ -47,7 +49,8 @@ size_flags_horizontal = 4
enabled_focus_mode = 0
text = "settings"
-[node name="local" type="Button" parent="ColorRect/MainButtons"]
+[node name="local" type="Button" parent="ColorRect/MainButtons" groups=["control"]]
+visible = false
margin_left = 113.0
margin_top = 242.0
margin_right = 275.0
@@ -58,11 +61,11 @@ size_flags_horizontal = 4
enabled_focus_mode = 0
text = "local"
-[node name="quit" type="Button" parent="ColorRect/MainButtons"]
+[node name="quit" type="Button" parent="ColorRect/MainButtons" groups=["control"]]
margin_left = 123.0
-margin_top = 363.0
+margin_top = 242.0
margin_right = 265.0
-margin_bottom = 469.0
+margin_bottom = 348.0
focus_mode = 0
size_flags_horizontal = 4
enabled_focus_mode = 0
@@ -71,7 +74,7 @@ text = "exit"
[node name="Settings" parent="ColorRect" instance=ExtResource( 3 )]
visible = false
-[node name="Lobby" parent="ColorRect" instance=ExtResource( 4 )]
+[node name="Lobby" parent="ColorRect" groups=["control"] instance=ExtResource( 4 )]
visible = false
[node name="Tween" type="Tween" parent="."]
diff --git a/ui/Timer.gd b/ui/Timer.gd
index 6915862..4896962 100644
--- a/ui/Timer.gd
+++ b/ui/Timer.gd
@@ -15,7 +15,7 @@ func _ready() -> void:
func _process(delta) -> void:
if !enabled:
return
- if Globals.turn:
+ if Globals.team:
if !whitelabel.set_time(whitelabel.time - delta):
enabled = false
else:
diff --git a/ui/TimerLabels.gd b/ui/TimerLabels.gd
index 03e8aee..6885881 100644
--- a/ui/TimerLabels.gd
+++ b/ui/TimerLabels.gd
@@ -38,6 +38,6 @@ func _on_game_over() -> void:
func set_color() -> void:
if time > 10:
- colorrect.color = Globals.grid.clockrunning_color if Globals.turn == white else Color.transparent
+ colorrect.color = Globals.grid.clockrunning_color if Globals.team == white else Color.transparent
else:
- colorrect.color = Globals.grid.clockrunninglow if Globals.turn == white else Globals.grid.clocklow
+ colorrect.color = Globals.grid.clockrunninglow if Globals.team == white else Globals.grid.clocklow