@tool extends Sprite2D class_name Pawn @export var Current_X_Position = 0 @export var Current_Y_Position = 0 # Item_Color = 0; White # Item_Color = 1; Black @export var Item_Color = 0 @export var Points = 1 var game: ChessGame = null var duration_label: Label var Temp_Color = 0 var Double_Start = true var En_Passant = false const BASE_SIZE = Vector2(40, 40) const CARD_TINT_COLOR = Color(0.3, 0.8, 0.3, 0.5) # Green tint for card effects const EFFECT_TINT_COLOR = Color(0.8, 0.2, 0.2, 0.5) var id: String = Utils.generate_guid() var original_movement_string: String = "mfWcfF" var current_movement_string: String = original_movement_string const DIRECTIONS = { "b": [0, 1], # backward "f": [0, -1], # forward "l": [-1, 0], # right "r": [1, 0], # left "bl": [-1, 1], # forward-right "br": [1, 1], # forward-left "fl": [-1, -1], # backward-right "fr": [1, -1] # backward-left } func _ready(): modulate = Color.WHITE if Item_Color == 0 else Color.BLACK func _init(chess: ChessGame) -> void: game = chess self.texture = load("res://addons/Chess/Textures/WPawn.svg") var background_style = StyleBoxFlat.new() background_style.bg_color = Color.WHITE background_style.corner_radius_top_left = 2 background_style.corner_radius_top_right = 2 background_style.corner_radius_bottom_left = 2 background_style.corner_radius_bottom_right = 2 background_style.content_margin_left = 2 background_style.content_margin_right = 2 background_style.content_margin_top = 1 background_style.content_margin_bottom = 1 duration_label = Label.new() duration_label.add_theme_stylebox_override("normal", background_style) duration_label.add_theme_color_override("font_color", Color.BLACK) duration_label.position = Vector2(-20, -20) # Position above the piece add_child(duration_label) duration_label.hide() func update_appearance() -> void: # print("update_appearance") var chess_game = game if !is_instance_valid(get_tree()) || !chess_game: return if !chess_game.deckManager: return var deck_manager = chess_game.deckManager var has_card = deck_manager.attached_cards.has(get_instance_id()) var has_effect = deck_manager.attached_effects.has(get_instance_id()) var base_color = Color.WHITE if Item_Color == 0 else Color.BLACK if has_effect and deck_manager.attached_effects[get_instance_id()].size() > 0: modulate = base_color * ( EFFECT_TINT_COLOR) if has_card: # Apply tint while keeping the piece color modulate = base_color * CARD_TINT_COLOR # Update duration display var card = deck_manager.attached_cards[get_instance_id()] if is_instance_valid(duration_label): duration_label.text = str(card.remaining_turns) duration_label.show() else: # Reset to normal color modulate = Color.WHITE if Item_Color == 0 else Color.BLACK if is_instance_valid(duration_label): duration_label.hide() func on_card_effect_changed() -> void: update_appearance() # Movement interface method that all pieces will implement func getValidMoves(board_flow, current_location: String) -> Dictionary: var moves = { "regular_moves": [], "special_moves": [] } var loc = current_location.split("-") var x = int(loc[0]) var y = int(loc[1]) # Invert directions if black var direction_multiplier = 1 if Item_Color == 1 else -1 # Parse the Betza notation var atoms = parseBetzaNotation(current_movement_string) # Process each atom for atom in atoms: var can_move = "m" in atom.modifiers || (!("m" in atom.modifiers) && !("c" in atom.modifiers)) var can_capture = "c" in atom.modifiers || (!("m" in atom.modifiers) && !("c" in atom.modifiers)) # Get directions for this atom var directions = getDirectionsForAtom(atom) # Process each direction for dir in directions: var dir_vector = DIRECTIONS[dir].duplicate() # Adjust direction based on color if Item_Color == 1: # If black if "f" in dir: dir_vector[1] *= -1 if "b" in dir: dir_vector[1] *= -1 if "l" in dir: dir_vector[0] *= -1 if "r" in dir: dir_vector[0] *= -1 # Handle different atom types match atom.type: "W": # Wazir (orthogonal step) processStep(board_flow, moves, x, y, dir_vector, can_move, can_capture) "F": # Ferz (diagonal step) processStep(board_flow, moves, x, y, dir_vector, can_move, can_capture) "R": # Rook (orthogonal slider) processSlider(board_flow, moves, x, y, dir_vector, can_move, can_capture, atom.range) "B": # Bishop (diagonal slider) processSlider(board_flow, moves, x, y, dir_vector, can_move, can_capture, atom.range) "N": # Knight processKnightMove(board_flow, moves, x, y, dir_vector, can_move, can_capture) # Add special pawn moves if this is a pawn and no movement effects currently applied if self.name == "Pawn" and self.original_movement_string == self.current_movement_string: addSpecialPawnMoves(board_flow, moves, x, y) return moves # Helper method for all pieces func is_valid_cell(board_flow, location: String) -> bool: var node = board_flow.get_node_or_null(location) return node != null # Helper for checking if cell is empty or contains enemy func can_move_to_cell(board_flow, location: String, is_capture: bool = false) -> bool: var container = board_flow.get_node(location) as PieceContainer var game = board_flow.get_parent() as ChessGame var tile = game.tileManager.get_tile(location) if tile && !tile.passable: return false if is_capture: var piece = container.get_piece() return piece != null && piece.Item_Color != self.Item_Color return !container.has_piece() func set_current_movement_string(mvmnt: String): current_movement_string = mvmnt func reset_current_movement_string(): original_movement_string = current_movement_string func get_current_movement_string() -> String: return current_movement_string func get_original_movement_string() -> String: return original_movement_string func animate_movement(target_position: Vector2, duration: float = 0.5) -> void: # print("--------------STARTING ANIM--------------", position, " ", target_position) z_index = 1 var tween = create_tween() # Make sure the tween is configured properly tween.set_trans(Tween.TRANS_LINEAR) # or TRANS_CUBIC for smoother movement tween.set_ease(Tween.EASE_IN_OUT) var start_pos = position tween.tween_property(self, "global_position", target_position, duration) #.from(start_pos) # Wait for animation to complete await tween.finished # print("--------------FINISHED ANIM--------------") func animate_capture(duration: float = 0.5) -> void: z_index = 1 # Ensure piece is visible above others during animation var tween = create_tween() tween.set_trans(Tween.TRANS_LINEAR) tween.set_ease(Tween.EASE_IN_OUT) # First turn red tween.tween_property(self, "modulate", Color.RED, duration/2) # Then shrink to nothing tween.tween_property(self, "scale", Vector2.ZERO, duration/2) # Finally remove the piece tween.tween_callback(queue_free) await tween.finished # ========================BETZA NOTATION================ # Parse the Betza notation string into a list of atoms func parseBetzaNotation(notation: String) -> Array: var atoms = [] var i = 0 while i < notation.length(): var atom = { "type": "", "modifiers": [], "range": -1 # -1 unlimited } # Collect modifiers before the atom type while i < notation.length() && ["m", "c"].has(notation[i]): atom.modifiers.append(notation[i]) i += 1 # direction modifiers var directions = "" while i < notation.length() && ["f", "b", "l", "r"].has(notation[i]): directions += notation[i] i += 1 if directions: atom.modifiers.append(directions) # atom type if i < notation.length(): atom.type = notation[i] i += 1 # range specification var range_str = "" while i < notation.length() && notation[i].is_valid_int(): range_str += notation[i] i += 1 if range_str: atom.range = int(range_str) atoms.append(atom) return atoms # Directions for an atom based on its modifiers func getDirectionsForAtom(atom) -> Array: var base_directions = [] var direction_modifiers = [] # Extract direction modifiers for modifier in atom.modifiers: if modifier.length() > 1 || ["f", "b", "l", "r"].has(modifier): for char in modifier: if ["f", "b", "l", "r"].has(char): direction_modifiers.append(char) # Set base directions based on atom type match atom.type: "W": # Wazir base_directions = ["f", "b", "l", "r"] "F": # Ferz base_directions = ["fl", "fr", "bl", "br"] "R": # Rook base_directions = ["f", "b", "l", "r"] "B": # Bishop base_directions = ["fl", "fr", "bl", "br"] "Q": # Queen base_directions = ["f", "b", "l", "r", "fl", "fr", "bl", "br"] "K": # King base_directions = ["f", "b", "l", "r", "fl", "fr", "bl", "br"] "N": # Knight - directions handled specially base_directions = ["f", "b", "l", "r"] if direction_modifiers: var filtered_directions = [] for dir in base_directions: # For each direction, check if it has any of the modifiers var contains_modifier = false for mod in direction_modifiers: if dir.contains(mod): contains_modifier = true break if contains_modifier: filtered_directions.append(dir) return filtered_directions return base_directions # Process a single step move func processStep(board_flow, moves, x, y, dir_vector, can_move, can_capture): var new_x = x + dir_vector[0] var new_y = y + dir_vector[1] var target = str(new_x) + "-" + str(new_y) print("processStep " + target ) if is_valid_cell(board_flow, target): if can_move && can_move_to_cell(board_flow, target, false): if !moves.regular_moves.has(target): moves.regular_moves.append(target) elif can_capture && can_move_to_cell(board_flow, target, true): if !moves.regular_moves.has(target): moves.regular_moves.append(target) # Process a slider move (rook, bishop, queen) func processSlider(board_flow, moves, x, y, dir_vector, can_move, can_capture, max_range): var step = 1 var blocked = false while !blocked && (max_range == -1 || step <= max_range): var new_x = x + (dir_vector[0] * step) var new_y = y + (dir_vector[1] * step) var target = str(new_x) + "-" + str(new_y) if !is_valid_cell(board_flow, target): blocked = true continue var target_cell = board_flow.get_node(target) as PieceContainer if target_cell.get_piece() == null: if can_move: if !moves.regular_moves.has(target): moves.regular_moves.append(target) else: if can_capture && target_cell.get_piece().Item_Color != self.Item_Color: if !moves.regular_moves.has(target): moves.regular_moves.append(target) blocked = true step += 1 # Process knight move func processKnightMove(board_flow, moves, x, y, dir_vector, can_move, can_capture): # Knight directions regardless of the dir_vector var knight_offsets = [ [1, 2], [2, 1], [2, -1], [1, -2], [-1, -2], [-2, -1], [-2, 1], [-1, 2] ] for offset in knight_offsets: var new_x = x + offset[0] var new_y = y + offset[1] var target = str(new_x) + "-" + str(new_y) if is_valid_cell(board_flow, target): var has_piece = board_flow.get_node(target).get_piece() != null var enemy_piece = has_piece && board_flow.get_node(target).get_piece().Item_Color != self.Item_Color if ((can_move && !has_piece) || (can_capture && enemy_piece)): if !moves.regular_moves.has(target): moves.regular_moves.append(target) # Add special pawn moves (double move, en passant) func addSpecialPawnMoves(board_flow, moves, x, y): var direction = 1 if Item_Color == 1 else -1 # Double move on first turn if Double_Start: var single_forward = str(x) + "-" + str(y + direction) var double_forward = str(x) + "-" + str(y + (direction * 2)) if is_valid_cell(board_flow, single_forward) and is_valid_cell(board_flow, double_forward) and can_move_to_cell(board_flow, single_forward) and can_move_to_cell(board_flow, double_forward): if !moves.regular_moves.has(double_forward): moves.regular_moves.append(double_forward) # En Passant for dx in [-1, 1]: var adjacent = str(x + dx) + "-" + str(y) var capture = str(x + dx) + "-" + str(y + direction) if is_valid_cell(board_flow, adjacent) and is_valid_cell(board_flow, capture): var adjacent_cell = board_flow.get_node(adjacent) as PieceContainer if adjacent_cell.get_piece() != null: var adjacent_piece = adjacent_cell.get_piece() if adjacent_piece.name == "Pawn" and adjacent_piece.En_Passant and adjacent_piece.Item_Color != self.Item_Color: moves.special_moves.append([adjacent, capture])