ChessBuilder/addons/Chess/Scripts/Pawn.gd

396 lines
14 KiB
GDScript

@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
if original_movement_string == "mfWcfF":
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 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"]
# Filter directions based on modifiers - NEW LOGIC HERE
if direction_modifiers:
var filtered_directions = []
for dir in base_directions:
# For each direction, check if it CONTAINS any of the modifiers
# (doesn't need to match all characters)
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])