extends Node class_name game @export var X : int @export var Y : int @export var tile_size = 64 @export var dark = Color(0,0,0,1) @export var light =Color(1,1,1,1) @onready var dark_tile_shader: ShaderMaterial = ShaderMaterial.new() @onready var light_tile_shader: ShaderMaterial = ShaderMaterial.new() @onready var opponent_shader: ShaderMaterial = ShaderMaterial.new() @onready var background = $Background @onready var tile_container = $TileContainer @onready var shadow_container = $ShadowContainer @onready var piece_container = $PieceContainer @onready var board = [] var selected_piece var selected_piece_value var selected_piece_position var valid_moves = [] @onready var selected_shader: ShaderMaterial = ShaderMaterial.new() var target_position var moving: bool var sprite: Sprite2D @onready var explosion_effect = $Explosion func _ready() -> void: DisplayServer.window_set_min_size(Vector2(1152, 648)) # Set minimum window size #creates 2D array for board, with empty (null) spaces for x in range(X): board.append([]) for y in range(Y): board[x].append(null) #hard-coded setup of pieces board[0][7] = Rook.new(true, Vector2(0, 7)) board[1][7] = Bishop.new(true, Vector2(1, 7)) board[2][7] = King.new(true, Vector2(2, 7)) board[3][6] = Queen.new(true, Vector2(3, 6)) board[4][7] = Pawn.new(true, Vector2(4, 7)) board[5][7] = Knight.new(true, Vector2(5, 7)) board[6][7] = Pawn.new(true, Vector2(6, 7)) board[7][7] = Rook.new(true, Vector2(7, 7)) board[0][4] = Rook.new(false, Vector2(0, 4)) board[1][5] = Bishop.new(false, Vector2(1, 5)) board[3][2] = Queen.new(false, Vector2(3, 2)) board[3][5] = Pawn.new(false, Vector2(3, 5)) board[4][4] = Pawn.new(false, Vector2(4, 4)) board[3][4] = Pawn.new(false, Vector2(3, 4)) board[4][3] = Pawn.new(false, Vector2(4, 3)) board[7][5] = Rook.new(false, Vector2(7, 5)) #potential 'holes', not quite working yet #board[3][3] = '/' draw_board(X,Y) #set up color for opponent's shader, currently set to black opponent_shader.shader = preload("res://assets/shaders/color.gdshader").duplicate() opponent_shader.set_shader_parameter('r', 0.2) opponent_shader.set_shader_parameter('g', 0.2) opponent_shader.set_shader_parameter('b', 0.2) #set up color for selected piece's shader, currently set to red selected_shader.shader = preload("res://assets/shaders/color.gdshader").duplicate() selected_shader.set_shader_parameter('r', 1.0) selected_shader.set_shader_parameter('g', 0.5) selected_shader.set_shader_parameter('b', 0.5) draw_pieces() # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(delta: float) -> void: #dynamically translates loaction of board and pieces to remain in corrent location tile_container.position = translate() shadow_container.position = translate() piece_container.position = translate() #attempt at linear interpolation for smooth movement of pieces# #lerp for smooth movement of pieces if moving: #if piece is not at target position, linearly interpolate to target if selected_piece.position != selected_piece_position: #if within threshold, snap to position to avoid floating point error if selected_piece.position.distance_to(target_position) < 0.1: selected_piece.position = selected_piece_position unselect_piece() moving = false # Snap to exact position else: selected_piece.position = selected_piece.position.lerp(selected_piece_position, 10 * delta) else: moving = false unselect_piece() func draw_pieces(): #iterate through all tiles, if tile has a piece, #add corresponding piece with correct sprite to piece_container for x in range(X): for y in range(Y): if !is_empty(x, y): var piece_scene if board[x][y] is Pawn: piece_scene = preload("res://scenes/Pawn.tscn") elif board[x][y] is Rook: piece_scene = preload("res://scenes/Rook.tscn") elif board[x][y] is Bishop: piece_scene = preload("res://scenes/Bishop.tscn") elif board[x][y] is Queen: piece_scene = preload("res://scenes/Queen.tscn") elif board[x][y] is King: piece_scene = preload("res://scenes/King.tscn") elif board[x][y] is Knight: piece_scene = preload("res://scenes/Knight.tscn") if piece_scene: var piece_instance = piece_scene.instantiate() piece_instance.position = Vector2((x * tile_size) + tile_size/2, (y * tile_size) + tile_size/2) piece_instance.z_index = 3 # Scale the sprite by 25% for all pieces except the king if board[x][y] is King: piece_instance.scale = Vector2(2.0, 2.0) # Scale the king by 50% else: piece_instance.scale = Vector2(1.25, 1.25) # Scale other pieces by 25% # If piece is opponent, add a black shader to piece if board[x][y].is_white == false: var sprite = piece_instance.get_node("Sprite2D") as Sprite2D if sprite: sprite.material = opponent_shader piece_container.add_child(piece_instance) #translates 'home' coordinates ((0,0)) to screen location #used to center board, shadow and pieces at 2/3 of x axis and 1/2 of y axis func translate() -> Vector2: var screen_size = get_viewport().get_visible_rect().size var board_x_offset = X * tile_size / 2 var board_y_offset = Y * tile_size / 2 #places top left corner at 2/3 of x axis and 1/2 of y axis, then translates to center return Vector2(screen_size.x * 2 / 3 - board_x_offset, screen_size.y / 2 - board_y_offset) #color rect version func draw_board(X, Y): for x in range(X): for y in range(Y): var tile = ColorRect.new() if (x + y)%2 == 0: tile.color = light else: tile.color = dark tile.size = Vector2(tile_size, tile_size) tile.position = Vector2(x * tile_size, y * tile_size) tile_container.add_child(tile) var screen_size = get_viewport().get_visible_rect().size var board_x_offset = X * tile_size / 2 var board_y_offset = Y * tile_size / 2 var shadow_shader = ShaderMaterial.new() shadow_shader.shader = preload("res://assets/shaders/shadow.gdshader") var shadow = ColorRect.new() shadow.size = Vector2(tile_size, tile_size) shadow.position = Vector2(x * tile_size + 15, y * tile_size + 15) shadow.material = shadow_shader shadow_container.add_child(shadow) #textured rect version func draw_board_texture_rect(X, Y): dark_tile_shader.shader = preload("res://assets/shaders/color.gdshader").duplicate() dark_tile_shader.set_shader_parameter('r', 0.1) dark_tile_shader.set_shader_parameter('g', 0.25) dark_tile_shader.set_shader_parameter('b', 0.6) light_tile_shader.shader = preload("res://assets/shaders/color.gdshader").duplicate() light_tile_shader.set_shader_parameter('r', 0.85) light_tile_shader.set_shader_parameter('g', 0.85) light_tile_shader.set_shader_parameter('b', 0.85) for x in range(X): for y in range(Y): if board[x][y] == '/': pass else: var tile = TextureRect.new() tile.texture = preload("res://assets/sprites/tile.png") if (x + y)%2 == 0: tile.material = light_tile_shader else: tile.material = dark_tile_shader tile.size = Vector2(tile_size, tile_size) tile.position = Vector2(x * tile_size, y * tile_size) tile_container.add_child(tile) var screen_size = get_viewport().get_visible_rect().size var board_x_offset = X * tile_size / 2 var board_y_offset = Y * tile_size / 2 var shadow_shader = ShaderMaterial.new() shadow_shader.shader = preload("res://assets/shaders/shadow.gdshader") var shadow = ColorRect.new() shadow.size = Vector2(tile_size, tile_size) shadow.position = Vector2(x * tile_size + 15, y * tile_size + 15) shadow.material = shadow_shader shadow_container.add_child(shadow) #detects if mouse events occur within the board func is_on_board(position : Vector2) -> bool: if position.x < (X * tile_size) && position.x > 0: if position.y < (Y * tile_size) && position.y > 0: return true return false func _input(event): if event is InputEventMouseButton: #handles only mouse clicks if !moving: if event.pressed: #translate the mouse click position to 'home' coordinates (start at 0,0) var translated_postition = event.position - translate() #if the click is within the board, proceed if is_on_board(translated_postition): #translates mouse position to array index. #example: (66.5, 99.9) = (1, 1) , (120.0, 12.0) = (2, 0) var mouse_position = Vector2(int(translated_postition.x / tile_size), int(translated_postition.y / tile_size)) var x = mouse_position.x var y = mouse_position.y #if mouse click is a left click, proceed if event.button_index == MOUSE_BUTTON_LEFT: #if the tile is a player's piece, select or deselect it if !is_empty(x,y) && !(is_opponent(x,y)): #if there is a currently selected piece that is on the selected tile, deselect that piece if selected_piece != null: if board[x][y] == selected_piece_value: unselect_piece() else: select_piece(x,y) #otherwise, select the piece else: select_piece(x,y) #if the tile is empty or an opponents piece, move the selected piece to that tile else: move_selected_piece(x,y) func is_opponent(x,y): if board[x][y] == null: return false else: # If the value of board[x][y] is an opponent piece if selected_piece != null: return board[x][y].is_white != selected_piece_value.is_white else: return !board[x][y].is_white func is_empty(x, y): return board[x][y] == null #test funciton to add pieces to board #func add_pawn(x, y): #var piece = TextureRect.new() #piece.position = Vector2(x*tile_size, y*tile_size) #piece.size = Vector2(64,64) #piece.z_index = 3 #piece.texture = preload("res://pawn.png") #piece_container.add_child(piece) func remove_piece(x,y): #iterate through the pieces in piece_container, if that piece is at location x,y, remove it for child in piece_container.get_children(): #translate x,y index values to screen coordinates if child.position == Vector2(x*tile_size + 32, y*tile_size + 32): explosion_effect.position = child.position + translate() explosion_effect.restart() piece_container.remove_child(child) child.queue_free() break func select_piece(x,y): unselect_piece() #iterate through the pieces in piece_container, if that piece is at location x,y, select it for child in piece_container.get_children(): if child.position == Vector2((x*tile_size)+32, (y*tile_size)+32): selected_piece_position = Vector2(x,y) selected_piece = child selected_piece_value = board[x][y] #add a red coloured shader to the selected piece child.material = selected_shader #highlight the valid moves the selected piece can make highlight_tiles() func unselect_piece(): if selected_piece != null: selected_piece.material = null selected_piece = null selected_piece_value = null remove_highlight() func highlight_tiles(): # Clear the previously selected valid moves remove_highlight() valid_moves = selected_piece_value.get_valid_moves(board, selected_piece_position) for child in tile_container.get_children(): if child.position / tile_size in valid_moves: child.modulate = Color(1, 0.5, 0.5) else: child.modulate = Color(1, 1, 1) func remove_highlight(): #remove all highlights from tiles in tile_container for child in tile_container.get_children(): child.modulate = Color(1,1,1) valid_moves = [] func move_selected_piece(x,y): #can only move a piece if selected_piece exists if selected_piece != null: #can only move to tiles in valid_moves if Vector2(x,y) in valid_moves: #if tile has opponent piece, remove piece if is_opponent(x,y): print("Capturing opponent piece") remove_piece(x,y) #clear the selected_piece's previous position board[selected_piece_position.x][selected_piece_position.y] = null #update selected_piece's screen coordinate position selected_piece_position = Vector2((x*tile_size)+ tile_size/2, (y*tile_size) + tile_size/2) board[x][y] = selected_piece_value target_position = selected_piece_position moving = true valid_moves = [] else: unselect_piece() remove_highlight()