Files
tekton/scenes/main.gd
T
2024-10-25 13:18:37 +08:00

435 lines
13 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_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
# Action State Management
enum ActionState {
NONE,
MOVING,
GRABBING,
PUTTING,
RANDOMIZING,
ARRANGING
}
var current_action_state = ActionState.NONE
# UI References
@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
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():
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 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(): set_action_state(ActionState.PUTTING))
randomize_button.pressed.connect(func(): set_action_state(ActionState.RANDOMIZING))
arrange_button.pressed.connect(func(): set_action_state(ActionState.ARRANGING))
func setup_playerboard_ui():
# Clear existing children
for child in playerboard_ui.get_children():
child.queue_free()
# Setup grid
playerboard_ui.columns = 5
# Create slots
for i in range(25):
var slot = TextureRect.new()
slot.custom_minimum_size = Vector2(40, 40)
slot.gui_input.connect(func(event): _on_playerboard_slot_clicked(event, i))
# Add a colored rectangle as background
var style = StyleBoxFlat.new()
style.bg_color = Color(0.2, 0.2, 0.2, 1.0)
style.border_color = Color(0.4, 0.4, 0.4, 1.0)
slot.add_theme_stylebox_override("panel", style)
playerboard_ui.add_child(slot)
func set_action_state(new_state):
if not local_player_character:
return
current_action_state = new_state
# Clear previous highlights
local_player_character.clear_highlights()
match new_state:
ActionState.MOVING:
local_player_character.highlight_movement_range()
ActionState.GRABBING:
local_player_character.highlight_adjacent_cells()
ActionState.PUTTING:
local_player_character.highlight_empty_adjacent_cells()
ActionState.RANDOMIZING:
local_player_character.highlight_random_valid_cells()
ActionState.ARRANGING:
show_arrangement_ui()
func update_button_states():
if not local_player_character:
return
var can_move = not local_player_character.is_player_moving and not local_player_character.has_performed_action
var can_grab = local_player_character.has_item_at_current_position()
var can_put = local_player_character.has_items_in_playerboard() and not local_player_character.has_item_at_current_position()
var can_randomize = not local_player_character.has_performed_action
var can_arrange = local_player_character.has_items_in_playerboard()
move_button.disabled = not can_move
grab_button.disabled = not can_grab
put_button.disabled = not can_put
randomize_button.disabled = not can_randomize
arrange_button.disabled = not can_arrange
func _on_playerboard_slot_clicked(event, slot_index):
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
if current_action_state == ActionState.ARRANGING and local_player_character:
local_player_character.arrange_playerboard_item(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]
if item != -1:
# Update slot appearance based on item
slot.modulate = Color(1, 1, 1, 1)
# You can set different colors or textures based on item type
slot.self_modulate = Color(0.5 + (item * 0.1), 0.7, 0.3, 1.0)
else:
slot.modulate = Color(0.5, 0.5, 0.5, 0.5)
slot.self_modulate = Color(1, 1, 1, 1)
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)
# 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
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) # 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
var bot = get_node(str(bot_id))
if bot and (turn_based_mode or not bot.is_player_moving):
moving_bots[bot_id] = true
var best_move = evaluate_bot_move(bot)
match best_move.action:
"grab":
if bot.grab_item():
bot.has_performed_action = true
moving_bots[bot_id] = false
# Don't end turn, allow movement after grab
move_bot(bot_id) # Try to move after grabbing
"put":
if bot.put_item():
bot.has_performed_action = true
moving_bots[bot_id] = false
# Don't end turn, allow movement after put
move_bot(bot_id) # Try to move after putting
"move":
if bot.has_performed_action:
var path = bot.enhanced_gridmap.find_path(Vector2(bot.current_position), Vector2(best_move.position))
if path.size() > 1:
path.pop_front()
var trimmed_path = path.slice(0, bot.movement_range)
bot.rotate_towards_target(best_move.position)
bot.move_bot_along_path(trimmed_path, bot_id)
else:
moving_bots[bot_id] = false
if turn_based_mode:
end_current_turn()
else:
moving_bots[bot_id] = false
if turn_based_mode:
end_current_turn()
_:
moving_bots[bot_id] = false
if turn_based_mode:
end_current_turn()
func bot_movement_completed(bot_id):
if multiplayer.is_server():
moving_bots[bot_id] = false
func evaluate_bot_move(bot: Node) -> Dictionary:
var best_move = {
"action": "none",
"position": bot.current_position,
"value": -1
}
# If no action performed yet, prioritize grab/put
if not bot.has_performed_action:
# Check current position for grabbing
var current_cell = Vector3i(bot.current_position.x, 1, bot.current_position.y)
var current_item = bot.enhanced_gridmap.get_cell_item(current_cell)
# Evaluate grabbing current item if it matches goals
if current_item != -1 and current_item in bot.goals and bot.playerboard.find(-1) != -1:
return {
"action": "grab",
"position": bot.current_position,
"value": 10
}
# Evaluate putting item from playerboard
for goal in bot.goals:
var item_index = bot.playerboard.find(goal)
if item_index != -1 and current_item == -1:
return {
"action": "put",
"position": bot.current_position,
"value": 15
}
else:
# Bot has performed action, look for movement
var neighbors = bot.enhanced_gridmap.get_neighbors(bot.current_position, 1)
for neighbor in neighbors:
if not neighbor.is_walkable:
continue
if not bot.is_within_movement_range(neighbor.position):
continue
var cell = Vector3i(neighbor.position.x, 1, neighbor.position.y)
var item = bot.enhanced_gridmap.get_cell_item(cell)
# Evaluate position for next turn
if item in bot.goals or item == -1:
var value = 8
if value > best_move.value:
best_move = {
"action": "move",
"position": neighbor.position,
"value": value
}
return best_move