extends Node3D @export var enhanced_gridmap_path: NodePath = "/root/Main/EnhancedGridMap" var enhanced_gridmap: EnhancedGridMap @export var current_position: Vector2i var is_player_moving: bool = false @export var cell_size: Vector3 = Vector3(2, 2, 2) @export var cell_offset: Vector3 = Vector3(0, 0, 0) @export var center_x: bool = false @export var center_y: bool = false @export var center_z: bool = false @export var movement_range: int = 1 # How many blocks can be moved at once @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) @export var is_my_turn: bool = false: set(value): is_my_turn = value if is_my_turn and is_multiplayer_authority(): rpc("display_message", "It's your turn!") @export var has_moved_this_turn = false @onready var main_scene = get_tree().current_scene func _ready(): name = str(get_multiplayer_authority()) $Name.text = str(name) enhanced_gridmap = get_node(enhanced_gridmap_path) if main_scene: enhanced_gridmap = main_scene.get_node("EnhancedGridMap") else: push_error("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() enhanced_gridmap.set_diagonal_movement(use_diagonal_movement) current_position = find_valid_starting_position() update_player_position(current_position) set_process_unhandled_input(is_multiplayer_authority()) func find_valid_starting_position() -> Vector2i: 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)) 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 attempts += 1 return Vector2i(0, 0) func find_random_valid_position_in_range() -> Vector2i: var rng = RandomNumberGenerator.new() rng.randomize() var max_attempts = 100 var attempts = 0 while attempts < max_attempts: var range_x = min(enhanced_gridmap.columns - 1, movement_range) var range_y = min(enhanced_gridmap.rows - 1, movement_range) # Generate position within movement range of current position var offset_x = rng.randi_range(-range_x, range_x) var offset_y = rng.randi_range(-range_y, range_y) var random_position = Vector2i( clamp(current_position.x + offset_x, 0, enhanced_gridmap.columns - 1), clamp(current_position.y + offset_y, 0, enhanced_gridmap.rows - 1) ) # Check if the position is within movement range if not is_within_movement_range(random_position): attempts += 1 continue var cell_item = enhanced_gridmap.get_cell_item(Vector3i(random_position.x, 0, random_position.y)) if cell_item not in enhanced_gridmap.non_walkable_items and random_position != current_position: return random_position attempts += 1 return current_position func _physics_process(_delta): if is_multiplayer_authority(): rpc("remote_set_position", global_position) func _unhandled_input(event): var main = get_node("/root/Main") if not is_multiplayer_authority() or (main.turn_based_mode and (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: 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) func is_within_movement_range(target_position: Vector2i) -> bool: var distance = 0 if use_diagonal_movement: # For diagonal movement, use max of x and y differences distance = max(abs(target_position.x - current_position.x), abs(target_position.y - current_position.y)) else: # For orthogonal movement, use Manhattan distance distance = abs(target_position.x - current_position.x) + \ abs(target_position.y - current_position.y) return distance <= movement_range func move_player_to_clicked_position(grid_position: Vector2i): if not is_multiplayer_authority(): return if is_player_moving: return # Check if the movement is within range if not is_within_movement_range(grid_position): print("Movement out of range") return 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() rpc("start_movement_along_path", path) else: print("No valid path found") @rpc("any_peer", "call_local") func start_movement_along_path(path: Array): 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 var main = get_node("/root/Main") if main.turn_based_mode: end_turn() ) func move_bot_along_path(path: Array, bot_id: int): if not is_multiplayer_authority(): return rpc("start_bot_movement_along_path", path, bot_id) @rpc("any_peer", "call_local") func start_bot_movement_along_path(path: Array, bot_id: int): 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 var main = get_node("/root/Main") if main.turn_based_mode: end_turn() # Notify main that bot movement is complete if multiplayer.is_server(): main.bot_movement_completed(bot_id) ) func update_player_position(grid_position: Vector2i): position = grid_to_world(grid_position) func grid_to_world(grid_position: Vector2i) -> Vector3: 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(): has_moved_this_turn = false is_my_turn = true if is_multiplayer_authority(): rpc("display_message", "It's your turn!") func end_turn(): is_my_turn = false has_moved_this_turn = false if is_multiplayer_authority(): get_node("/root/Main").request_next_turn() @rpc("any_peer", "call_local", "unreliable") func remote_set_position(authority_position): global_position = authority_position @rpc("any_peer", "call_local") func display_message(message): $Bubble.show() $Bubble/Message.show() $Bubble/Message.text = str(message) await get_tree().create_timer(3).timeout $Bubble.hide() $Bubble/Message.hide()