dce55c2fe6
- Refactor the Player.gd - Attempt to fix joining the available room, via Room ID
535 lines
17 KiB
GDScript
535 lines
17 KiB
GDScript
# -------------------------------------------------------------------------------------
|
|
# Tekton Dash - Multiplayer Board Game - 2024
|
|
# -------------------------------------------------------------------------------------
|
|
|
|
extends Node3D
|
|
|
|
# Manager references
|
|
var ui_manager
|
|
var obstacle_manager
|
|
|
|
# Minimal local state
|
|
var match_id_input: LineEdit
|
|
var _connection_check_timer: float = 0.0
|
|
|
|
func _ready():
|
|
# Initialize scene managers
|
|
_init_managers()
|
|
|
|
# Connect to multiplayer signals
|
|
multiplayer.peer_connected.connect(_on_peer_connected)
|
|
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
|
|
|
|
# Connect to Nakama signals
|
|
NakamaManager.match_joined.connect(_on_match_joined)
|
|
|
|
# Setup UI
|
|
ui_manager.setup_action_buttons(_set_action_state_callback)
|
|
ui_manager.setup_playerboard_ui()
|
|
_setup_obstacle_ui()
|
|
_setup_match_input()
|
|
|
|
func _init_managers():
|
|
# Create and attach scene managers
|
|
ui_manager = load("res://scripts/managers/ui_manager.gd").new()
|
|
ui_manager.name = "UIManager"
|
|
add_child(ui_manager)
|
|
ui_manager.initialize(self)
|
|
|
|
obstacle_manager = load("res://scripts/managers/obstacle_manager.gd").new()
|
|
obstacle_manager.name = "ObstacleManager"
|
|
add_child(obstacle_manager)
|
|
obstacle_manager.initialize($EnhancedGridMap)
|
|
|
|
func _setup_match_input():
|
|
match_id_input = LineEdit.new()
|
|
match_id_input.placeholder_text = "Enter Match ID to Join"
|
|
match_id_input.custom_minimum_size = Vector2(200, 30)
|
|
match_id_input.position = Vector2(10, 50)
|
|
$Menu.add_child(match_id_input)
|
|
|
|
func _setup_obstacle_ui():
|
|
var obstacle_button = Button.new()
|
|
obstacle_button.text = "Place Obstacle"
|
|
obstacle_button.pressed.connect(func(): _set_action_state(ui_manager.ActionState.PLACING_OBSTACLE))
|
|
$ActionMenu/ActionButtonContainer.add_child(obstacle_button)
|
|
|
|
var orientation_button = Button.new()
|
|
orientation_button.text = "Direction: North"
|
|
orientation_button.pressed.connect(func():
|
|
orientation_button.text = obstacle_manager.cycle_obstacle_orientation()
|
|
)
|
|
$ActionMenu/ActionButtonContainer.add_child(orientation_button)
|
|
|
|
var type_button = Button.new()
|
|
type_button.text = "Type: 1"
|
|
type_button.pressed.connect(func():
|
|
type_button.text = obstacle_manager.cycle_obstacle_type()
|
|
)
|
|
$ActionMenu/ActionButtonContainer.add_child(type_button)
|
|
|
|
func _process(delta):
|
|
if multiplayer.is_server() and GameStateManager.is_game_started():
|
|
if TurnManager.turn_based_mode:
|
|
rpc("sync_turn_index", TurnManager.current_turn_index)
|
|
update_all_players_goals()
|
|
|
|
_connection_check_timer += delta
|
|
if _connection_check_timer >= 5.0:
|
|
_connection_check_timer = 0.0
|
|
verify_all_connections()
|
|
|
|
# =============================================================================
|
|
# Network Button Handlers
|
|
# =============================================================================
|
|
|
|
func _on_host_pressed():
|
|
$NetworkPanel/NetworkInfo/NetworkSideDisplay.text = "Server (Creating Match...)"
|
|
$Menu.visible = false
|
|
var success = await NakamaManager.connect_to_nakama_async()
|
|
if not success:
|
|
$NetworkPanel/NetworkInfo/NetworkSideDisplay.text = "Connection Failed"
|
|
$Menu.visible = true
|
|
return
|
|
NakamaManager.host_game()
|
|
|
|
func _on_join_pressed():
|
|
var match_id = match_id_input.text.strip_edges()
|
|
if match_id.is_empty():
|
|
print("Please enter a Match ID")
|
|
return
|
|
|
|
$NetworkPanel/NetworkInfo/NetworkSideDisplay.text = "Client (Joining...)"
|
|
$Menu.visible = false
|
|
var success = await NakamaManager.connect_to_nakama_async()
|
|
if not success:
|
|
$NetworkPanel/NetworkInfo/NetworkSideDisplay.text = "Connection Failed"
|
|
$Menu.visible = true
|
|
return
|
|
NakamaManager.join_game(match_id)
|
|
|
|
func _on_match_joined(match_id: String):
|
|
$NetworkPanel/NetworkInfo/UniquePeerID.text = str(multiplayer.get_unique_id())
|
|
|
|
if multiplayer.is_server():
|
|
$NetworkPanel/NetworkInfo/NetworkSideDisplay.text = "Server (Match: %s)" % match_id
|
|
_setup_host_game()
|
|
else:
|
|
$NetworkPanel/NetworkInfo/NetworkSideDisplay.text = "Client"
|
|
_setup_client_game()
|
|
|
|
# =============================================================================
|
|
# Game Setup
|
|
# =============================================================================
|
|
|
|
func _setup_host_game():
|
|
# Generate goals
|
|
GoalManager.generate_preset_goals(GameStateManager.max_players)
|
|
|
|
# Add host player
|
|
var player_id = 1
|
|
var player_character = PlayerManager.add_player_character(player_id)
|
|
add_child(player_character)
|
|
player_character.add_to_group("Players", true)
|
|
GameStateManager.add_player(player_id)
|
|
GameStateManager.local_player_character = player_character
|
|
ui_manager.set_local_player(player_character)
|
|
|
|
# Set host goals
|
|
player_character.goals = GoalManager.get_goals_for_player(0)
|
|
rpc("sync_player_goals", player_id, player_character.goals)
|
|
|
|
rpc("sync_preset_goals", GoalManager.preset_goals)
|
|
|
|
# Add bots
|
|
if GameStateManager.enable_bots:
|
|
for i in range(2, GameStateManager.max_players + 1):
|
|
_add_bot(i)
|
|
|
|
_start_game()
|
|
|
|
func _setup_client_game():
|
|
await get_tree().create_timer(2.0).timeout
|
|
rpc_id(1, "request_full_player_sync", multiplayer.get_unique_id())
|
|
|
|
func _start_game():
|
|
if multiplayer.is_server():
|
|
GameStateManager.start_game()
|
|
rpc("sync_game_start", GameStateManager.players, TurnManager.turn_based_mode)
|
|
if TurnManager.turn_based_mode:
|
|
TurnManager.reset_turn()
|
|
var next_player = TurnManager.next_turn(GameStateManager.players)
|
|
rpc("set_current_turn", next_player)
|
|
|
|
# =============================================================================
|
|
# Player Management
|
|
# =============================================================================
|
|
|
|
func _add_bot(bot_id: int):
|
|
rpc("create_bot", bot_id)
|
|
|
|
@rpc("call_local")
|
|
func create_bot(bot_id: int):
|
|
if not GameStateManager.enable_bots:
|
|
return
|
|
|
|
if has_node(str(bot_id)):
|
|
return
|
|
|
|
var bot_character = PlayerManager.create_bot(bot_id)
|
|
call_deferred("add_child", bot_character)
|
|
bot_character.add_to_group("Players", true)
|
|
bot_character.add_to_group("Bots", true)
|
|
|
|
if multiplayer.is_server():
|
|
GameStateManager.add_bot(bot_id)
|
|
|
|
var goal_index = bot_id - 1
|
|
if goal_index < GoalManager.preset_goals.size():
|
|
bot_character.goals = GoalManager.preset_goals[goal_index].duplicate()
|
|
rpc("sync_player_goals", bot_id, bot_character.goals)
|
|
|
|
await get_tree().create_timer(0.1).timeout
|
|
bot_character.rpc("sync_bot_status", true)
|
|
|
|
@rpc("any_peer", "call_local")
|
|
func add_player_character(peer_id: int):
|
|
if has_node(str(peer_id)):
|
|
return
|
|
|
|
var player_character = PlayerManager.add_player_character(peer_id)
|
|
add_child(player_character)
|
|
player_character.add_to_group("Players", true)
|
|
|
|
GameStateManager.add_player(peer_id)
|
|
|
|
if peer_id == multiplayer.get_unique_id():
|
|
GameStateManager.local_player_character = player_character
|
|
ui_manager.set_local_player(player_character)
|
|
ui_manager.update_button_states()
|
|
ui_manager.update_playerboard_ui()
|
|
|
|
func _on_peer_connected(new_peer_id: int):
|
|
if multiplayer.is_server():
|
|
await get_tree().create_timer(1.5).timeout
|
|
add_player_character(new_peer_id)
|
|
rpc("add_newly_connected_player_character", new_peer_id)
|
|
|
|
@rpc
|
|
func add_newly_connected_player_character(new_peer_id: int):
|
|
add_player_character(new_peer_id)
|
|
|
|
func _on_peer_disconnected(peer_id: int):
|
|
if multiplayer.is_server():
|
|
GameStateManager.remove_player(peer_id)
|
|
if GameStateManager.enable_bots:
|
|
var next_id = PlayerManager.get_next_available_bot_id(GameStateManager.max_players, GameStateManager.players)
|
|
if next_id != -1:
|
|
_add_bot(next_id)
|
|
|
|
# =============================================================================
|
|
# Turn Management (RPC Handlers)
|
|
# =============================================================================
|
|
|
|
@rpc("reliable")
|
|
func sync_turn_index(index: int):
|
|
TurnManager.current_turn_index = index
|
|
|
|
@rpc("any_peer", "call_local")
|
|
func set_current_turn(player_id: int):
|
|
if not TurnManager.turn_based_mode:
|
|
return
|
|
|
|
for player in get_tree().get_nodes_in_group("Players"):
|
|
var is_current_turn = player.name == str(player_id)
|
|
player.is_my_turn = is_current_turn
|
|
|
|
if is_current_turn and not (player.is_bot or player.is_in_group("Bots")):
|
|
player.action_points = 2
|
|
player.has_moved_this_turn = false
|
|
player.has_performed_action = false
|
|
player.start_turn()
|
|
player.clear_highlights()
|
|
player.clear_playerboard_highlights()
|
|
else:
|
|
player.is_my_turn = false
|
|
|
|
@rpc("call_local")
|
|
func sync_game_start(player_list: Array, is_turn_based: bool):
|
|
GameStateManager.players = player_list
|
|
TurnManager.turn_based_mode = is_turn_based
|
|
GameStateManager.start_game()
|
|
|
|
# =============================================================================
|
|
# UI / Action State Management
|
|
# =============================================================================
|
|
|
|
func _set_action_state_callback(new_state):
|
|
_set_action_state(new_state)
|
|
|
|
func _set_action_state(new_state):
|
|
var local_player = GameStateManager.local_player_character
|
|
if not local_player or not local_player.is_multiplayer_authority():
|
|
return
|
|
|
|
if local_player.is_bot or local_player.is_in_group("Bots"):
|
|
ui_manager.current_action_state = new_state
|
|
return
|
|
|
|
if ui_manager.current_action_state == new_state or local_player.action_points <= 0:
|
|
return
|
|
|
|
ui_manager.current_action_state = new_state
|
|
local_player.clear_highlights()
|
|
local_player.clear_playerboard_highlights()
|
|
|
|
match new_state:
|
|
ui_manager.ActionState.MOVING:
|
|
local_player.highlight_movement_range()
|
|
ui_manager.ActionState.GRABBING:
|
|
local_player.highlight_adjacent_cells()
|
|
ui_manager.ActionState.PUTTING:
|
|
local_player.highlight_occupied_playerboard_slots()
|
|
ui_manager.ActionState.RANDOMIZING:
|
|
local_player.highlight_random_valid_cells()
|
|
ui_manager.ActionState.ARRANGING:
|
|
_show_arrangement_ui()
|
|
local_player.highlight_occupied_playerboard_slots()
|
|
ui_manager.ActionState.PLACING_OBSTACLE:
|
|
local_player.highlight_valid_obstacle_cells()
|
|
|
|
func _show_arrangement_ui():
|
|
if ui_manager.playerboard_ui:
|
|
ui_manager.playerboard_ui.visible = true
|
|
ui_manager.update_playerboard_ui()
|
|
|
|
func _on_playerboard_slot_clicked(event, slot_index):
|
|
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
|
var local_player = GameStateManager.local_player_character
|
|
if not local_player:
|
|
return
|
|
|
|
match ui_manager.current_action_state:
|
|
ui_manager.ActionState.ARRANGING:
|
|
local_player.arrange_playerboard_item(slot_index)
|
|
|
|
# =============================================================================
|
|
# Obstacle Management
|
|
# =============================================================================
|
|
|
|
func place_obstacle(grid_position: Vector2i) -> bool:
|
|
var local_player = GameStateManager.local_player_character
|
|
var success = obstacle_manager.place_obstacle(grid_position, local_player)
|
|
|
|
if success:
|
|
local_player.clear_highlights()
|
|
_set_action_state(ui_manager.ActionState.NONE)
|
|
|
|
if is_multiplayer_authority():
|
|
rpc("sync_place_obstacle", grid_position.x, grid_position.y, 3,
|
|
obstacle_manager.current_obstacle_item, obstacle_manager.current_obstacle_orientation)
|
|
|
|
return success
|
|
|
|
@rpc("any_peer", "call_local")
|
|
func sync_place_obstacle(x: int, y: int, floor_index: int, item_index: int, orientation: int):
|
|
$EnhancedGridMap.place_obstacle(Vector3i(x, floor_index, y), item_index, orientation)
|
|
|
|
# =============================================================================
|
|
# Goal & Playerboard Sync
|
|
# =============================================================================
|
|
|
|
@rpc("reliable")
|
|
func sync_preset_goals(goals_list: Array):
|
|
GoalManager.preset_goals = goals_list
|
|
|
|
@rpc("any_peer", "call_local")
|
|
func sync_player_goals(player_id: int, goals: Array):
|
|
var player = get_node_or_null(str(player_id))
|
|
if player:
|
|
player.goals = goals.duplicate()
|
|
if multiplayer.is_server():
|
|
update_all_players_goals()
|
|
|
|
@rpc("any_peer", "call_local")
|
|
func sync_playerboard(player_id: int, new_playerboard: Array):
|
|
if player_id == multiplayer.get_unique_id() and GameStateManager.local_player_character:
|
|
ui_manager.update_playerboard_ui()
|
|
update_all_players_boards()
|
|
|
|
# =============================================================================
|
|
# UI Update Functions
|
|
# =============================================================================
|
|
|
|
func update_all_players_goals():
|
|
if not GameStateManager.is_game_started():
|
|
return
|
|
|
|
var all_players = get_tree().get_nodes_in_group("Players")
|
|
all_players.sort_custom(func(a, b):
|
|
var a_id = int(String(a.name).get_slice("@", 0))
|
|
var b_id = int(String(b.name).get_slice("@", 0))
|
|
return a_id < b_id
|
|
)
|
|
|
|
for i in range($AllPlayerGoals.get_child_count()):
|
|
$AllPlayerGoals.get_child(i).visible = false
|
|
|
|
var max_panels = $AllPlayerGoals.get_child_count()
|
|
for i in range(min(all_players.size(), max_panels)):
|
|
var player = all_players[i]
|
|
if player and player.goals.size() > 0:
|
|
$AllPlayerGoals.get_child(i).visible = true
|
|
_update_player_goals_ui(i, player.goals)
|
|
|
|
func _update_player_goals_ui(player_idx: int, goals: Array):
|
|
if player_idx < 0 or player_idx >= $AllPlayerGoals.get_child_count():
|
|
return
|
|
|
|
var panel = $AllPlayerGoals.get_child(player_idx)
|
|
if not panel.has_node("MarginContainer/Playergoals"):
|
|
return
|
|
|
|
var goals_grid = panel.get_node("MarginContainer/Playergoals")
|
|
for slot_idx in range(9):
|
|
if slot_idx >= goals_grid.get_child_count():
|
|
break
|
|
|
|
var slot = goals_grid.get_child(slot_idx)
|
|
var goal_value = goals[slot_idx] if slot_idx < goals.size() else -1
|
|
|
|
for tile_name in ["TileHeart", "TileDiamond", "TileStar", "TileCoin"]:
|
|
if slot.has_node(tile_name):
|
|
slot.get_node(tile_name).hide()
|
|
|
|
match goal_value:
|
|
7:
|
|
if slot.has_node("TileHeart"):
|
|
slot.get_node("TileHeart").show()
|
|
8:
|
|
if slot.has_node("TileDiamond"):
|
|
slot.get_node("TileDiamond").show()
|
|
9:
|
|
if slot.has_node("TileStar"):
|
|
slot.get_node("TileStar").show()
|
|
10:
|
|
if slot.has_node("TileCoin"):
|
|
slot.get_node("TileCoin").show()
|
|
|
|
func update_all_players_boards():
|
|
if not GameStateManager.is_game_started():
|
|
return
|
|
|
|
var all_players = get_tree().get_nodes_in_group("Players")
|
|
var all_player_boards = $AllPlayerBoards
|
|
|
|
# Update boards (simplified version - full implementation would mirror original)
|
|
pass
|
|
|
|
# =============================================================================
|
|
# Connection Verification
|
|
# =============================================================================
|
|
|
|
func verify_all_connections():
|
|
if multiplayer.is_server():
|
|
for peer_id in GameStateManager.players:
|
|
if peer_id != 1:
|
|
rpc_id(peer_id, "connection_verify", GameStateManager.players)
|
|
|
|
@rpc
|
|
func connection_verify(expected_players: Array):
|
|
for peer_id in expected_players:
|
|
if peer_id != multiplayer.get_unique_id() and not has_node(str(peer_id)):
|
|
rpc_id(1, "request_specific_player_data", peer_id)
|
|
|
|
@rpc("any_peer")
|
|
func request_specific_player_data(requested_peer_id: int):
|
|
if multiplayer.is_server():
|
|
var player = get_node_or_null(str(requested_peer_id))
|
|
if player:
|
|
var player_data = {
|
|
"peer_id": requested_peer_id,
|
|
"position": player.current_position,
|
|
"goals": player.goals,
|
|
"playerboard": player.playerboard,
|
|
"is_bot": player.is_bot || player.is_in_group("Bots")
|
|
}
|
|
rpc_id(multiplayer.get_remote_sender_id(), "create_specific_player", player_data)
|
|
|
|
@rpc("any_peer")
|
|
func request_full_player_sync(requesting_peer_id: int):
|
|
if multiplayer.is_server():
|
|
for peer_id in GameStateManager.players:
|
|
var player = get_node_or_null(str(peer_id))
|
|
if player:
|
|
var player_data = {
|
|
"peer_id": peer_id,
|
|
"position": player.current_position,
|
|
"goals": player.goals,
|
|
"playerboard": player.playerboard,
|
|
"is_bot": player.is_bot || player.is_in_group("Bots")
|
|
}
|
|
rpc_id(requesting_peer_id, "create_specific_player", player_data)
|
|
await get_tree().create_timer(0.1).timeout
|
|
|
|
@rpc("reliable")
|
|
func create_specific_player(data: Dictionary):
|
|
var peer_id = data["peer_id"]
|
|
if has_node(str(peer_id)):
|
|
return
|
|
|
|
var player_character = PlayerManager.add_player_character(peer_id)
|
|
player_character.current_position = data["position"]
|
|
add_child(player_character)
|
|
player_character.add_to_group("Players", true)
|
|
|
|
if data["is_bot"]:
|
|
player_character.add_to_group("Bots", true)
|
|
player_character.is_bot = true
|
|
player_character.rpc("sync_bot_status", true)
|
|
|
|
player_character.goals = data["goals"].duplicate()
|
|
player_character.playerboard = data["playerboard"].duplicate()
|
|
player_character.global_position = Vector3(
|
|
data["position"].x * 2 + 1,
|
|
1.0,
|
|
data["position"].y * 2 + 1
|
|
)
|
|
player_character.rpc("sync_position", data["position"])
|
|
|
|
# =============================================================================
|
|
# Grid Item Randomization
|
|
# =============================================================================
|
|
|
|
func randomize_item_at_position(grid_position: Vector2i):
|
|
if not multiplayer.is_server():
|
|
rpc_id(1, "request_randomize_item", grid_position)
|
|
return
|
|
|
|
var enhanced_gridmap = $EnhancedGridMap
|
|
if enhanced_gridmap:
|
|
var cell = Vector3i(grid_position.x, 1, grid_position.y)
|
|
var current_item = enhanced_gridmap.get_cell_item(cell)
|
|
|
|
if current_item != -1:
|
|
var rng = RandomNumberGenerator.new()
|
|
rng.randomize()
|
|
var new_item = rng.randi_range(7, 10)
|
|
|
|
while new_item == current_item:
|
|
new_item = rng.randi_range(7, 10)
|
|
|
|
sync_grid_item(cell.x, cell.y, cell.z, new_item)
|
|
rpc("sync_grid_item", cell.x, cell.y, cell.z, new_item)
|
|
|
|
@rpc("any_peer")
|
|
func request_randomize_item(grid_position: Vector2i):
|
|
if multiplayer.is_server():
|
|
randomize_item_at_position(grid_position)
|
|
|
|
@rpc("any_peer", "call_local", "reliable")
|
|
func sync_grid_item(x: int, y: int, z: int, item: int):
|
|
var enhanced_gridmap = $EnhancedGridMap
|
|
if enhanced_gridmap:
|
|
enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item)
|