Files
tekton/scenes/main.gd
T
2025-01-28 14:17:29 +08:00

521 lines
16 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)
else:
pass
#bot_move_timer += delta
#if bot_move_timer >= BOT_MOVE_INTERVAL:
#bot_move_timer = 0.0
#var current_bots = bots.duplicate()
#for bot_id in current_bots:
#if not moving_bots.get(bot_id, false):
#move_bot(bot_id)
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
local_player_character.clear_highlights()
local_player_character.clear_playerboard_highlights()
match new_state:
ActionState.MOVING:
local_player_character.highlight_movement_range()
ActionState.GRABBING:
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:
local_player_character.highlight_occupied_playerboard_slots()
ActionState.RANDOMIZING:
local_player_character.highlight_random_valid_cells()
ActionState.ARRANGING:
show_arrangement_ui()
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():
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)
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):
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)
bot_character.name = str(bot_id)
add_child(bot_character)
bot_character.add_to_group("Players", true)
bot_character.add_to_group("Bots", true)
# The BehaviorTree will auto-activate for bots
var behavior_tree = bot_character.get_node_or_null("BotBehavior")
if behavior_tree:
behavior_tree.enabled = true
behavior_tree.actor = bot_character
if multiplayer.is_server():
bots.append(bot_id)
players.append(bot_id)
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])
#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
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)
#func move_bot(bot_id):
#if not multiplayer.is_server() or moving_bots.get(bot_id, false):
#return
#
#var bot = get_node_or_null(str(bot_id))
#if not is_instance_valid(bot) or bot.is_player_moving:
#moving_bots.erase(bot_id)
#return
#
#if bot.action_points <= 0:
#if turn_based_mode:
#moving_bots[bot_id] = false
#end_current_turn()
#else:
#bot.action_points = 2
#bot.has_moved_this_turn = false
#bot.has_performed_action = false
#moving_bots[bot_id] = false
#await get_tree().create_timer(0.5).timeout
#move_bot(bot_id)
#return
#
#moving_bots[bot_id] = true
#await get_tree().create_timer(0.5).timeout
#
#if not is_instance_valid(bot):
#moving_bots.erase(bot_id)
#return
#
#var best_move = evaluate_bot_move(bot)
#execute_bot_move(bot, bot_id, best_move)
#func execute_bot_move(bot, bot_id, move):
#match move.action:
#"arrange":
#if bot.action_points >= 2:
#bot.arrange_playerboard_item(bot.find_best_arrangement_slot())
#bot.action_points -= 2
#moving_bots[bot_id] = false
#if bot.action_points > 0 or not turn_based_mode:
#move_bot(bot_id)
#elif turn_based_mode:
#end_current_turn()
#
#"grab", "put":
#var success = bot.grab_item(move.position) if move.action == "grab" else bot.put_item(move.position)
#if success:
#bot.action_points -= 1
#moving_bots[bot_id] = false
#if bot.action_points > 0 or not turn_based_mode:
#move_bot(bot_id)
#elif turn_based_mode:
#end_current_turn()
#
#"move":
#if is_instance_valid(bot) and bot.action_points >= 1:
#var path = bot.enhanced_gridmap.find_path(Vector2(bot.current_position), Vector2(move.position))
#if path.size() > 1:
#path.pop_front()
#var trimmed_path = path.slice(0, bot.movement_range)
#bot.rotate_towards_target(move.position)
#bot.move_bot_along_path(trimmed_path, bot_id)
#bot.action_points -= 1
#else:
#moving_bots[bot_id] = false
#if bot.action_points > 0 or not turn_based_mode:
#move_bot(bot_id)
#elif turn_based_mode:
#end_current_turn()
#else:
#moving_bots[bot_id] = false
#if bot.action_points > 0 or not turn_based_mode:
#move_bot(bot_id)
#elif turn_based_mode:
#end_current_turn()
#
#_:
#moving_bots[bot_id] = false
#if turn_based_mode:
#end_current_turn()
#else:
#move_bot(bot_id)
#func evaluate_bot_move(bot: Node) -> Dictionary:
#if not is_instance_valid(bot) or bot.action_points <= 0:
#return { "action": "none", "position": Vector2i.ZERO, "value": -1 }
#
#var moves = []
#
#if bot.action_points >= 2 and bot.check_playerboard_arrangement(bot):
#moves.append({ "action": "arrange", "position": bot.current_position, "value": 20 })
#
#if bot.action_points >= 1:
#var put_position = bot.find_best_put_position(bot)
#if put_position != Vector2i(-1, -1):
#moves.append({ "action": "put", "position": put_position, "value": 18 })
#
#if bot.action_points >= 1 and not bot.playerboard_is_full():
#var grab_position = bot.find_best_grab_position()
#if grab_position != Vector2i(-1, -1):
#moves.append({ "action": "grab", "position": grab_position, "value": 15 })
#
#if bot.action_points >= 1 and not bot.has_moved_this_turn:
#var random_pos = bot.find_random_valid_position_in_range()
#if random_pos != bot.current_position:
#moves.append({ "action": "move", "position": random_pos, "value": 5 })
#
#moves.sort_custom(func(a, b): return a.value > b.value)
#return moves[0] if moves.size() > 0 else { "action": "none", "position": bot.current_position, "value": 0 }
#func bot_movement_completed(bot_id):
#if not multiplayer.is_server():
#return
#
#moving_bots[bot_id] = false
#var bot = get_node_or_null(str(bot_id))
#
#if not is_instance_valid(bot):
#moving_bots.erase(bot_id)
#return
#
#if bot.action_points > 0 or not turn_based_mode:
#await get_tree().create_timer(0.5).timeout
#if is_instance_valid(bot):
#move_bot(bot_id)
#else:
#end_current_turn()