404 lines
12 KiB
GDScript
404 lines
12 KiB
GDScript
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_players = 4
|
|
var bots = []
|
|
|
|
@export var turn_based_mode: bool = true
|
|
#var bot_move_timer: float = 0.0
|
|
#const BOT_MOVE_INTERVAL: float = 2.0
|
|
|
|
#var moving_bots = {}
|
|
|
|
enum ActionState {
|
|
NONE,
|
|
MOVING,
|
|
GRABBING,
|
|
PUTTING,
|
|
RANDOMIZING,
|
|
ARRANGING
|
|
}
|
|
|
|
var current_action_state = ActionState.NONE
|
|
|
|
@onready var action_menu = $ActionMenu
|
|
@onready var move_button = $ActionMenu/ActionButtonContainer/MoveButton
|
|
@onready var grab_button = $ActionMenu/ActionButtonContainer/GrabButton
|
|
@onready var put_button = $ActionMenu/ActionButtonContainer/PutButton
|
|
@onready var randomize_button = $ActionMenu/ActionButtonContainer/RandomizeButton
|
|
@onready var arrange_button = $ActionMenu/ActionButtonContainer/ArrangeButton
|
|
@onready var playerboard_ui = $PlayerboardUI
|
|
|
|
const item_tex = [
|
|
preload("res://assets/textures/player_board_and_blue_print/tile_null.tres"),
|
|
preload("res://assets/textures/player_board_and_blue_print/tile_heart.tres"),
|
|
preload("res://assets/textures/player_board_and_blue_print/tile_diamond.tres"),
|
|
preload("res://assets/textures/player_board_and_blue_print/tile_star.tres"),
|
|
preload("res://assets/textures/player_board_and_blue_print/tile_coin.tres")
|
|
]
|
|
|
|
func _ready():
|
|
multiplayer_peer.peer_connected.connect(_on_peer_connected)
|
|
multiplayer_peer.peer_disconnected.connect(_on_peer_disconnected)
|
|
setup_action_buttons()
|
|
setup_playerboard_ui()
|
|
|
|
func _process(delta):
|
|
if multiplayer.is_server() and game_started:
|
|
if turn_based_mode:
|
|
rpc("sync_turn_index", current_turn_index)
|
|
|
|
func setup_action_buttons():
|
|
move_button.pressed.connect(func(): set_action_state(ActionState.MOVING))
|
|
grab_button.pressed.connect(func(): set_action_state(ActionState.GRABBING))
|
|
put_button.pressed.connect(func():
|
|
if local_player_character:
|
|
local_player_character.handle_put_action()
|
|
set_action_state(ActionState.PUTTING)
|
|
)
|
|
randomize_button.pressed.connect(func(): set_action_state(ActionState.RANDOMIZING))
|
|
arrange_button.pressed.connect(func():
|
|
if local_player_character and local_player_character.action_points >= 2:
|
|
set_action_state(ActionState.ARRANGING)
|
|
)
|
|
|
|
func setup_playerboard_ui():
|
|
for child in playerboard_ui.get_children():
|
|
child.queue_free()
|
|
|
|
playerboard_ui.columns = 5
|
|
|
|
for i in range(25):
|
|
var slot = TextureRect.new()
|
|
|
|
var highlight_rect = TextureRect.new()
|
|
var hr_tex = load("res://assets/models/pboard/HighlightRect.tres")
|
|
|
|
var select_rect = TextureRect.new()
|
|
var sr_tex = load("res://assets/models/pboard/SelectRect.tres")
|
|
|
|
var adjacent_rect = TextureRect.new()
|
|
var ar_tex = load("res://assets/models/pboard/AdjacentRect.tres")
|
|
|
|
slot.custom_minimum_size = Vector2(36, 36)
|
|
slot.gui_input.connect(func(event): _on_playerboard_slot_clicked(event, i))
|
|
slot.texture = item_tex[0]
|
|
playerboard_ui.add_child(slot, true)
|
|
|
|
highlight_rect.texture = hr_tex
|
|
highlight_rect.size = Vector2(36, 36)
|
|
select_rect.texture = sr_tex
|
|
select_rect.size = Vector2(36, 36)
|
|
adjacent_rect.texture = ar_tex
|
|
adjacent_rect.size = Vector2(36, 36)
|
|
|
|
slot.add_child(highlight_rect)
|
|
slot.add_child(select_rect)
|
|
slot.add_child(adjacent_rect)
|
|
|
|
slot.get_child(0).hide()
|
|
slot.get_child(1).hide()
|
|
slot.get_child(2).hide()
|
|
|
|
func set_action_state(new_state):
|
|
if not local_player_character or not local_player_character.is_multiplayer_authority() or current_action_state == new_state or local_player_character.action_points <= 0:
|
|
return
|
|
|
|
current_action_state = new_state
|
|
if local_player_character.is_bot == false:
|
|
local_player_character.clear_highlights()
|
|
local_player_character.clear_playerboard_highlights()
|
|
|
|
match new_state:
|
|
ActionState.MOVING:
|
|
if local_player_character.is_bot == false:
|
|
local_player_character.highlight_movement_range()
|
|
ActionState.GRABBING:
|
|
if local_player_character.is_bot == false:
|
|
local_player_character.highlight_adjacent_cells()
|
|
if local_player_character.has_item_at_current_position():
|
|
local_player_character.highlighted_cells.append(local_player_character.current_position)
|
|
local_player_character.enhanced_gridmap.set_cell_item(
|
|
Vector3i(local_player_character.current_position.x, 0, local_player_character.current_position.y),
|
|
local_player_character.enhanced_gridmap.hover_item
|
|
)
|
|
ActionState.PUTTING:
|
|
if local_player_character.is_bot == false:
|
|
local_player_character.highlight_occupied_playerboard_slots()
|
|
ActionState.RANDOMIZING:
|
|
if local_player_character.is_bot == false:
|
|
local_player_character.highlight_random_valid_cells()
|
|
ActionState.ARRANGING:
|
|
show_arrangement_ui()
|
|
if local_player_character.is_bot == false:
|
|
local_player_character.highlight_occupied_playerboard_slots()
|
|
|
|
func update_button_states():
|
|
if not local_player_character or local_player_character.is_in_group("Bots"):
|
|
move_button.visible = false
|
|
grab_button.visible = false
|
|
put_button.visible = false
|
|
randomize_button.visible = false
|
|
arrange_button.visible = false
|
|
return
|
|
|
|
move_button.visible = true
|
|
grab_button.visible = true
|
|
put_button.visible = true
|
|
randomize_button.visible = true
|
|
arrange_button.visible = true
|
|
|
|
move_button.disabled = local_player_character.is_player_moving or local_player_character.has_performed_action
|
|
grab_button.disabled = not local_player_character.has_item_at_current_position()
|
|
put_button.disabled = not (local_player_character.has_items_in_playerboard() and not local_player_character.has_item_at_current_position())
|
|
randomize_button.disabled = local_player_character.has_performed_action
|
|
arrange_button.disabled = not local_player_character.has_items_in_playerboard()
|
|
|
|
func _on_playerboard_slot_clicked(event, slot_index):
|
|
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
|
if not local_player_character:
|
|
return
|
|
|
|
match current_action_state:
|
|
ActionState.ARRANGING:
|
|
local_player_character.arrange_playerboard_item(slot_index)
|
|
ActionState.GRABBING:
|
|
local_player_character.handle_playerboard_slot_selected(slot_index)
|
|
ActionState.PUTTING:
|
|
local_player_character.handle_put_slot_selected(slot_index)
|
|
|
|
func update_playerboard_ui():
|
|
if not local_player_character:
|
|
return
|
|
|
|
for i in range(25):
|
|
var slot = playerboard_ui.get_child(i)
|
|
var item = local_player_character.playerboard[i]
|
|
|
|
slot.texture = item_tex[0]
|
|
|
|
match item:
|
|
7: slot.texture = item_tex[1]
|
|
8: slot.texture = item_tex[2]
|
|
9: slot.texture = item_tex[3]
|
|
10: slot.texture = item_tex[4]
|
|
|
|
func update_playerboard_highlights(highlighted_slots: Array):
|
|
for i in range(playerboard_ui.get_child_count()):
|
|
var slot = playerboard_ui.get_child(i)
|
|
if slot.get_child_count() > 1:
|
|
slot.get_child(1).visible = highlighted_slots.has(i)
|
|
|
|
func show_arrangement_ui():
|
|
if playerboard_ui:
|
|
playerboard_ui.visible = true
|
|
update_playerboard_ui()
|
|
|
|
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)
|
|
|
|
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():
|
|
# Increase delay to ensure scene is ready
|
|
await get_tree().create_timer(1.5).timeout
|
|
|
|
# Sync full state first
|
|
rpc_id(new_peer_id, "sync_game_state", players, bots, game_started, turn_based_mode)
|
|
|
|
# Then add players in correct order
|
|
for peer_id in connected_peer_ids:
|
|
rpc_id(new_peer_id, "add_player_character", peer_id)
|
|
|
|
# Finally add the new player
|
|
add_player_character(new_peer_id)
|
|
rpc("add_newly_connected_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())
|
|
|
|
@rpc("any_peer", "call_local")
|
|
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)
|
|
|
|
if multiplayer.is_server() and bots.size() > 0:
|
|
var bot_to_replace = get_node_or_null(str(bots[0]))
|
|
if bot_to_replace:
|
|
player_character.current_position = bot_to_replace.current_position
|
|
|
|
add_child(player_character)
|
|
player_character.add_to_group("Players", true)
|
|
|
|
if peer_id == multiplayer.get_unique_id():
|
|
local_player_character = player_character
|
|
update_button_states()
|
|
update_playerboard_ui()
|
|
|
|
func add_bot(bot_id):
|
|
rpc("create_bot", bot_id)
|
|
|
|
|
|
@rpc("call_local")
|
|
func create_bot(bot_id):
|
|
# Ensure we're not duplicating bots
|
|
if has_node(str(bot_id)):
|
|
push_error("Bot already exists: " + str(bot_id))
|
|
return
|
|
|
|
var bot_character = player_scene.instantiate()
|
|
if not bot_character:
|
|
push_error("Failed to instantiate bot scene")
|
|
return
|
|
|
|
bot_character.set_multiplayer_authority(1) # Server controls bots
|
|
bot_character.name = str(bot_id)
|
|
|
|
# Add to scene tree
|
|
call_deferred("add_child", bot_character)
|
|
|
|
# Add to groups after adding to scene tree
|
|
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)
|
|
# Sync bot status after a short delay to ensure node is ready
|
|
await get_tree().create_timer(0.1).timeout
|
|
bot_character.rpc("sync_bot_status", true)
|
|
|
|
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)
|
|
rpc("remove_bot", bot_id)
|
|
rpc("sync_players", players)
|
|
|
|
@rpc("call_local")
|
|
func remove_bot(bot_id):
|
|
var bot_node = get_node_or_null(str(bot_id))
|
|
if bot_node:
|
|
bot_node.queue_free()
|
|
|
|
func get_next_available_bot_id() -> int:
|
|
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
|
|
|
|
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 = (current_turn_index + 1) % players.size()
|
|
rpc("set_current_turn", 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
|
|
|
|
for player in get_tree().get_nodes_in_group("Players"):
|
|
if player.name == str(player_id):
|
|
player.is_my_turn = true
|
|
player.action_points = 2
|
|
player.has_moved_this_turn = false
|
|
player.has_performed_action = false
|
|
player.start_turn()
|
|
else:
|
|
player.is_my_turn = false
|
|
|
|
func end_current_turn():
|
|
if multiplayer.is_server():
|
|
next_turn()
|
|
rpc("sync_turn_index", current_turn_index)
|