online multiplayer chess game (note server currently down)
UNDOS
- workflow update? - fen support - threefold repetition DITCHED
bendn 2022-06-12
parent 0a438a1 · commit 327e727
-rw-r--r--.github/workflows/export.yml118
-rw-r--r--Board.gd228
-rw-r--r--FEN/Fen.gd83
-rw-r--r--Globals.gd16
-rw-r--r--PGN/PGN.gd1
-rw-r--r--PGN/test_pgns.gd2
-rw-r--r--SanParse/Move.gd26
-rw-r--r--SanParse/SanParse.gd24
-rw-r--r--Utils.gd85
-rw-r--r--assets/ui/draw.png4
-rw-r--r--assets/ui/flag.png.import2
-rw-r--r--assets/ui/svg/draw.svg4
-rw-r--r--assets/ui/svg/undo.svg3
-rw-r--r--assets/ui/undo.png3
-rw-r--r--assets/ui/undo.png.import35
-rw-r--r--export_presets.cfg10
-rw-r--r--networking/Network.gd39
-rw-r--r--pieces/B.gd (renamed from pieces/Bishop.gd)0
-rw-r--r--pieces/K.gd (renamed from pieces/King.gd)32
-rw-r--r--pieces/N.gd (renamed from pieces/Knight.gd)0
-rw-r--r--pieces/P.gd (renamed from pieces/Pawn.gd)60
-rw-r--r--pieces/Piece.gd24
-rw-r--r--pieces/Q.gd (renamed from pieces/Queen.gd)0
-rw-r--r--pieces/R.gd (renamed from pieces/Rook.gd)4
-rw-r--r--project.godot37
-rw-r--r--test.gd8
-rw-r--r--ui/barbutton/confirmbutton.gd38
-rw-r--r--ui/barbutton/drawbutton.gd38
-rw-r--r--ui/barbutton/resignbutton.gd33
-rw-r--r--ui/barbutton/undobutton.gd46
-rw-r--r--ui/chat/Chat.gd12
-rw-r--r--ui/confirm/Confirm.tscn4
-rw-r--r--ui/menus/StartMenu.tscn4
-rw-r--r--ui/menus/sidebarright/SidebarRight.tscn29
-rw-r--r--ui/menus/sidebarright/Timer.gd36
-rw-r--r--ui/menus/sidebarright/TimerLabels.gd16
-rw-r--r--ui/menus/sidebarright/sandisplay/SanDisplay.gd17
37 files changed, 704 insertions, 417 deletions
diff --git a/.github/workflows/export.yml b/.github/workflows/export.yml
index f0cdba4..7b912b9 100644
--- a/.github/workflows/export.yml
+++ b/.github/workflows/export.yml
@@ -1,26 +1,110 @@
+name: "export"
on:
push:
branches:
- main
+env:
+ GODOT_VERSION: 3.4.4
+ EXPORT_NAME: chess
+
jobs:
- export_game:
- permissions: write-all # can be read-all, write-all, or read-write
- # Always use ubuntu-latest for this action
- runs-on: ubuntu-latest
- name: export
+ export-windows:
+ name: Windows Export
+ runs-on: ubuntu-20.04
+ container:
+ image: docker://barichello/godot-ci:3.4.4
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ lfs: true
+ - name: Setup
+ run: |
+ mkdir -v -p ~/.local/share/godot/templates
+ mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable
+ - name: Windows Build
+ run: |
+ mkdir -v -p build/windows
+ godot -v --export "Windows" ./build/windows/$EXPORT_NAME.exe
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v1
+ with:
+ name: windows
+ path: build/windows
+
+ export-linux:
+ name: Linux Export
+ runs-on: ubuntu-20.04
+ container:
+ image: docker://barichello/godot-ci:3.4.4
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Setup
+ run: |
+ mkdir -v -p ~/.local/share/godot/templates
+ mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable
+ - name: Linux Build
+ run: |
+ mkdir -v -p build/linux
+ godot -v --export "Linux" ./build/linux/$EXPORT_NAME.x86_64
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v1
+ with:
+ name: linux
+ path: build/linux
+
+ export-web:
+ name: Web Export
+ runs-on: ubuntu-20.04
+ container:
+ image: docker://barichello/godot-ci:3.4.4
steps:
- - name: checkout
- uses: actions/[email protected]
- with: # Ensure that you get the entire project history
- fetch-depth: 0
+ - name: Checkout
+ uses: actions/checkout@v2
+ - name: Setup
+ run: |
+ mkdir -v -p ~/.local/share/godot/templates
+ mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable
+ - name: Web Build
+ run: |
+ mkdir -v -p build/web
+ godot -v --export "HTML" ./build/web/index.html
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v1
+ with:
+ name: web
+ path: build/web
+ - name: Install rsync 📚
+ run: |
+ apt-get update && apt-get install -y rsync
+ - name: Deploy to GitHub Pages 🚀
+ uses: JamesIves/github-pages-deploy-action@releases/v4
+ with:
+ branch: gh-pages # The branch the action should deploy to.
+ folder: build/web # The folder the action should deploy.
- - name: export game
- uses: firebelley/[email protected]
+ export-mac:
+ name: Mac Export
+ runs-on: ubuntu-20.04
+ container:
+ image: docker://barichello/godot-ci:3.4.4
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ lfs: true
+ - name: Setup
+ run: |
+ mkdir -v -p ~/.local/share/godot/templates
+ mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable
+ - name: Mac Build
+ run: |
+ mkdir -v -p build/mac
+ godot -v --export "Mac" ./build/mac/$EXPORT_NAME.zip
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v1
with:
- # Defining all the required inputs
- godot_executable_download_url: https://downloads.tuxfamily.org/godotengine/3.4.4/Godot_v3.4.4-stable_linux_headless.64.zip
- godot_export_templates_download_url: https://downloads.tuxfamily.org/godotengine/3.4.4/Godot_v3.4.4-stable_export_templates.tpz
- relative_project_path: ./
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ name: mac
+ path: build/mac
diff --git a/Board.gd b/Board.gd
index 7589a8a..d8a9cae 100644
--- a/Board.gd
+++ b/Board.gd
@@ -5,15 +5,6 @@ const PieceScene := preload("res://Piece.tscn")
const Square := preload("res://Square.tscn")
const piece_size := Vector2(80, 80)
-const default_metadata := {
- wccl = false, # white can castle left
- wccr = false, # white can castle right
- bccl = false, # black can castle left
- bccr = false, # black can castle right
- turn = true, # true = white, false = black
- wcep = [], # white can enpassant
- bcep = [], # black can enpassant
-}
export(Color) var overlay_color := Color(0.078431, 0.333333, 0.117647, 0.498039)
export(Color) var clockrunning_color := Color(0.219608, 0.278431, 0.133333)
@@ -23,10 +14,11 @@ export(Color) var clocklow := Color(0.313726, 0.156863, 0.14902)
var matrix := []
var stop_input := true
var background_array := []
-var history_matrixes := {}
var last_clicked: Piece = null
var flipped := false
+signal move_decided
+
var labels := {letters = [], numbers = []}
onready var background := $Background
@@ -36,6 +28,8 @@ onready var pieces := $Pieces
export(NodePath) onready var ui = get_node(ui)
export(NodePath) onready var sidebar = get_node(sidebar)
+enum { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING }
+
func _init() -> void:
Globals.grid = self
@@ -46,14 +40,13 @@ func _ready() -> void:
if Globals.network:
Globals.network.connect("move_data", self, "play_san")
init_board() # create the tile squares
- init_matrix() # create the pieces
+ init_matrix() # create 2d matrix
+ init_pieces() # create the pieces
init_labels() # add the labels
Events.connect("turn_over", self, "_on_turn_over") # listen for turn_over events
Events.connect("outoftime", self, "_on_outoftime") # listen for timeout events
Debug.monitor(self, "last_clicked")
- Debug.monitor(self, "meta", "matrix[8]")
- Debug.monitor(self, "highest value in 3fold", "threefoldrepetition()")
stop_input = false
@@ -81,8 +74,11 @@ static func print_matrix_pretty(mat: Array) -> void: # print the matrix
print(middle_header) # print the middle border
var row := "%s %s%s" % [ender.strip_edges(), 8 - j, ender] # init the string
for i in range(8): # for each column
- var c: Piece = r[i] # get the column
- row += "%s%s" % [c.mininame, ender] if c else " " + ender # add the piece
+ if typeof(r[i]) != TYPE_STRING:
+ var c: Piece = r[i] # get the column
+ row += "%s%s" % [c.mininame, ender] if c else " " + ender # add the piece
+ else:
+ row += "%s%s" % [r[i] if r[i] else " ", ender]
print(row) # print the string
print("%s\n%s\n%s" % [middish_heads, letter_header, smaller_heads])
@@ -134,21 +130,34 @@ func init_labels() -> void:
foreground.offset = rect_global_position
for i in range(8):
labels.letters.append(
- init_label(i, Vector2(i, 7), "abcdefgh"[i], Label.VALIGN_BOTTOM, Label.ALIGN_LEFT, Vector2(10, -10))
+ init_label(
+ i,
+ Vector2(i, 7),
+ "abcdefgh"[i],
+ Label.VALIGN_BOTTOM,
+ Label.ALIGN_LEFT,
+ Vector2(10, -10)
+ )
)
labels.numbers.append(
- init_label(i, Vector2(7, i), str(8 - i), Label.VALIGN_TOP, Label.ALIGN_RIGHT, Vector2(-10, 10))
+ init_label(
+ i, Vector2(7, i), str(8 - i), Label.VALIGN_TOP, Label.ALIGN_RIGHT, Vector2(-10, 10)
+ )
)
-func init_label(i: int, position: Vector2, text: String, valign := 0, align := 0, off := Vector2.ZERO) -> Label:
+func init_label(
+ i: int, position: Vector2, text: String, valign := 0, align := 0, off := Vector2.ZERO
+) -> Label:
var label := Label.new()
label.rect_size = piece_size
label.align = align
label.valign = valign
label.rect_position = (position * piece_size) + off
label.text = text
- label.add_color_override("font_color", Globals.board_color1 if i % 2 == 0 else Globals.board_color2)
+ label.add_color_override(
+ "font_color", Globals.board_color1 if i % 2 == 0 else Globals.board_color2
+ )
var font: DynamicFont = load("res://ui/verdana-bold.tres").duplicate()
font.size = 15
label.add_font_override("font", font)
@@ -156,21 +165,6 @@ func init_label(i: int, position: Vector2, text: String, valign := 0, align := 0
return label
-func threefoldrepetition() -> int:
- return 0 if !history_matrixes.values() else history_matrixes.values().max()
-
-
-func mat2str(mat: Array = matrix) -> String:
- var string := ""
- for y in range(8):
- for x in range(8):
- var spot: Piece = mat[y][x]
- string += spot.mininame if spot else "*"
- for key in mat[8].keys(): # store the metadata
- string += "%s:%s" % [key, mat[8][key]]
- return string
-
-
func drawed(reason := "") -> void:
ui.set_status("draw by " + reason, 0)
Events.emit_signal("game_over")
@@ -193,7 +187,9 @@ func check_in_check(prin := false) -> bool: # check if in_check
for j in range(0, 8): # for each column
var spot: Piece = 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.can_attack_piece(
+ Globals.white_king if Globals.turn else Globals.black_king
+ ): # if it can take the king
if prin:
# control never flows here
Globals.in_check = true # set in_check
@@ -218,14 +214,16 @@ func init_matrix() -> void: # create the matrix
matrix.append([]) # add a row
for _j in range(8): # for each column
matrix[i].append(null) # add a square
- matrix.append(default_metadata.duplicate()) # metadata for threefold repetition check
- add_pieces() # add the pieces
-func make_piece(position: Vector2, script: String, white: bool = true) -> void: # make peace
+func make_piece(
+ position: Vector2, piece_type: int, white: bool = true, visible: bool = true
+) -> void: # make peace
var piece := PieceScene.instance() # create a piece
- piece.script = load("res://pieces/%s.gd" % script) # set the script
+ piece.name = Utils.to_str(piece_type)
+ piece.script = load("res://pieces/%s.gd" % Utils.to_str(piece_type)) # set the script
piece.real_position = position # set the real position
+ piece.visible = visible
piece.rect_global_position = position * piece_size # set the global position
piece.white = white # set its team
pieces.add_child(piece) # add the piece to the grid
@@ -249,52 +247,8 @@ func get_background_element(pos: Vector2) -> ColorRect:
return background_array[8 * pos.y + pos.x]
-func add_pieces() -> void: # add the pieces
- add_pawns()
- add_rooks()
- add_knights()
- add_bishops()
- add_queens()
- add_kings()
-
-
-func add_pawns() -> void:
- for i in range(8):
- make_piece(Vector2(i, 1), "Pawn", false)
- make_piece(Vector2(i, 6), "Pawn", true)
-
-
-func add_rooks() -> void:
- make_piece(Vector2(0, 0), "Rook", false)
- make_piece(Vector2(7, 0), "Rook", false)
- make_piece(Vector2(0, 7), "Rook", true)
- make_piece(Vector2(7, 7), "Rook", true)
-
-
-func add_knights() -> void:
- make_piece(Vector2(1, 0), "Knight", false)
- make_piece(Vector2(6, 0), "Knight", false)
- make_piece(Vector2(1, 7), "Knight", true)
- make_piece(Vector2(6, 7), "Knight", true)
-
-
-func add_bishops() -> void:
- make_piece(Vector2(2, 0), "Bishop", false)
- make_piece(Vector2(5, 0), "Bishop", false)
- make_piece(Vector2(2, 7), "Bishop", true)
- make_piece(Vector2(5, 7), "Bishop", true)
-
-
-func add_queens() -> void:
- make_piece(Vector2(3, 0), "Queen", false)
- make_piece(Vector2(3, 7), "Queen", true)
-
-
-func add_kings() -> void:
- make_piece(Vector2(4, 0), "King", false)
- make_piece(Vector2(4, 7), "King", true)
- Globals.white_king = matrix[7][4] # set the white king
- Globals.black_king = matrix[0][4] # set the black king
+func init_pieces(visible: bool = true) -> void: # add the pieces
+ load_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", visible)
func check_for_circle(position: Vector2) -> bool: # check for a circle, validating movement
@@ -320,9 +274,11 @@ func square_clicked(position: Vector2) -> void: # square clicked
if check_for_frame(position): # takeable
handle_take(position)
stop_input = true
+ emit_signal("move_decided")
elif check_for_circle(position): # see if theres a circle at the position
handle_move(position) # move
stop_input = true
+ emit_signal("move_decided")
if last_clicked:
last_clicked.clear_clicked() # remove the circles
last_clicked = null # set it to null
@@ -337,7 +293,9 @@ func handle_take(position: Vector2) -> void:
if Utils.is_pawn(last_clicked): # if its a pawn
if check_promote(last_clicked, position, "take"):
return
- var mov = Move.new(SanParse.from_str(last_clicked.shortname), [last_clicked.real_position, position], true)
+ var mov = Move.new(
+ SanParse.from_str(last_clicked.shortname), [last_clicked.real_position, position], true
+ )
Globals.network.send_mov(mov) # piece taking piece
@@ -347,7 +305,7 @@ func handle_move(position: Vector2) -> void:
var castle_data = last_clicked.can_castle[i]
if castle_data[0] == position:
# send some packet
- var mov = Move.new(SanParser.KING, Move.castle_type(castle_data[3]))
+ var mov = Move.new(SanParser.KING, castle_data[3])
Globals.network.send_mov(mov)
return
if Utils.is_pawn(last_clicked):
@@ -362,7 +320,9 @@ func handle_move(position: Vector2) -> void:
return
elif check_promote(pawn, position):
return
- var mov = Move.new(SanParse.from_str(last_clicked.shortname), [last_clicked.real_position, position])
+ var mov = Move.new(
+ SanParse.from_str(last_clicked.shortname), [last_clicked.real_position, position]
+ )
Globals.network.send_mov(mov)
@@ -388,45 +348,48 @@ func _on_outoftime(who: bool) -> void:
func _on_turn_over() -> void:
- var matstr := mat2str()
- # Log.debug("matstr: " + matstr)
- if !matstr in history_matrixes:
- # Log.debug("new matrix entry")
- history_matrixes[matstr] = 1
- else:
- # Log.debug(["matrix entry = ", history_matrixes[matstr], "+ 1"])
- history_matrixes[matstr] += 1
Globals.checking_piece = null # reset checking_piece
Globals.in_check = false # reset in_check
- matrix[8] = default_metadata.duplicate() # add the metadata to the matrix
- matrix[8].turn = Globals.turn
check_in_check(true) # check if in_check
if !can_move():
if Globals.in_check:
win(!Globals.turn, "checkmate")
else:
drawed("stalemate")
- elif threefoldrepetition() >= 3:
- drawed("threefold repetition")
func play_pgn(pgn: String, instant := false):
+ var hitlist = []
+ if instant:
+ kill_matrix()
+ hitlist = pieces.get_children()
+ else:
+ kill_pieces()
stop_input = true
- for san in Pgn.parse(pgn).moves:
- play_san(san, false, false) # instant is not working right right now
+ init_pieces(!instant) # if instant, hide the pieces
+ var mvtext = Pgn.parse(pgn).moves
+ for san in mvtext:
+ play_san(san, instant) # instant is not working right right now
# so just change the delay :>
if instant:
yield(get_tree(), "idle_frame")
else:
yield(get_tree().create_timer(.3), "timeout")
+ for i in range(8):
+ for j in range(8):
+ if matrix[i][j]:
+ matrix[i][j].update_visual_position()
+ matrix[i][j].show()
+ for c in hitlist:
+ if is_instance_valid(c):
+ c.free()
stop_input = false
func play_san(san: String, instant := false, set_input := true) -> void:
Log.debug("playing " + san)
var san_to_add := san
- var mov = SanParse.parse(san)
- mov.make_long()
+ var mov = SanParse.parse(san).make_long()
Globals.add_turn()
match mov.move_kind.type:
Move.MoveKind.CASTLE:
@@ -451,10 +414,12 @@ func play_san(san: String, instant := false, set_input := true) -> void:
# this handles promotion, taking, enpassant, and moves.
var positions = mov.move_kind.data
if mov.promotion != -1: # promotion part
- var promote_to = Utils.to_str(mov.promotion)
- Piece.at_pos(positions[0]).promote_to(promote_to, mov.is_capture, positions[1], instant)
+ Piece.at_pos(positions[0]).promote_to(
+ mov.promotion, mov.is_capture, positions[1], instant
+ )
elif mov.is_capture: # taking part
+ Globals.reset_halfmove()
if Piece.at_pos(positions[1]):
Piece.at_pos(positions[0]).take(Piece.at_pos(positions[1]), instant)
elif mov.piece == SanParser.PAWN: # enpassant part
@@ -462,6 +427,55 @@ func play_san(san: String, instant := false, set_input := true) -> void:
pawn.passant(positions[1], instant)
san_to_add += " e.p."
else: # a very normal move
- Piece.at_pos(positions[0]).moveto(positions[1], instant)
+ var piece = Piece.at_pos(positions[0])
+ piece.moveto(positions[1], instant)
+ Globals.reset_halfmove()
Utils.add_move(san_to_add)
stop_input = false if set_input else stop_input
+
+
+func load_fen(fen: String, visible: bool = true):
+ var data: Dictionary = Fen.parse(fen)
+ load_matrix(data.mat, visible)
+ Globals.turn = data.turn
+ Globals.fullmove = data.fullmove
+ Globals.halfmove = data.halfmove
+
+
+func load_matrix(mat: Array, visible: bool = true):
+ if visible:
+ kill_pieces()
+ for x in range(8):
+ for y in range(8):
+ var ret = from_str_with_team(mat[y][x])
+ if ret[0] != -1:
+ make_piece(Vector2(x, y), ret[0], ret[1], visible)
+
+
+func from_str_with_team(string: String) -> Array:
+ var result = SanParser.from_str(string)
+ if result != -1:
+ return [result, true]
+ result = SanParser.from_str(string.to_upper())
+ return [result, false]
+
+
+func kill_pieces():
+ for i in pieces.get_children():
+ i.free()
+ kill_matrix()
+
+
+func kill_matrix():
+ matrix = []
+ init_matrix()
+
+
+func undo():
+ Globals.turn = true
+ Globals.fullmove = 1
+ Globals.halfmove = 0
+ Globals.in_check = false
+ Globals.checking_piece = null
+ clear_fx()
+ play_pgn(Utils.pop_move(), true)
diff --git a/FEN/Fen.gd b/FEN/Fen.gd
new file mode 100644
index 0000000..da02550
--- /dev/null
+++ b/FEN/Fen.gd
@@ -0,0 +1,83 @@
+extends Node
+class_name FEN
+
+
+func parse(fen: String) -> Dictionary:
+ var reg = RegEx.new()
+ reg.compile(
+ "^(?<pieces>([pnbrqkPNBRQK1-8]{1,8}\\/?){8})\\s+(?<turn>b|w)\\s+(?<castling>-|K?Q?k?q)\\s+(?<enpassant>-|[a-h][3-6])\\s+(?<halfmove>\\d+)\\s+(?<fullmove>\\d+)\\s*$"
+ )
+ var res = reg.search(fen)
+ if res:
+ var mat: Array = []
+ var rows = res.strings[res.names.pieces].split("/")
+ for row in rows:
+ var append_row: Array = []
+ for col in row:
+ if int(col) != 0:
+ for _i in range(int(col)):
+ append_row.append("")
+ else:
+ append_row.append(col)
+ mat.append(append_row)
+ var fenobj = {
+ "mat": mat,
+ "turn": res.strings[res.names.turn] == "w",
+ "castling": res.strings[res.names.castling],
+ "enpassant": res.strings[res.names.enpassant],
+ "halfmove": int(res.strings[res.names.halfmove]),
+ "fullmove": int(res.strings[res.names.fullmove])
+ }
+ return fenobj
+ else:
+ Log.err("bad fen")
+ return {}
+
+
+func get_fen() -> String:
+ var pieces := ""
+ for rank in range(8):
+ var empty := 0
+ for file in range(8):
+ var spot: Piece = Globals.grid.matrix[rank][file]
+ if spot == null:
+ empty += 1
+ if len(pieces) > 0 and 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 = whitecastling.to_upper() + blackcastling.to_lower()
+ else:
+ castlingrights = "-"
+ # castling rights are slightly janke
+
+ var enpassants := ""
+ for pawn in Globals.pawns:
+ if pawn.just_double_stepped and pawn.just_set:
+ enpassants = Utils.to_algebraic(pawn.real_position + (Vector2.DOWN * pawn.whiteint))
+ break
+ return (
+ "%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
diff --git a/Globals.gd b/Globals.gd
index 0dfc0dc..a90b33b 100644
--- a/Globals.gd
+++ b/Globals.gd
@@ -33,14 +33,6 @@ func reset_vars() -> void:
Utils.reset_vars()
-func pack_vars() -> Dictionary:
- return {
- "fullmove": fullmove,
- "halfmove": halfmove,
- "turn": turn,
- }
-
-
func reset_halfmove() -> void:
halfmove = 0
__nosethalfmove = true
@@ -48,19 +40,17 @@ func reset_halfmove() -> void:
func add_turn() -> void:
Events.emit_signal("just_before_turn_over")
- if !turn:
+ turn = not turn
+ if turn: # white just moved
fullmove += 1
if __nosethalfmove:
__nosethalfmove = false
else:
halfmove += 1
- turn = not turn
Events.emit_signal("turn_over")
-func get_turn(flip := false) -> String:
- if flip:
- return "black" if turn else "white"
+func get_turn() -> String:
return "white" if turn else "black"
diff --git a/PGN/PGN.gd b/PGN/PGN.gd
index 52d15bc..30aa103 100644
--- a/PGN/PGN.gd
+++ b/PGN/PGN.gd
@@ -37,7 +37,6 @@ func parse(string) -> Dictionary:
return {}
else:
break
-
var movetext := PoolStringArray()
while !lines.empty():
var line = lines.pop_front().strip_edges()
diff --git a/PGN/test_pgns.gd b/PGN/test_pgns.gd
index 3144beb..2b727aa 100644
--- a/PGN/test_pgns.gd
+++ b/PGN/test_pgns.gd
@@ -15,7 +15,7 @@ func _load(i: int):
var boar = load("res://Game.tscn").instance()
get_tree().get_root().add_child(boar)
boar = boar.get_board()
- boar.play_pgn(pgns[i])
+ boar.play_pgn(pgns[i], true)
get_parent().hide()
diff --git a/SanParse/Move.gd b/SanParse/Move.gd
index afa07a9..f6e9ca1 100644
--- a/SanParse/Move.gd
+++ b/SanParse/Move.gd
@@ -21,7 +21,7 @@ static func castle_type(type: String) -> int:
return MoveKind.CASTLETYPES.QUEEN_SIDE if type == "O-O-O" else MoveKind.CASTLETYPES.KING_SIDE
-func set_check_type(type: String) -> void:
+func set_check_type(type: String) -> Move:
match type:
"+":
check_type = CHECKTYPES.CHECK
@@ -29,6 +29,7 @@ func set_check_type(type: String) -> void:
check_type = CHECKTYPES.CHECKMATE
_:
check_type = CHECKTYPES.NONE
+ return self
func compile() -> String: # compiles the structure to a san
@@ -37,11 +38,11 @@ func compile() -> String: # compiles the structure to a san
MoveKind.CASTLE:
res += move_kind.to_str()
MoveKind.NORMAL:
- res += Utils.to_str(piece)
+ res += to_str(piece)
res += Utils.to_algebraic(move_kind.data[0])
res += "x" if is_capture else ""
res += Utils.to_algebraic(move_kind.data[1])
- res += "=" + Utils.to_str(promotion) if promotion != -1 else ""
+ res += "=" + to_str(promotion) if promotion != -1 else ""
match check_type:
CHECKTYPES.CHECK:
res += "+"
@@ -50,6 +51,10 @@ func compile() -> String: # compiles the structure to a san
return res.strip_edges()
+static func to_str(type: int) -> String:
+ return " NBRQK"[type].strip_edges() # if its a pawn, return nothing
+
+
## tests
# print(Utils.to_algebraic(make_long(SanParse.parse("e4").move_kind.data, false, SanParser.PAWN)[0]) == "e2")
# print(Utils.to_algebraic(make_long(SanParse.parse("Nbc3").move_kind.data, false, SanParser.KNIGHT)[0]) == "b1")
@@ -60,15 +65,15 @@ func compile() -> String: # compiles the structure to a san
# print(Utils.to_algebraic(make_long(SanParse.parse("Nbxc3").move_kind.data, false, SanParser.KNIGHT)[0]) == "b1")
# print(Utils.to_algebraic(make_long(SanParse.parse("N1xc3").move_kind.data, false, SanParser.KNIGHT)[0]) == "b1")
### fix short san
-func make_long():
+func make_long() -> Move:
if move_kind.type == MoveKind.CASTLE:
- return
+ return self
var newvecs: PoolVector2Array = []
var vectors = move_kind.data
if Piece.is_on_board(vectors[0]): # [0] is the only one with -1(s) possible
- return vectors
+ return self
if is_capture:
newvecs.append(long_helper(vectors[0], true, false, vectors[1]))
@@ -77,10 +82,11 @@ func make_long():
if newvecs.empty():
Log.error("cruddlesticks")
- return
+ return self
newvecs.append(vectors[1])
move_kind.data = newvecs
+ return self
func long_helper(vec: Vector2, attack: bool, move: bool, touch: Vector2):
@@ -103,7 +109,11 @@ func long_helper(vec: Vector2, attack: bool, move: bool, touch: Vector2):
func long_helper_helper(spot, touch, attack, move):
- return Utils.spotispiece(piece, spot) and spot.white == Globals.turn and spot.can_touch(touch, attack, move)
+ return (
+ Utils.spotispiece(piece, spot)
+ and spot.white == Globals.turn
+ and spot.can_touch(touch, attack, move)
+ )
class MoveKind:
diff --git a/SanParse/SanParse.gd b/SanParse/SanParse.gd
index a93b430..cd7d889 100644
--- a/SanParse/SanParse.gd
+++ b/SanParse/SanParse.gd
@@ -30,7 +30,7 @@ const UNKNOWN_POS = Vector2(-1, -1)
enum { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING }
-func from_str(string: String) -> int:
+static func from_str(string: String) -> int:
var find = " NBRQK".find(string)
if find != -1:
return find
@@ -54,15 +54,13 @@ func regexmatch(san: String) -> Move:
var re: RegExMatch = regexs.pawn_move.search(san)
if re:
var cap = re.strings
- var mov = Move.new(PAWN, [UNKNOWN_POS, pos(cap[1], cap[2])])
- mov.set_check_type(cap[3])
+ var mov = Move.new(PAWN, [UNKNOWN_POS, pos(cap[1], cap[2])]).set_check_type(cap[3])
return mov
re = regexs.long_pawn_move.search(san)
if re:
var cap = re.strings
- var mov = Move.new(PAWN, [pos(cap[1], cap[2]), pos(cap[3], cap[4])])
- mov.set_check_type(cap[5])
+ var mov = Move.new(PAWN, [pos(cap[1], cap[2]), pos(cap[3], cap[4])]).set_check_type(cap[5])
return mov
re = regexs.piece_movement.search(san)
@@ -75,14 +73,18 @@ func regexmatch(san: String) -> Move:
re = regexs.specific_row_piece_movement.search(san)
if re:
var cap = re.strings
- var mov = Move.new(from_str(cap[1]), [Vector2(-1, Utils.row_pos(cap[2])), pos(cap[3], cap[4])])
+ var mov = Move.new(
+ from_str(cap[1]), [Vector2(-1, Utils.row_pos(cap[2])), pos(cap[3], cap[4])]
+ )
mov.set_check_type(cap[5])
return mov
re = regexs.specific_column_piece_movement.search(san)
if re:
var cap = re.strings
- var mov = Move.new(from_str(cap[1]), [Vector2(Utils.col_pos(cap[2]), -1), pos(cap[3], cap[4])])
+ var mov = Move.new(
+ from_str(cap[1]), [Vector2(Utils.col_pos(cap[2]), -1), pos(cap[3], cap[4])]
+ )
mov.set_check_type(cap[5])
return mov
@@ -119,14 +121,18 @@ func regexmatch(san: String) -> Move:
re = regexs.specific_column_piece_capture.search(san)
if re:
var cap = re.strings
- var mov = Move.new(from_str(cap[1]), [Vector2(Utils.col_pos(cap[2]), -1), pos(cap[3], cap[4])], true)
+ var mov = Move.new(
+ from_str(cap[1]), [Vector2(Utils.col_pos(cap[2]), -1), pos(cap[3], cap[4])], true
+ )
mov.set_check_type(cap[5])
return mov
re = regexs.specific_row_piece_capture.search(san)
if re:
var cap = re.strings
- var mov = Move.new(from_str(cap[1]), [Vector2(-1, Utils.row_pos(cap[2])), pos(cap[3], cap[4])], true)
+ var mov = Move.new(
+ from_str(cap[1]), [Vector2(-1, Utils.row_pos(cap[2])), pos(cap[3], cap[4])], true
+ )
mov.set_check_type(cap[5])
return mov
diff --git a/Utils.gd b/Utils.gd
index d255488..99fafca 100644
--- a/Utils.gd
+++ b/Utils.gd
@@ -3,6 +3,7 @@ extends Node
var internet := false
signal newmove(move)
signal newfen(fen)
+signal pop_move(fen, was_num)
var moves_list: PoolStringArray = []
var fen := ""
@@ -13,56 +14,21 @@ func get_pgn():
func _on_turn_over() -> void:
- fen = get_fen()
+ fen = Fen.get_fen()
Log.info("fen: " + fen)
emit_signal("newfen", fen)
-func spotispiece(piece_type: int, spot: Piece) -> bool:
- return SanParse.from_str(spot.shortname.to_upper()) == piece_type if spot else false
+func pop_move() -> String:
+ emit_signal("pop_move")
+ moves_list.remove(moves_list.size() - 1)
+ var pgn = get_pgn()
+ moves_list.resize(0)
+ return pgn
-func get_fen() -> String:
- var pieces := ""
- for rank in range(8):
- var empty := 0
- for file in range(8):
- var spot: Piece = Globals.grid.matrix[rank][file]
- if spot == null:
- empty += 1
- if len(pieces) > 0 and 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 = whitecastling.to_upper() + blackcastling.to_lower()
- else:
- castlingrights = "-"
-
- var enpassants := ""
- for pawn in Globals.pawns:
- if pawn.just_double_stepped and pawn.just_set:
- enpassants += Utils.to_algebraic(pawn.real_position + (Vector2.DOWN * pawn.whiteint))
- return (
- "%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
+func spotispiece(piece_type: int, spot: Piece) -> bool:
+ return SanParse.from_str(spot.shortname.to_upper()) == piece_type if spot else false
static func str_bool(string: String) -> bool:
@@ -94,7 +60,6 @@ func get_args() -> Dictionary:
func _ready() -> void:
- internet_available()
Events.connect("turn_over", self, "_on_turn_over")
if "help" in get_args():
print("usage: ./chess%s [debug | help]" % exec_ext())
@@ -103,6 +68,16 @@ func _ready() -> void:
get_tree().quit() # dont wait
Debug.monitor(self, "fen")
Debug.monitor(self, "pgn", "get_pgn()")
+ var t = Timer.new()
+ add_child(t)
+ t.name = "t"
+ t.connect("timeout", self, "_on_timeout", [t])
+ _on_timeout(t)
+
+
+func _on_timeout(timer: Timer) -> void:
+ timer.start(600) # every 10m
+ request() # ping server so it doesnt go down
static func exec_ext() -> String:
@@ -146,14 +121,14 @@ static func get_node_name(node: Node) -> Array:
return ["", ""]
-func internet_available() -> bool:
+func request() -> int: # returns err
var http := HTTPRequest.new()
add_child(http)
- var httpurl := "https://1.1.1.1"
- var returnable := http.request(httpurl) == OK
- http.queue_free()
- internet = returnable
- return returnable
+ var httpurl := Network.url.replace("wss://", "http://")
+ var error := http.request(httpurl)
+ http.free()
+ internet = error == OK
+ return error
func walk_dir(path := "res://assets/pieces", only_dir := true, exclude := []) -> PoolStringArray: # walk the directory, finding the asset packs
@@ -182,7 +157,10 @@ func format_seconds(time: float, use_milliseconds: bool = false) -> String:
func _notification(what: int) -> void:
- if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST or what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST:
+ if (
+ what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST
+ or what == MainLoop.NOTIFICATION_WM_GO_BACK_REQUEST
+ ):
Log.debug("Bye!")
@@ -205,9 +183,10 @@ static func from_algebraic(pos: String) -> Vector2:
static func to_str(type: int) -> String:
- return " NBRQK"[type].strip_edges() # if its a pawn, return nothing
+ return "PNBRQK"[type]
+# cant wait for 4.0 dict.merge(dict) :C
static func append_dict(dict: Dictionary, newdict: Dictionary) -> Dictionary:
for key in newdict:
dict[key] = newdict[key]
diff --git a/assets/ui/draw.png b/assets/ui/draw.png
index 5c1cae7..8e9cf86 100644
--- a/assets/ui/draw.png
+++ b/assets/ui/draw.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:938a831facef14c00c2cfd69898cf62aef52c3f33a9e3353322a21d13682e388
-size 644
+oid sha256:1de02f935449aa8801afe6bd3595cc1e910af713869c3153b5eb20dfc2a686a1
+size 3806
diff --git a/assets/ui/flag.png.import b/assets/ui/flag.png.import
index 4b333f1..c4498fe 100644
--- a/assets/ui/flag.png.import
+++ b/assets/ui/flag.png.import
@@ -20,7 +20,7 @@ compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
flags/repeat=0
-flags/filter=true
+flags/filter=false
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
diff --git a/assets/ui/svg/draw.svg b/assets/ui/svg/draw.svg
index 27c7f10..6f57116 100644
--- a/assets/ui/svg/draw.svg
+++ b/assets/ui/svg/draw.svg
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ba8a0b64ab69629c16aa711fb491e18d6faa118c8a1aa9f7af3152d346198a2b
-size 1519
+oid sha256:9bd1bcd279dfa4ae4e7e77ea1aef70b74b12fcbc4049032e6f0dccfea413193e
+size 2699
diff --git a/assets/ui/svg/undo.svg b/assets/ui/svg/undo.svg
new file mode 100644
index 0000000..1e9ba7f
--- /dev/null
+++ b/assets/ui/svg/undo.svg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6de601cb55b98d499b61c94c1b54274469ada71dc4500a6acc594d0519c5cb62
+size 1773
diff --git a/assets/ui/undo.png b/assets/ui/undo.png
new file mode 100644
index 0000000..99cec56
--- /dev/null
+++ b/assets/ui/undo.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b21bf6fd833e8212031384a2c8df03eea5dc2a2244401e3e4f99878558644142
+size 3291
diff --git a/assets/ui/undo.png.import b/assets/ui/undo.png.import
new file mode 100644
index 0000000..9c9d227
--- /dev/null
+++ b/assets/ui/undo.png.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/undo.png-1a08869722195ead3fe48ffb6aa0ef0b.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/ui/undo.png"
+dest_files=[ "res://.import/undo.png-1a08869722195ead3fe48ffb6aa0ef0b.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=false
+svg/scale=1.0
diff --git a/export_presets.cfg b/export_presets.cfg
index 5316d5b..e3fd4f7 100644
--- a/export_presets.cfg
+++ b/export_presets.cfg
@@ -7,7 +7,7 @@ custom_features=""
export_filter="all_resources"
include_filter="COPYING.md, LICENSE"
exclude_filter=""
-export_path="exports/chess.zip"
+export_path=""
script_export_mode=1
script_encryption_key=""
@@ -40,7 +40,7 @@ custom_features=""
export_filter="all_resources"
include_filter="COPYING.md, LICENSE"
exclude_filter="assets/pieces/alpha/*, assets/pieces/cburnett/*, assets/pieces/fresca/*, assets/pieces/governor/*, assets/pieces/horsey/*, assets/pieces/libra/*, assets/pieces/maestro/*, assets/pieces/pixel/*"
-export_path="exports/chess.html"
+export_path=""
script_export_mode=1
script_encryption_key=""
@@ -70,12 +70,12 @@ progressive_web_app/background_color=Color( 0, 0, 0, 1 )
name="Windows"
platform="Windows Desktop"
-runnable=false
+runnable=true
custom_features=""
export_filter="all_resources"
include_filter="COPYING.md, LICENSE"
exclude_filter=""
-export_path="exports/chess.exe"
+export_path=""
script_export_mode=1
script_encryption_key=""
@@ -116,7 +116,7 @@ custom_features=""
export_filter="all_resources"
include_filter="COPYING.md, LICENSE"
exclude_filter=""
-export_path="exports/linux/chess.x86_64"
+export_path=""
script_export_mode=1
script_encryption_key=""
diff --git a/networking/Network.gd b/networking/Network.gd
index 7b19c8f..8496386 100644
--- a/networking/Network.gd
+++ b/networking/Network.gd
@@ -15,11 +15,11 @@ const HEADERS := {
"relay": "R", # relay goes to both
"signal": "S", # signal is one way
"loadpgn": "L", # server telling me to load a pgn
- "info": "I"
+ "info": "I",
+ "move": "M",
+ "undo": "<"
}
-const MOVEHEADERS := {move = "M", take = "K", castle = "C", passant = "P", promote = "Q"} # subheaders for HEADERS.move
-
const RELAYHEADERS := {chat = "C"}
const SIGNALHEADERS := {takeback = "T", draw = "D", resign = "R", info = "I"} # subheaders for HEADERS.signal
@@ -33,6 +33,7 @@ signal game_over(problem, isok)
signal connection_established
signal signal_recieved(what)
signal chat(text)
+signal undo(undo)
signal info_recieved(info)
## for accounts(mostly)
@@ -82,25 +83,29 @@ func _connection_error() -> void:
func signal(body: Dictionary, header: String, _mainheader := HEADERS.signal) -> Dictionary:
- var data: Dictionary = Utils.append_dict({"type": header, "gamecode": game_code}, body)
- send_packet(data, _mainheader)
+ var data: Dictionary = Utils.append_dict({"type": header}, body)
+ send_gamecode_packet(data, _mainheader)
return data
func join_game(game: String) -> void:
- send_packet(Utils.append_dict({"gamecode": game}, SaveLoad.get_public_info()), HEADERS.joinrequest)
+ send_gamecode_packet(SaveLoad.get_public_info(), HEADERS.joinrequest, game)
func host_game(game: String) -> void:
- send_packet(Utils.append_dict({"gamecode": game}, SaveLoad.get_public_info()), HEADERS.hostrequest)
+ send_gamecode_packet(SaveLoad.get_public_info(), HEADERS.hostrequest, game)
+
+
+func send_gamecode_packet(data: Dictionary, header: String, gamecode: String = game_code):
+ send_packet(Utils.append_dict({"gamecode": gamecode}, data), header)
-func relay_signal(body: Dictionary, header: String) -> Dictionary: # its really the same thing as signal()
- return signal(body, header, HEADERS.relay)
+func relay_signal(body: Dictionary, header: String, _mainheader := HEADERS.relay) -> Dictionary: # its really the same thing as signal()
+ return signal(body, header, _mainheader)
func send_mov(mov: Move):
- relay_signal({"move": mov.compile()}, MOVEHEADERS.move)
+ send_packet({move = mov.compile(), gamecode = game_code}, HEADERS.move)
func stopgame(reason: String) -> void:
@@ -108,12 +113,16 @@ func stopgame(reason: String) -> void:
func _data_recieved() -> void:
- if !OS.is_window_focused():
- OS.request_attention()
var recieve: Dictionary = ws.get_peer(1).get_var()
var header: String = recieve.header
var text = recieve.data
match header:
+ HEADERS.undo:
+ emit_signal("undo", text)
+ HEADERS.move:
+ if !OS.is_window_focused():
+ OS.request_attention()
+ emit_signal("move_data", text.move)
HEADERS.hostrequest:
emit_signal("host_result", text)
HEADERS.relay:
@@ -121,8 +130,6 @@ func _data_recieved() -> void:
match relay.type:
RELAYHEADERS.chat:
emit_signal("chat", relay)
- MOVEHEADERS.move:
- emit_signal("move_data", text.move)
HEADERS.joinrequest:
emit_signal("join_result", text)
HEADERS.info:
@@ -139,7 +146,9 @@ func _data_recieved() -> void:
PacketHandler.leaving = false
HEADERS.signal:
var signal: Dictionary = text
- emit_signal("signal_recieved", signal)
+ match signal.type:
+ _:
+ emit_signal("signal_recieved", signal)
HEADERS.signup:
emit_signal("signupresult", text)
HEADERS.signin:
diff --git a/pieces/Bishop.gd b/pieces/B.gd
index 18029fb..18029fb 100644
--- a/pieces/Bishop.gd
+++ b/pieces/B.gd
diff --git a/pieces/King.gd b/pieces/K.gd
index e0d56d7..ea253cc 100644
--- a/pieces/King.gd
+++ b/pieces/K.gd
@@ -4,8 +4,14 @@ class_name King, "res://assets/pieces/california/wK.png"
var castle_check := true
var can_castle := []
+enum { NONE, QUEEN_SIDE, KING_SIDE } # keep up to date with move.movekind.castletypes
+
func _ready() -> void:
+ if white:
+ Globals.white_king = self
+ else:
+ Globals.black_king = self
Events.connect("just_before_turn_over", self, "just_before_over")
@@ -26,18 +32,6 @@ func get_moves(no_enemys := false, check_spots_check := true) -> PoolVector2Arra
func just_before_over() -> void: # assign metadata for threefold repetition draw check
castleing()
- if can_castle.size() > 0:
- for i in can_castle:
- if i[3] == "O-O-O":
- if white:
- Globals.grid.matrix[8].wccl = true
- else:
- Globals.grid.matrix[8].bccl = true
- else:
- if white:
- Globals.grid.matrix[8].wccr = true
- else:
- Globals.grid.matrix[8].bccr = true
func castleing(justcheckrooks := false) -> Array:
@@ -68,25 +62,17 @@ func castleing(justcheckrooks := false) -> Array:
var posx3 := pos_around(direction * 3)
if at_pos(posx3) or checkcheck(posx3):
continue
- can_castle.append([posx2, rook, rook_motion[i], "O-O-O" if i == 1 else "O-O"])
+ can_castle.append([posx2, rook, rook_motion[i], QUEEN_SIDE if i == 1 else KING_SIDE])
moves.append(posx2)
if justcheckrooks:
moves.sort()
return moves
-func castle(position: Vector2, instant := false) -> String:
- var return_string := ""
- if can_castle.size() == 1:
- return_string = can_castle[0][3]
- else:
- for i in can_castle:
- if i[0] == position:
- return_string = i[3]
- break
+# basically a wrapper for move to
+func castle(position: Vector2, instant := false) -> void:
can_castle.clear()
moveto(position, instant)
- return return_string
func can_move() -> bool: # checks if you can legally move
diff --git a/pieces/Knight.gd b/pieces/N.gd
index dfb05c8..dfb05c8 100644
--- a/pieces/Knight.gd
+++ b/pieces/N.gd
diff --git a/pieces/Pawn.gd b/pieces/P.gd
index bc94c1c..c130948 100644
--- a/pieces/Pawn.gd
+++ b/pieces/P.gd
@@ -20,10 +20,11 @@ onready var popup: Popup = $Popup
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")
for i in range(4): # add 4 sprites
var newsprite: TextureButton = load("res://ui/PromotionPreview.tscn").instance()
- newsprite.texture_normal = load("%s%s%s.png" % [Globals.grid.ASSETS_PATH, team.to_lower(), promotables[i]])
+ newsprite.texture_normal = load(
+ "%s%s%s.png" % [Globals.grid.ASSETS_PATH, team.to_lower(), promotables[i]]
+ )
newsprite.name = promotables[i]
newsprite.connect("pressed", self, "_pressed", [newsprite.name])
previews.add_child(newsprite)
@@ -34,7 +35,6 @@ func open_previews() -> void:
var rect := popup.get_global_rect()
rect.position = rect_global_position
popup.popup(rect)
- # popup.visible = true
func _exit_tree() -> void:
@@ -44,14 +44,9 @@ func _exit_tree() -> void:
func moveto(position: Vector2, instant := false) -> void:
# check if 2 step
if !just_double_stepped and !has_moved:
- if white and real_position.y - position.y == 2:
- just_double_stepped = true
- just_set = true
- if !white and position.y - real_position.y == 2:
- just_double_stepped = true
- just_set = true
+ just_double_stepped = true
+ just_set = true
.moveto(position, instant)
- Globals.reset_halfmove()
func get_moves(_var := false, check_spots_check := true) -> PoolVector2Array:
@@ -61,9 +56,10 @@ func get_moves(_var := false, check_spots_check := true) -> PoolVector2Array:
var point: Vector2 = points[i]
point *= whiteint
point = pos_around(point)
- if is_on_board(point) and at_pos(point) == null:
- if i == 1 and has_moved or at_pos(pos_around(points[0] * whiteint)) != null:
- continue
+ if at_pos(point) == null:
+ if i == 1:
+ if has_moved or at_pos(pos_around(points[0] * whiteint)) != null:
+ continue
if check_spots_check and checkcheck(point):
continue
if is_on_board(point):
@@ -108,9 +104,9 @@ func en_passant(turncheck := true, check_spots_check := true) -> Array: # in pa
var passants := [pos_around(Vector2.LEFT), pos_around(Vector2.RIGHT)]
var moves := []
for i in passants:
- if !is_on_board(i) or !at_pos(i):
- continue
var spot := at_pos(i)
+ if !spot:
+ continue
if spot.white == white or !Utils.is_pawn(spot):
continue
if turncheck and white != Globals.turn:
@@ -136,11 +132,11 @@ func promote(position: Vector2, type: String) -> void:
open_previews()
-func promote_to(promote_to: String, is_capture: bool, position: Vector2, instant := false):
+func promote_to(promote_to: int, is_capture: bool, position: Vector2, instant := false):
if is_capture and at_pos(position):
at_pos(position).took(instant)
clear_clicked()
- Globals.grid.make_piece(position, piece(promote_to), white)
+ Globals.grid.make_piece(position, promote_to, white)
took()
@@ -153,39 +149,9 @@ func _pressed(promote_to: String) -> void:
Globals.network.send_mov(mov)
-static func piece(string: String) -> String:
- match string:
- "Q":
- return "Queen"
- "N":
- return "Knight"
- "R":
- return "Rook"
- "B":
- return "Bishop"
- _:
- return "Piece"
-
-
func _on_turn_over() -> void:
if just_set:
just_set = false
return
if just_double_stepped:
just_double_stepped = false
-
-
-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
- en_passant(false)
- var temporary := []
- for i in enpassant:
- temporary.append(i[0])
- if !temporary:
- return
- if white:
- Globals.grid.matrix[8].wcep.append_array(temporary)
- else:
- Globals.grid.matrix[8].bcep.append_array(temporary)
diff --git a/pieces/Piece.gd b/pieces/Piece.gd
index 6764e62..da61d93 100644
--- a/pieces/Piece.gd
+++ b/pieces/Piece.gd
@@ -27,6 +27,8 @@ func _ready() -> void:
rect_pivot_offset = Globals.grid.piece_size / 2
frame.modulate = Globals.grid.overlay_color
colorrect.color = Globals.grid.overlay_color
+ sprite.flip_v = Globals.grid.flipped
+ sprite.flip_h = Globals.grid.flipped
load_texture()
@@ -34,7 +36,9 @@ func set_zindex(zindex: int, obj: CanvasItem = self):
VisualServer.canvas_item_set_z_index(obj.get_canvas_item(), zindex)
-func load_texture(path := "%s%s%s.png" % [Globals.grid.ASSETS_PATH, team, shortname.to_upper()]) -> void:
+func load_texture(
+ path := "%s%s%s.png" % [Globals.grid.ASSETS_PATH, team, shortname.to_upper()]
+) -> void:
sprite.texture = load(path)
@@ -51,7 +55,12 @@ func clear_clicked() -> void:
func move(newpos: Vector2) -> void: # dont use directly; use moveto
tween.interpolate_property(
- self, "rect_position", rect_position, newpos * Globals.grid.piece_size, 0.3, Tween.TRANS_BACK
+ self,
+ "rect_position",
+ rect_position,
+ newpos * Globals.grid.piece_size,
+ 0.3,
+ Tween.TRANS_BACK
)
var signresult := int(sign(real_position.x - newpos.x))
if signresult == 1:
@@ -69,7 +78,13 @@ func moveto(pos: Vector2, instant := false) -> void:
move(pos)
real_position = pos
SoundFx.play("Move")
+ else:
has_moved = true
+ real_position = pos
+
+
+func update_visual_position():
+ rect_position = real_position * Globals.grid.piece_size
func pos_around(around_vector: Vector2) -> Vector2:
@@ -91,7 +106,9 @@ static func all_dirs() -> PoolVector2Array:
)
-func traverse(arr: PoolVector2Array = [], no_enemys := false, check_spots_check := true) -> PoolVector2Array:
+func traverse(
+ arr: PoolVector2Array = [], no_enemys := false, check_spots_check := true
+) -> PoolVector2Array:
var circle_array: PoolVector2Array = []
for i in arr:
var pos := real_position
@@ -198,7 +215,6 @@ func take(piece: Piece, instant := false) -> void:
clear_clicked()
piece.took(instant)
moveto(piece.real_position, instant)
- Globals.reset_halfmove()
func took(instant := false) -> void: # called when piece is taken
diff --git a/pieces/Queen.gd b/pieces/Q.gd
index 6143b16..6143b16 100644
--- a/pieces/Queen.gd
+++ b/pieces/Q.gd
diff --git a/pieces/Rook.gd b/pieces/R.gd
index 40150d8..ed81f62 100644
--- a/pieces/Rook.gd
+++ b/pieces/R.gd
@@ -3,4 +3,6 @@ class_name Rook, "res://assets/pieces/california/wR.png"
func get_moves(no_enemys := false, check_spots_check := true) -> PoolVector2Array:
- return traverse([Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT], no_enemys, check_spots_check)
+ return traverse(
+ [Vector2.UP, Vector2.DOWN, Vector2.LEFT, Vector2.RIGHT], no_enemys, check_spots_check
+ )
diff --git a/project.godot b/project.godot
index fa975ae..bbd5ca7 100644
--- a/project.godot
+++ b/project.godot
@@ -17,7 +17,7 @@ _global_script_classes=[ {
"base": "Piece",
"class": "Bishop",
"language": "GDScript",
-"path": "res://pieces/Bishop.gd"
+"path": "res://pieces/B.gd"
}, {
"base": "Control",
"class": "ColorPickerBetter",
@@ -40,10 +40,20 @@ _global_script_classes=[ {
"path": "res://ui/confirm/confirm.gd"
}, {
"base": "BarTextureButton",
+"class": "ConfirmButton",
+"language": "GDScript",
+"path": "res://ui/barbutton/confirmbutton.gd"
+}, {
+"base": "ConfirmButton",
"class": "DrawButton",
"language": "GDScript",
"path": "res://ui/barbutton/drawbutton.gd"
}, {
+"base": "Node",
+"class": "FEN",
+"language": "GDScript",
+"path": "res://FEN/Fen.gd"
+}, {
"base": "LineEdit",
"class": "FENLabel",
"language": "GDScript",
@@ -77,12 +87,12 @@ _global_script_classes=[ {
"base": "Piece",
"class": "King",
"language": "GDScript",
-"path": "res://pieces/King.gd"
+"path": "res://pieces/K.gd"
}, {
"base": "Piece",
"class": "Knight",
"language": "GDScript",
-"path": "res://pieces/Knight.gd"
+"path": "res://pieces/N.gd"
}, {
"base": "Control",
"class": "Lobby",
@@ -117,7 +127,7 @@ _global_script_classes=[ {
"base": "Piece",
"class": "Pawn",
"language": "GDScript",
-"path": "res://pieces/Pawn.gd"
+"path": "res://pieces/P.gd"
}, {
"base": "Control",
"class": "Piece",
@@ -132,9 +142,9 @@ _global_script_classes=[ {
"base": "Piece",
"class": "Queen",
"language": "GDScript",
-"path": "res://pieces/Queen.gd"
+"path": "res://pieces/Q.gd"
}, {
-"base": "BarTextureButton",
+"base": "ConfirmButton",
"class": "ResignButton",
"language": "GDScript",
"path": "res://ui/barbutton/resignbutton.gd"
@@ -147,7 +157,7 @@ _global_script_classes=[ {
"base": "Piece",
"class": "Rook",
"language": "GDScript",
-"path": "res://pieces/Rook.gd"
+"path": "res://pieces/R.gd"
}, {
"base": "Node",
"class": "SanParser",
@@ -164,6 +174,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://ui/Status.gd"
}, {
+"base": "ConfirmButton",
+"class": "UndoButton",
+"language": "GDScript",
+"path": "res://ui/barbutton/undobutton.gd"
+}, {
"base": "Control",
"class": "UsernamePass",
"language": "GDScript",
@@ -176,7 +191,9 @@ _global_script_class_icons={
"ColorPickerButtonBetter": "",
"ColorSelect": "",
"Confirm": "",
+"ConfirmButton": "",
"DrawButton": "res://assets/ui/draw.png",
+"FEN": "",
"FENLabel": "",
"FlipButton": "res://assets/ui/flip_board.png",
"Grid": "",
@@ -201,6 +218,7 @@ _global_script_class_icons={
"SanParser": "",
"SaveLoader": "",
"StatusLabel": "",
+"UndoButton": "res://assets/ui/undo.png",
"UsernamePass": ""
}
@@ -229,6 +247,7 @@ Debug="*res://Debug.gd"
SanParse="*res://SanParse/SanParse.gd"
Pgn="*res://PGN/PGN.gd"
Log="*res://Log.gd"
+Fen="*res://FEN/Fen.gd"
[debug]
@@ -251,6 +270,10 @@ main_run_args="debug"
enabled=PoolStringArray( )
+[gui]
+
+theme/custom="res://ui/theme/main.tres"
+
[importer_defaults]
texture={
diff --git a/test.gd b/test.gd
index 735500b..007d455 100644
--- a/test.gd
+++ b/test.gd
@@ -19,7 +19,9 @@ class TestSan:
func assert_capture(mv: String, start: Vector2, dest: Vector2, piece: int) -> void:
assert_all(parse(mv), PoolVector2Array([start, dest]), piece, true)
- func assert_all(mv: Move, vectors: PoolVector2Array, piece: int, capture: bool, promote = -1) -> void:
+ func assert_all(
+ mv: Move, vectors: PoolVector2Array, piece: int, capture: bool, promote = -1
+ ) -> void:
assert(mv.move_kind.type == Move.MoveKind.NORMAL)
assert([mv.move_kind.data == vectors, mv.piece == piece, mv.is_capture == capture].min())
if promote != -1:
@@ -60,7 +62,9 @@ class TestSan:
assert_capture("exd4", Vector2(4, -1), Vector2(3, 4), PAWN)
func test_pawn_capture_promotion():
- assert_all(parse("exd8=Q"), PoolVector2Array([Vector2(4, -1), Vector2(3, 0)]), PAWN, true, QUEEN)
+ assert_all(
+ parse("exd8=Q"), PoolVector2Array([Vector2(4, -1), Vector2(3, 0)]), PAWN, true, QUEEN
+ )
func test_pawn_capture_long():
assert_capture("e3xd4", Vector2(4, 5), Vector2(3, 4), PAWN)
diff --git a/ui/barbutton/confirmbutton.gd b/ui/barbutton/confirmbutton.gd
new file mode 100644
index 0000000..d04e2ce
--- /dev/null
+++ b/ui/barbutton/confirmbutton.gd
@@ -0,0 +1,38 @@
+extends BarTextureButton
+class_name ConfirmButton
+
+const Confirm = preload("res://ui/confirm/Confirm.tscn")
+var waiting_on_answer: Confirm = null
+export(String) var confirm_text = ""
+
+export(NodePath) onready var status = get_node(status) as StatusLabel
+
+
+func _ready() -> void:
+ PacketHandler.connect("game_over", self, "set_disabled", [true])
+ if Globals.network:
+ Globals.network.connect("signal_recieved", self, "_signal_recieved")
+
+
+func confirm() -> void:
+ var confirm = Confirm.instance()
+ add_child(confirm)
+ confirm.confirm(self, confirm_text, 20)
+ waiting_on_answer = confirm
+
+
+func _signal_recieved(_signal: Dictionary) -> void:
+ pass
+
+
+func _confirmed(what: bool) -> void:
+ if waiting_on_answer:
+ if !waiting_on_answer.is_queued_for_deletion():
+ waiting_on_answer.queue_free()
+ waiting_on_answer = null
+ if what:
+ after_confirmed()
+
+
+func after_confirmed() -> void:
+ pass
diff --git a/ui/barbutton/drawbutton.gd b/ui/barbutton/drawbutton.gd
index 2003794..41d3c38 100644
--- a/ui/barbutton/drawbutton.gd
+++ b/ui/barbutton/drawbutton.gd
@@ -1,31 +1,16 @@
-extends BarTextureButton
+extends ConfirmButton
class_name DrawButton, "res://assets/ui/draw.png"
-const Confirm = preload("res://ui/confirm/Confirm.tscn")
-var waiting_on_answer: Confirm = null
-export(NodePath) onready var status = get_node(status) as StatusLabel
-
-
-func _ready() -> void:
- PacketHandler.connect("game_over", self, "set_disabled", [true])
- if Globals.network:
- Globals.network.connect("signal_recieved", self, "_on_signal")
-
-
-func _on_signal(what: Dictionary) -> void:
+func _signal_recieved(what: Dictionary) -> void:
if what.type == Network.SIGNALHEADERS.draw:
if "question" in what:
- var confirm = Confirm.instance()
- add_child(confirm)
- confirm.confirm(self, "Your opponent wants to draw", 20)
- waiting_on_answer = confirm
+ confirm()
else:
- disabled = false
if what.accepted:
drawed()
else:
- status.set_text("Your opponent rejected the draw")
+ status.set_text("Draw request rejected")
func drawed() -> GDScriptFunctionState:
@@ -36,17 +21,12 @@ func _pressed() -> void:
if waiting_on_answer:
_confirmed(true)
else:
- disabled = true
Globals.network.signal({"question": ""}, Network.SIGNALHEADERS.draw)
status.set_text("Draw request sent")
-func _confirmed(yes: bool) -> void: # called from confirmbar.confirmed
- if waiting_on_answer:
- if !waiting_on_answer.is_queued_for_deletion():
- waiting_on_answer.queue_free()
- disabled = false
- waiting_on_answer = null
- Globals.network.signal({"accepted": yes}, Network.SIGNALHEADERS.draw)
- if yes:
- drawed()
+func _confirmed(what: bool) -> void: # called from confirmbar.confirmed
+ ._confirmed(what)
+ Globals.network.signal({"accepted": what}, Network.SIGNALHEADERS.draw)
+ if what:
+ drawed()
diff --git a/ui/barbutton/resignbutton.gd b/ui/barbutton/resignbutton.gd
index 6fdfee4..a9baed5 100644
--- a/ui/barbutton/resignbutton.gd
+++ b/ui/barbutton/resignbutton.gd
@@ -1,19 +1,8 @@
-extends BarTextureButton
+extends ConfirmButton
class_name ResignButton, "res://assets/ui/flag.png"
-const Confirm = preload("res://ui/confirm/Confirm.tscn")
-var waiting_on_answer: Confirm = null
-export(NodePath) onready var status = get_node(status) as StatusLabel
-
-
-func _ready() -> void:
- PacketHandler.connect("game_over", self, "set_disabled", [true])
- if Globals.network:
- Globals.network.connect("signal_recieved", self, "resigned")
-
-
-func resigned(what: Dictionary) -> void:
+func _signal_recieved(what: Dictionary) -> void:
if what.type == Network.SIGNALHEADERS.resign:
Globals.grid.win(Globals.team, "resignation")
@@ -22,18 +11,10 @@ func _pressed() -> void:
if waiting_on_answer:
_confirmed(true)
else:
- var confirm = Confirm.instance()
- add_child(confirm)
- confirm.confirm(self, "Resign?", 20)
- waiting_on_answer = confirm
+ confirm()
-func _confirmed(what: bool) -> void:
- if waiting_on_answer:
- if !waiting_on_answer.is_queued_for_deletion():
- waiting_on_answer.queue_free()
- waiting_on_answer = null
- if what:
- Globals.network.signal({}, Network.SIGNALHEADERS.resign)
- Globals.grid.win(!Globals.team, "resignation")
- disabled = true
+func after_confirmed():
+ Globals.network.signal({}, Network.SIGNALHEADERS.resign)
+ Globals.grid.win(!Globals.team, "resignation")
+ disabled = true
diff --git a/ui/barbutton/undobutton.gd b/ui/barbutton/undobutton.gd
new file mode 100644
index 0000000..72c2190
--- /dev/null
+++ b/ui/barbutton/undobutton.gd
@@ -0,0 +1,46 @@
+extends ConfirmButton
+class_name UndoButton, "res://assets/ui/undo.png"
+
+
+func _ready():
+ Globals.network.connect("undo", self, "undo_recieved")
+
+
+func _pressed():
+ if waiting_on_answer:
+ _confirmed(true)
+ else:
+ if Utils.moves_list.size() == 0:
+ status.set_text("No moves to undo!")
+ return
+ elif Globals.turn == Globals.team:
+ status.set_text("It is your turn!")
+ return
+ Globals.network.send_packet(
+ {gamecode = Globals.network.game_code, question = ""}, Network.HEADERS.undo
+ )
+ status.set_text("Undo request sent")
+
+
+func undo_recieved(sig: Dictionary) -> void:
+ if "question" in sig:
+ confirm()
+ else:
+ if sig.accepted:
+ status.set_text("Undo request accepted")
+ undo()
+ else:
+ status.set_text("Undo request rejected")
+
+
+func _confirmed(what: bool) -> void:
+ ._confirmed(what)
+ Globals.network.send_packet(
+ {gamecode = Globals.network.game_code, accepted = what}, Network.HEADERS.undo
+ )
+ if what:
+ undo()
+
+
+func undo():
+ Globals.grid.undo()
diff --git a/ui/chat/Chat.gd b/ui/chat/Chat.gd
index 6b2a46a..e529720 100644
--- a/ui/chat/Chat.gd
+++ b/ui/chat/Chat.gd
@@ -40,7 +40,9 @@ func add_label_with(data):
add_label(string)
-func add_label(bbcode: String, name = "richtextlabel", size = Vector2(rect_size.x, 35)) -> RichTextLabel:
+func add_label(
+ bbcode: String, name = "richtextlabel", size = Vector2(rect_size.x, 35)
+) -> RichTextLabel:
var l = RichTextLabel.new()
l.name = name
l.rect_min_size = size
@@ -50,7 +52,9 @@ func add_label(bbcode: String, name = "richtextlabel", size = Vector2(rect_size.
l.connect("meta_clicked", self, "open_url")
l.bbcode_text = bbcode
# l.fit_content_height = true
- tween.interpolate_property(scrollbar, "value", scrollbar.value, scrollbar.max_value, .5, Tween.TRANS_BOUNCE)
+ tween.interpolate_property(
+ scrollbar, "value", scrollbar.value, scrollbar.max_value, .5, Tween.TRANS_BOUNCE
+ )
tween.start()
return l
@@ -67,7 +71,9 @@ func send(_arg = 0):
text.text = ""
var name_data = SaveLoad.get_data("id").name
var name = name_data if name_data else "Anonymous"
- Globals.network.relay_signal({"text": t, "who": name if name else "Anonymous"}, Network.RELAYHEADERS.chat)
+ Globals.network.relay_signal(
+ {"text": t, "who": name if name else "Anonymous"}, Network.RELAYHEADERS.chat
+ )
# markdown to bbcode
diff --git a/ui/confirm/Confirm.tscn b/ui/confirm/Confirm.tscn
index 6226aa9..b15ba1b 100644
--- a/ui/confirm/Confirm.tscn
+++ b/ui/confirm/Confirm.tscn
@@ -1,16 +1,18 @@
-[gd_scene load_steps=6 format=2]
+[gd_scene load_steps=7 format=2]
[ext_resource path="res://ui/theme/main.tres" type="Theme" id=1]
[ext_resource path="res://ui/barbutton/BarTextureButton.tscn" type="PackedScene" id=2]
[ext_resource path="res://assets/ui/close.png" type="Texture" id=3]
[ext_resource path="res://ui/confirm/confirm.gd" type="Script" id=4]
[ext_resource path="res://assets/ui/check.png" type="Texture" id=5]
+[ext_resource path="res://ui/verdana-bold-small.tres" type="DynamicFont" id=6]
[node name="Confirm" type="WindowDialog"]
margin_right = 576.0
margin_bottom = 168.0
rect_min_size = Vector2( 700, 0 )
theme = ExtResource( 1 )
+custom_fonts/title_font = ExtResource( 6 )
popup_exclusive = true
script = ExtResource( 4 )
diff --git a/ui/menus/StartMenu.tscn b/ui/menus/StartMenu.tscn
index 99e0533..e282f34 100644
--- a/ui/menus/StartMenu.tscn
+++ b/ui/menus/StartMenu.tscn
@@ -24,9 +24,9 @@ script = ExtResource( 7 )
[node name="tabs" type="TabContainer" parent="."]
margin_left = 89.0
-margin_top = 47.0
+margin_top = 23.0
margin_right = 1333.0
-margin_bottom = 753.0
+margin_bottom = 777.0
rect_min_size = Vector2( 2, 2 )
mouse_filter = 1
size_flags_horizontal = 0
diff --git a/ui/menus/sidebarright/SidebarRight.tscn b/ui/menus/sidebarright/SidebarRight.tscn
index 78b3046..7393a0a 100644
--- a/ui/menus/sidebarright/SidebarRight.tscn
+++ b/ui/menus/sidebarright/SidebarRight.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=29 format=2]
+[gd_scene load_steps=31 format=2]
[ext_resource path="res://ui/barbutton/drawbutton.gd" type="Script" id=1]
[ext_resource path="res://ui/barbutton/resignbutton.gd" type="Script" id=2]
@@ -14,8 +14,10 @@
[ext_resource path="res://assets/ui/Roboto-Medium.ttf" type="DynamicFontData" id=12]
[ext_resource path="res://ui/menus/sidebarright/SidebarRight.gd" type="Script" id=13]
[ext_resource path="res://ui/theme/main.tres" type="Theme" id=14]
+[ext_resource path="res://assets/ui/undo.png" type="Texture" id=15]
[ext_resource path="res://assets/ui/draw.png" type="Texture" id=16]
[ext_resource path="res://assets/ui/flip_board.png" type="Texture" id=17]
+[ext_resource path="res://ui/barbutton/undobutton.gd" type="Script" id=18]
[ext_resource path="res://ui/barbutton/flipbutton.gd" type="Script" id=19]
[ext_resource path="res://assets/ui/flag.png" type="Texture" id=20]
[ext_resource path="res://ui/menus/sidebarright/sandisplay/SanDisplay.tscn" type="PackedScene" id=21]
@@ -144,23 +146,36 @@ custom_constants/separation = 0
alignment = 1
[node name="FlipBoard" parent="V/buttonbarholder/Panel/buttonbar" instance=ExtResource( 6 )]
-margin_left = 166.0
-margin_right = 216.0
+margin_left = 141.0
+margin_right = 191.0
+hint_tooltip = "rotate the board"
texture_normal = ExtResource( 17 )
script = ExtResource( 19 )
[node name="DrawButton" parent="V/buttonbarholder/Panel/buttonbar" instance=ExtResource( 6 )]
-margin_left = 216.0
-margin_right = 266.0
+margin_left = 191.0
+margin_right = 241.0
+hint_tooltip = "request a draw"
texture_normal = ExtResource( 16 )
script = ExtResource( 1 )
+confirm_text = "Your opponent requests a draw"
status = NodePath("../../../../Status")
[node name="ResignButton" parent="V/buttonbarholder/Panel/buttonbar" instance=ExtResource( 6 )]
-margin_left = 266.0
-margin_right = 316.0
+margin_left = 241.0
+margin_right = 291.0
+hint_tooltip = "resign"
texture_normal = ExtResource( 20 )
script = ExtResource( 2 )
+confirm_text = "Resign?"
+status = NodePath("../../../../Status")
+
+[node name="UndoButton" parent="V/buttonbarholder/Panel/buttonbar" instance=ExtResource( 6 )]
+margin_left = 291.0
+margin_right = 341.0
+texture_normal = ExtResource( 15 )
+script = ExtResource( 18 )
+confirm_text = "Your opponent requests a takeback"
status = NodePath("../../../../Status")
[node name="SanDisplay" type="PanelContainer" parent="V"]
diff --git a/ui/menus/sidebarright/Timer.gd b/ui/menus/sidebarright/Timer.gd
index 0f128d3..e97d8d7 100644
--- a/ui/menus/sidebarright/Timer.gd
+++ b/ui/menus/sidebarright/Timer.gd
@@ -1,32 +1,22 @@
extends Node
-var enabled := false
+export(NodePath) var blacklabel
+export(NodePath) var whitelabel
+onready var labels = [get_node(blacklabel), get_node(whitelabel)]
-var count := 0
+var turn_time := 0.0
-export(NodePath) onready var whitelabel = get_node(whitelabel) as Label
-export(NodePath) onready var blacklabel = get_node(blacklabel) as Label
+func _process(delta):
+ # int of false is 0 and true is 1
+ turn_time += delta
+ labels[int(Globals.turn)].tick()
-func _ready() -> void:
- Events.connect("turn_over", self, "turn_over")
- # disable, because they work wierdly with laggy and stuff
- whitelabel.hide() # disable
- blacklabel.hide() # disable
- set_process(false) # disable
+func _move_decided():
+ prints("turn took", turn_time)
+ turn_time = 0.0
-func _process(delta: float) -> void:
- if !enabled:
- return
- if Globals.turn:
- if !whitelabel.set_time(whitelabel.time - delta):
- enabled = false
- else:
- if !blacklabel.set_time(blacklabel.time - delta):
- enabled = false
-
-func turn_over() -> void:
- count += 1
- enabled = count >= 2
+func _ready():
+ Globals.grid.connect("move_decided", self, "_move_decided")
diff --git a/ui/menus/sidebarright/TimerLabels.gd b/ui/menus/sidebarright/TimerLabels.gd
index 59e3374..81317a7 100644
--- a/ui/menus/sidebarright/TimerLabels.gd
+++ b/ui/menus/sidebarright/TimerLabels.gd
@@ -24,6 +24,10 @@ func set_time(newtime: float) -> bool:
return true
+func tick(delta: float):
+ time -= delta
+
+
func _ready() -> void:
set_time(STARTTIME)
set_color()
@@ -38,6 +42,14 @@ 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.turn == 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.turn == white
+ else Globals.grid.clocklow
+ )
diff --git a/ui/menus/sidebarright/sandisplay/SanDisplay.gd b/ui/menus/sidebarright/sandisplay/SanDisplay.gd
index 58ddbad..dc8410f 100644
--- a/ui/menus/sidebarright/sandisplay/SanDisplay.gd
+++ b/ui/menus/sidebarright/sandisplay/SanDisplay.gd
@@ -13,24 +13,29 @@ func _ready() -> void:
scroll_bar.step = .15
add_child(tween)
Utils.connect("newmove", self, "on_new_move")
+ Utils.connect("pop_move", self, "reset_moves")
func create_number_label(num: int) -> void:
var base = Base.instance()
sans.add_child(base)
yield(get_tree(), "idle_frame")
- base.number.text = "%s." % str(num)
+ base.number.text = "%s." % num
+ base.name = base.number.text
func add_move_to_label(move: String) -> void:
+ if !Globals.turn:
+ create_number_label(Globals.fullmove)
sans.get_children()[-1].add_move(move)
func on_new_move(move: String) -> void:
- if !Globals.turn: # black just moved
- yield(create_number_label(Globals.fullmove), "completed")
add_move_to_label(move)
- tween.interpolate_property( # scrolldown
- scroll_bar, "value", scroll_bar.value, scroll_bar.max_value, 0.5, Tween.TRANS_BOUNCE
- )
+ tween.interpolate_property(scroll_bar, "value", scroll_bar.value, scroll_bar.max_value, 0.5, 9) # bouncy
tween.start()
+
+
+func reset_moves():
+ for i in sans.get_children():
+ i.queue_free()