extends Node3D var multiplayer_peer = ENetMultiplayerPeer.new() const PORT = 9999 const ADDRESS = "127.0.0.1" var connected_peer_ids = [] var local_player_character : CharacterBody3D var player_scene = preload("res://scenes/player.tscn") var current_turn_index = 0 @export var players = [] var game_started = false var max_message_input_char = 51 var max_players = 4 var bots = [] @export var turn_based_mode: bool = true # Toggle between turn-based and realtime modes var bot_move_timer: float = 0.0 # Timer for bot movement in realtime mode const BOT_MOVE_INTERVAL: float = 2.0 # Time between bot movements in realtime mode # Bot movement state tracking var moving_bots = {} # Dictionary to track which bots are currently moving func _ready(): multiplayer_peer.peer_connected.connect(_on_peer_connected) multiplayer_peer.peer_disconnected.connect(_on_peer_disconnected) func _process(delta): if multiplayer.is_server(): if game_started: if turn_based_mode: rpc("sync_turn_index", current_turn_index) else: # Handle realtime bot movement bot_move_timer += delta if bot_move_timer >= BOT_MOVE_INTERVAL: bot_move_timer = 0.0 for bot_id in bots: if not moving_bots.get(bot_id, false): # Only move if bot isn't already moving move_bot(bot_id) func _on_host_pressed(): $NetworkInfo/NetworkSideDisplay.text = "Server" $Menu.visible = false multiplayer_peer.create_server(PORT) multiplayer.multiplayer_peer = multiplayer_peer $NetworkInfo/UniquePeerID.text = str(multiplayer.get_unique_id()) add_player_character(1) players.append(1) # Add bots to fill remaining slots for i in range(2, max_players + 1): add_bot(i) start_game() func _on_join_pressed(): $NetworkInfo/NetworkSideDisplay.text = "Client" $Menu.visible = false multiplayer_peer.create_client(ADDRESS, PORT) multiplayer.multiplayer_peer = multiplayer_peer $NetworkInfo/UniquePeerID.text = str(multiplayer.get_unique_id()) func _on_peer_connected(new_peer_id): if multiplayer.is_server(): await get_tree().create_timer(1).timeout rpc("add_newly_connected_player_character", new_peer_id) rpc_id(new_peer_id, "add_previously_connected_player_characters", connected_peer_ids) rpc_id(new_peer_id, "sync_game_state", players, bots, game_started, turn_based_mode) add_player_character(new_peer_id) replace_bot_with_player(new_peer_id) func _on_peer_disconnected(peer_id): if multiplayer.is_server(): connected_peer_ids.erase(peer_id) players.erase(peer_id) add_bot(get_next_available_bot_id()) func add_player_character(peer_id): connected_peer_ids.append(peer_id) var player_character = player_scene.instantiate() player_character.set_multiplayer_authority(peer_id) add_child(player_character) player_character.add_to_group("Players", true) if peer_id == multiplayer.get_unique_id(): local_player_character = player_character func add_bot(bot_id): if multiplayer.is_server(): rpc("create_bot", bot_id) moving_bots[bot_id] = false @rpc("call_local") func create_bot(bot_id): var bot_character = player_scene.instantiate() bot_character.set_multiplayer_authority(1) # Set server as authority for bots bot_character.name = str(bot_id) add_child(bot_character) bot_character.add_to_group("Players", true) bot_character.add_to_group("Bots", true) if multiplayer.is_server(): bots.append(bot_id) players.append(bot_id) moving_bots[bot_id] = false func replace_bot_with_player(player_id): if multiplayer.is_server() and bots.size() > 0: var bot_id = bots.pop_front() players.erase(bot_id) players.append(player_id) moving_bots.erase(bot_id) # Remove bot from moving tracking rpc("remove_bot", bot_id) rpc("sync_players", players) @rpc("call_local") func remove_bot(bot_id): var bot_node = get_node(str(bot_id)) if bot_node: bot_node.queue_free() func get_next_available_bot_id(): for i in range(2, max_players + 1): if not i in players: return i return -1 @rpc func add_newly_connected_player_character(new_peer_id): add_player_character(new_peer_id) @rpc func add_previously_connected_player_characters(peer_ids): for peer_id in peer_ids: add_player_character(peer_id) @rpc("call_local") func sync_game_state(current_players, current_bots, is_game_started, is_turn_based): players = current_players bots = current_bots game_started = is_game_started turn_based_mode = is_turn_based # Create bot characters for existing bots for bot_id in bots: if not has_node(str(bot_id)): create_bot(bot_id) func start_game(): if multiplayer.is_server(): game_started = true connected_peer_ids.sort() rpc("sync_game_start", connected_peer_ids, players, bots, turn_based_mode) if turn_based_mode: current_turn_index = -1 next_turn() @rpc("call_local") func sync_game_start(peer_ids, current_players, current_bots, is_turn_based): connected_peer_ids = peer_ids players = current_players bots = current_bots turn_based_mode = is_turn_based game_started = true @rpc("reliable") func sync_turn_index(index): current_turn_index = index @rpc("reliable") func sync_players(new_players): players = new_players func next_turn(): if multiplayer.is_server() and turn_based_mode: current_turn_index += 1 if current_turn_index >= players.size(): current_turn_index = 0 rpc("set_current_turn", players[current_turn_index]) if players[current_turn_index] in bots: move_bot(players[current_turn_index]) func request_next_turn(): if multiplayer.is_server(): end_current_turn() else: rpc_id(1, "server_end_current_turn") @rpc("any_peer") func server_end_current_turn(): if multiplayer.is_server(): end_current_turn() @rpc("any_peer", "call_local") func set_current_turn(player_id): if not turn_based_mode: return var players = get_tree().get_nodes_in_group("Players") for player in players: player.is_my_turn = (player.name == str(player_id)) if player.is_my_turn: player.start_turn() func end_current_turn(): if multiplayer.is_server(): next_turn() rpc("sync_turn_index", current_turn_index) func _on_message_input_text_submitted(new_text): if new_text.length() > max_message_input_char: new_text = new_text.substr(0, max_message_input_char) + " ... " local_player_character.rpc("display_message", new_text) $MessageInput.text = "" $MessageInput.release_focus() func move_bot(bot_id): if multiplayer.is_server(): if moving_bots.get(bot_id, false): return # Skip if bot is already moving var bot = get_node(str(bot_id)) if bot and (turn_based_mode or not bot.is_player_moving): moving_bots[bot_id] = true # Mark bot as moving var target_position = bot.find_random_valid_position_in_range() # Use the new function if target_position != bot.current_position: var path = bot.enhanced_gridmap.find_path(Vector2(bot.current_position), Vector2(target_position)) if path.size() > 1: path.pop_front() # Trim path to respect movement range var trimmed_path = path.slice(0, bot.movement_range) bot.move_bot_along_path(trimmed_path, bot_id) else: print("No valid path found for bot") moving_bots[bot_id] = false if turn_based_mode: end_current_turn() else: print("No new valid position found for bot") moving_bots[bot_id] = false if turn_based_mode: end_current_turn() else: print("Bot not found or is moving") if turn_based_mode: end_current_turn() # Called when a bot finishes moving func bot_movement_completed(bot_id): if multiplayer.is_server(): moving_bots[bot_id] = false