extends Node3D # This script is attached to a Node3D # Export variables for inspector configuration @export var enhanced_gridmap_path: NodePath = "/root/Main/EnhancedGridMap" # Path to the EnhancedGridMap node var enhanced_gridmap: EnhancedGridMap # References to the EnhancedGridMap node @export var current_position: Vector2i # Current grid position of the player var is_player_moving: bool = false # Flag to prevent movement while already moving # Customizable cell size and offset @export var cell_size: Vector3 = Vector3(2, 2, 2) # Size of each grid cell @export var cell_offset: Vector3 = Vector3(0, 0, 0) # Offset for the grid # Center offset flags @export var center_x: bool = false # Center the player on X axis @export var center_y: bool = false # Center the player on Y axis @export var center_z: bool = false # Center the player on Z axis # Diagonal movement flag @export var use_diagonal_movement: bool = false: # Allow diagonal movement set(value): use_diagonal_movement = value if enhanced_gridmap: enhanced_gridmap.set_diagonal_movement(value) # Turn management variables @export var is_my_turn: bool = false: # Flag to indicate if it's this player's turn set(value): is_my_turn = value if is_my_turn and is_multiplayer_authority(): rpc("display_message", "It's your turn!") # RPC call to display turn message @export var has_moved_this_turn = false # Flag to track if player has moved this turn @onready var main_scene = get_tree().current_scene # Reference to the main scene func _ready(): # Called when the node enters the scene tree name = str(get_multiplayer_authority()) # Set the node name to the multiplayer authority ID $Name.text = str(name) # Set the displayed name enhanced_gridmap = get_node(enhanced_gridmap_path) # Get the EnhancedGridMap node if main_scene: enhanced_gridmap = main_scene.get_node("EnhancedGridMap") # Get EnhancedGridMap from main scene else: push_error("Main scene not found") # Error if main scene not found if not enhanced_gridmap: push_error("EnhancedGridMap node not found. Please set the correct path in the inspector.") return enhanced_gridmap.initialize_astar() # Initialize A* pathfinding enhanced_gridmap.set_diagonal_movement(use_diagonal_movement) # Set diagonal movement option current_position = find_valid_starting_position() # Find a valid starting position update_player_position(current_position) # Update player's position set_process_unhandled_input(is_multiplayer_authority()) # Only process input for the authority func find_valid_starting_position() -> Vector2i: # Find a valid starting position var rng = RandomNumberGenerator.new() rng.randomize() var max_attempts = 100 var attempts = 0 while attempts < max_attempts: current_position = Vector2i(0, rng.randi_range(0, 9)) # Generate random position var cell_item = enhanced_gridmap.get_cell_item(Vector3i(current_position.x, 0, current_position.y)) if cell_item not in enhanced_gridmap.non_walkable_items: return current_position # Return valid position attempts += 1 return Vector2i(0, 0) # Default position if no valid position found func _physics_process(_delta): # Called every physics frame if is_multiplayer_authority(): rpc("remote_set_position", global_position) # RPC call to sync position func _unhandled_input(event): # Handle input events if not is_multiplayer_authority() or not is_my_turn or is_player_moving: return if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: var camera = get_viewport().get_camera_3d() var from = camera.project_ray_origin(event.position) var to = from + camera.project_ray_normal(event.position) * 1000 var click_position = raycast_to_grid(from, to) if click_position != Vector2i(-1, -1): move_player_to_clicked_position(click_position) func raycast_to_grid(from: Vector3, to: Vector3) -> Vector2i: # Convert 3D raycast to grid position var plane = Plane(Vector3.UP, cell_offset.y) var intersection = plane.intersects_ray(from, to - from) if intersection: var adjusted_intersection = intersection - cell_offset var grid_position = Vector2i( floor(adjusted_intersection.x / cell_size.x), floor(adjusted_intersection.z / cell_size.z) ) if grid_position.x >= 0 and grid_position.x < enhanced_gridmap.columns and \ grid_position.y >= 0 and grid_position.y < enhanced_gridmap.rows: return grid_position return Vector2i(-1, -1) # Invalid position func move_player_to_clicked_position(grid_position: Vector2i): # Move player to clicked position var cell_item = enhanced_gridmap.get_cell_item(Vector3i(grid_position.x, 0, grid_position.y)) if cell_item in enhanced_gridmap.non_walkable_items: print("Cannot move to non-walkable cell") return var path = enhanced_gridmap.find_path(Vector2(current_position), Vector2(grid_position)) if path.size() > 1: path.pop_front() move_player_along_path(path) else: print("No valid path found") func move_player_along_path(path: Array): # Move player along calculated path is_player_moving = true var tween = create_tween() tween.set_trans(Tween.TRANS_CUBIC) tween.set_ease(Tween.EASE_IN_OUT) for point in path: var target_position = grid_to_world(Vector2i(point.x, point.y)) tween.tween_property(self, "position", target_position, 0.5) tween.tween_callback(func(): current_position = Vector2i(path[-1].x, path[-1].y) is_player_moving = false enhanced_gridmap.clear_path_visualization() has_moved_this_turn = true end_turn() ) func update_player_position(grid_position: Vector2i): # Update player's position position = grid_to_world(grid_position) func grid_to_world(grid_position: Vector2i) -> Vector3: # Convert grid position to world position var world_position = Vector3( grid_position.x * cell_size.x, cell_size.y, grid_position.y * cell_size.z ) world_position.x += cell_size.x * 0.5 world_position.z += cell_size.z * 0.5 if center_x: world_position.x += cell_size.x * 0.5 if center_y: world_position.y += cell_size.y * 0.5 if center_z: world_position.z += cell_size.z * 0.5 return world_position + cell_offset func start_turn(): # Start player's turn has_moved_this_turn = false is_my_turn = true if is_multiplayer_authority(): rpc("display_message", "It's your turn!") # RPC call to display turn message func end_turn(): # End player's turn is_my_turn = false has_moved_this_turn = false if is_multiplayer_authority(): get_node("/root/Main").request_next_turn() # Request next turn from main scene @rpc("any_peer", "call_local", "unreliable") func remote_set_position(authority_position): # RPC function to sync position global_position = authority_position @rpc("any_peer", "call_local") func display_message(message): # RPC function to display messages $Bubble.show() $Bubble/Message.show() $Bubble/Message.text = str(message) await get_tree().create_timer(3).timeout $Bubble.hide() $Bubble/Message.hide()