From 09df1be65489713246a8a7760046ed9729230d71 Mon Sep 17 00:00:00 2001 From: adtpdn Date: Tue, 22 Oct 2024 17:31:50 +0800 Subject: [PATCH] update with bot system, but bot visually hosted on host side, doesn't visually appear on client side --- scenes/main.gd | 163 +++++++++++++++++++++++++++++++---------------- scenes/player.gd | 37 +++++++++++ 2 files changed, 145 insertions(+), 55 deletions(-) diff --git a/scenes/main.gd b/scenes/main.gd index bb2c4f7..6eb2f9d 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -1,133 +1,186 @@ -extends Node3D # This script is attached to a Node3D (main scene) +extends Node3D -var multiplayer_peer = ENetMultiplayerPeer.new() # Create a new ENet multiplayer peer +var multiplayer_peer = ENetMultiplayerPeer.new() +const PORT = 9999 +const ADDRESS = "127.0.0.1" -const PORT = 9999 # Define the port for multiplayer -const ADDRESS = "127.0.0.1" # Define the IP address for multiplayer +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 = [] -var connected_peer_ids = [] # List of connected peer IDs -var local_player_character : CharacterBody3D # Reference to the local player character -var player_scene = preload("res://scenes/player.tscn") # Preload the player scene -var current_turn_index = 0 # Index of the current turn -@export var players = [] # List of player IDs -var game_started = false # Flag to track if the game has started +func _ready(): + multiplayer_peer.peer_connected.connect(_on_peer_connected) + multiplayer_peer.peer_disconnected.connect(_on_peer_disconnected) -var max_message_input_char = 51 # Maximum characters allowed in a message - -func _on_host_pressed(): # Called when the host button is pressed +func _on_host_pressed(): $NetworkInfo/NetworkSideDisplay.text = "Server" $Menu.visible = false - multiplayer_peer.create_server(PORT) # Create a multiplayer server + multiplayer_peer.create_server(PORT) multiplayer.multiplayer_peer = multiplayer_peer $NetworkInfo/UniquePeerID.text = str(multiplayer.get_unique_id()) - add_player_character(1) # Add host player character + add_player_character(1) + players.append(1) - players.append(1) # Add host to players list - if players.size() == 2: - start_game() # Start game if two players are connected + # Add bots to fill remaining slots + for i in range(2, max_players + 1): + add_bot(i) - multiplayer_peer.peer_connected.connect(_on_peer_connected) # Connect peer connected signal + start_game() -func _on_join_pressed(): # Called when the join button is pressed +func _on_join_pressed(): $NetworkInfo/NetworkSideDisplay.text = "Client" $Menu.visible = false - multiplayer_peer.create_client(ADDRESS, PORT) # Create a multiplayer client + 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): # Called when a new peer connects +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 call to add new player - rpc_id(new_peer_id, "add_previously_connected_player_characters", connected_peer_ids) # RPC call to sync existing players + rpc("add_newly_connected_player_character", new_peer_id) + rpc_id(new_peer_id, "add_previously_connected_player_characters", connected_peer_ids) add_player_character(new_peer_id) - players.append(new_peer_id) - if players.size() == 2 and not game_started: - start_game() # Start game if two players are connected + replace_bot_with_player(new_peer_id) -func _on_peer_disconnected(peer_id): # Called when a peer disconnects +func _on_peer_disconnected(peer_id): if multiplayer.is_server(): connected_peer_ids.erase(peer_id) players.erase(peer_id) - if players.size() < 2: - game_started = false + add_bot(get_next_available_bot_id()) -func add_player_character(peer_id): # Add a player character to the game +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) + player_character.add_to_group("Players", true) if peer_id == multiplayer.get_unique_id(): local_player_character = player_character -@rpc -func add_newly_connected_player_character(new_peer_id): # RPC function to add a new player character +func add_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) + bots.append(bot_id) + players.append(bot_id) + +func replace_bot_with_player(player_id): + if bots.size() > 0: + var bot_id = bots.pop_front() + players.erase(bot_id) + players.append(player_id) + var bot_node = get_node(str(bot_id)) + if bot_node: + bot_node.queue_free() + rpc("sync_players", players) + +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): # RPC function to add existing player characters +func add_previously_connected_player_characters(peer_ids): for peer_id in peer_ids: add_player_character(peer_id) -func _process(_delta): # Called every frame +func _process(_delta): if multiplayer.is_server() and game_started: - rpc("sync_turn_index", current_turn_index) # RPC call to sync turn index + rpc("sync_turn_index", current_turn_index) -func start_game(): # Start the game +func start_game(): if multiplayer.is_server(): game_started = true connected_peer_ids.sort() - rpc("sync_game_start", connected_peer_ids) # RPC call to sync game start + rpc("sync_game_start", connected_peer_ids) current_turn_index = -1 next_turn() @rpc -func sync_game_start(peer_ids): # RPC function to sync game start +func sync_game_start(peer_ids): connected_peer_ids = peer_ids game_started = true @rpc("reliable") -func sync_turn_index(index): # RPC function to sync turn index +func sync_turn_index(index): current_turn_index = index -func next_turn(): # Move to the next turn +@rpc("reliable") +func sync_players(new_players): + players = new_players + +func next_turn(): if multiplayer.is_server(): current_turn_index += 1 - if current_turn_index >= connected_peer_ids.size(): - current_turn_index = -1 - next_turn() - else: - rpc("set_current_turn", connected_peer_ids[current_turn_index]) # RPC call to set current turn + 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(): # Request to move to the next turn +func request_next_turn(): if multiplayer.is_server(): end_current_turn() else: - rpc_id(1, "server_end_current_turn") # RPC call to end turn on server + rpc_id(1, "server_end_current_turn") @rpc("any_peer") -func server_end_current_turn(): # RPC function to end current turn on server +func server_end_current_turn(): if multiplayer.is_server(): end_current_turn() @rpc("any_peer", "call_local") -func set_current_turn(player_id): # RPC function to set the current turn +func set_current_turn(player_id): 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(): # End the current turns +func end_current_turn(): if multiplayer.is_server(): next_turn() - rpc("sync_turn_index", current_turn_index) # RPC call to sync turn index + rpc("sync_turn_index", current_turn_index) -func _on_message_input_text_submitted(new_text): # Handle message input +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) # RPC call to display message + local_player_character.rpc("display_message", new_text) $MessageInput.text = "" $MessageInput.release_focus() + +func move_bot(bot_id): + if multiplayer.is_server(): + var bot = get_node(str(bot_id)) + if bot: + var target_position = bot.find_random_valid_position() + 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() # Remove the starting position + bot.move_bot_along_path(path) + else: + print("No valid path found for bot") + end_current_turn() + else: + print("No new valid position found for bot") + end_current_turn() + else: + print("Bot not found") + end_current_turn() diff --git a/scenes/player.gd b/scenes/player.gd index db81e65..2f90425 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -72,6 +72,25 @@ func find_valid_starting_position() -> Vector2i: # Find a valid starting positi return Vector2i(0, 0) # Default position if no valid position found +func find_random_valid_position() -> Vector2i: + var rng = RandomNumberGenerator.new() + rng.randomize() + + var max_attempts = 100 + var attempts = 0 + + while attempts < max_attempts: + var random_position = Vector2i(rng.randi_range(0, enhanced_gridmap.columns - 1), + rng.randi_range(0, enhanced_gridmap.rows - 1)) + 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 # Return valid position + + attempts += 1 + + return current_position # Return current position if no new 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 @@ -139,6 +158,24 @@ func move_player_along_path(path: Array): # Move player along calculated path end_turn() ) +func move_bot_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 + end_turn() + ) + func update_player_position(grid_position: Vector2i): # Update player's position position = grid_to_world(grid_position)