模板脚本文件

This commit is contained in:
2026-09-12 22:18:23 +08:00
parent 2816092fb4
commit d3f1058d79
32 changed files with 3200 additions and 0 deletions
+487
View File
@@ -0,0 +1,487 @@
extends Node3D
const MAX_CHAT_MESSAGE_LENGTH := 160
const MIN_NICKNAME_HEIGHT := 2.0
const MAX_NICKNAME_HEIGHT := 8.0
@export var player_scene: PackedScene
var chat_visible := false
var inventory_visible := false
var player_list_visible := false
var _player_nickname_heights: Dictionary = {}
var _nickname_heights_requested := false
var _nickname_height_requesters: Dictionary = {}
@onready var players_container: Node3D = $PlayersContainer
@onready var main_menu: MainMenuUI = $MainMenuUI
@onready var multiplayer_chat: MultiplayerChatUI = $MultiplayerChatUI
@onready var inventory_ui: InventoryUI = $InventoryUI
@onready var player_list_ui: PlayerListUI = $PlayerListUI
@onready var pause_menu: PauseMenuUI = $PauseMenuUI
func _ready():
after_ready()
if DisplayServer.get_name() == "headless":
Network.start_host("", "")
pause_menu.hide_menu()
main_menu.show_menu()
multiplayer_chat.set_process_input(true)
main_menu.host_pressed.connect(_on_host_pressed)
main_menu.join_pressed.connect(_on_join_pressed)
main_menu.quit_pressed.connect(_on_quit_pressed)
pause_menu.resume_pressed.connect(_on_pause_resume_pressed)
pause_menu.main_menu_pressed.connect(_on_pause_main_menu_pressed)
pause_menu.quit_pressed.connect(_on_pause_quit_pressed)
if inventory_ui:
inventory_ui.inventory_closed.connect(_on_inventory_closed)
if multiplayer_chat:
multiplayer_chat.message_sent.connect(_on_chat_message_sent)
Network.server_disconnected.connect(_on_server_disconnected)
Network.connect("player_connected", Callable(self, "_on_player_connected"))
multiplayer.peer_disconnected.connect(_remove_player)
_update_mouse_mode()
func _process(_delta: float) -> void:
var can_show_player_list := (
not main_menu.is_menu_visible() and not pause_menu.is_menu_visible() and multiplayer.has_multiplayer_peer()
)
if Input.is_key_pressed(KEY_TAB) and can_show_player_list:
if not player_list_visible:
_show_player_list()
elif player_list_visible:
_hide_player_list()
func after_ready():
var ip_address: String
if OS.has_feature("windows"):
if OS.has_environment("COMPUTERNAME"):
ip_address = IP.resolve_hostname(str(OS.get_environment("COMPUTERNAME")), IP.TYPE_IPV4)
elif OS.has_feature("x11"):
if OS.has_environment("HOSTNAME"):
ip_address = IP.resolve_hostname(str(OS.get_environment("HOSTNAME")), IP.TYPE_IPV4)
elif OS.has_feature("OSX"):
if OS.has_environment("HOSTNAME"):
ip_address = IP.resolve_hostname(str(OS.get_environment("HOSTNAME")), IP.TYPE_IPV4)
main_menu.address_input.text = ip_address
func _on_server_disconnected():
_reset_session_ui()
func _reset_session_ui() -> void:
for child in players_container.get_children():
child.queue_free()
var loot_root := get_node_or_null("Environment/ItemContainer")
if loot_root:
for child in loot_root.get_children():
child.queue_free()
chat_visible = false
inventory_visible = false
_hide_player_list()
_player_nickname_heights.clear()
_nickname_heights_requested = false
_nickname_height_requesters.clear()
multiplayer_chat.close_chat()
multiplayer_chat.clear_chat()
if inventory_ui:
inventory_ui.close_inventory()
inventory_ui.current_player = null
_hide_pause_menu(false)
main_menu.show_menu()
_update_mouse_mode()
func _on_player_connected(peer_id, player_info):
var player = _add_player(peer_id, player_info)
if multiplayer.is_server() and player:
player.call_deferred("_sync_inventory_to_owner")
if multiplayer.is_server():
if peer_id != 1:
call_deferred("_sync_nickname_heights_to_peer", peer_id)
elif not _nickname_heights_requested:
_nickname_heights_requested = true
call_deferred("_request_nickname_heights")
_refresh_player_list()
func _on_host_pressed(nickname: String, skin: String):
var error = Network.start_host(nickname, skin)
if error:
push_warning("Failed to host game. Error: " + str(error))
main_menu.show_menu()
_update_mouse_mode()
return
main_menu.hide_menu()
_update_mouse_mode()
func _on_join_pressed(nickname: String, skin: String, address: String):
var error = Network.join_game(nickname, skin, address)
if error:
push_warning("Failed to join game. Error: " + str(error))
main_menu.show_menu()
_update_mouse_mode()
return
main_menu.hide_menu()
_update_mouse_mode()
func _add_player(id: int, player_info: Dictionary) -> Character:
if DisplayServer.get_name() == "headless" and id == 1:
return null
if players_container.has_node(str(id)):
return players_container.get_node(str(id)) as Character
var player = player_scene.instantiate()
player.name = str(id)
player.position = get_spawn_point(id)
players_container.add_child(player, true)
var nick = Network.sanitize_nickname(str(player_info.get("nick", "")), "Player_" + str(id))
player.nickname.text = nick
var skin_enum = Network.sanitize_skin_value(player_info.get("skin", Character.SkinColor.BLUE))
player.set_player_skin(skin_enum)
_apply_player_nickname_height(id)
return player
func get_spawn_point(id: int) -> Vector3:
var spawn_angle := fmod(float(id) * 2.399963229728653, 2.0 * PI)
var spawn_point := Vector2.from_angle(spawn_angle) * 10
return Vector3(spawn_point.x, 0, spawn_point.y)
func _remove_player(id):
_player_nickname_heights.erase(id)
if not players_container.has_node(str(id)):
call_deferred("_refresh_player_list")
return
var player_node = players_container.get_node(str(id))
if player_node:
player_node.queue_free()
call_deferred("_refresh_player_list")
func _on_quit_pressed() -> void:
Network.leave_game()
get_tree().quit()
func toggle_chat():
if main_menu.is_menu_visible() or is_gameplay_input_blocked():
return
multiplayer_chat.toggle_chat()
chat_visible = multiplayer_chat.is_chat_visible()
_update_mouse_mode()
func is_chat_visible() -> bool:
return multiplayer_chat.is_chat_visible()
func _input(event):
if event.is_action_pressed("pause"):
_handle_pause_action()
get_viewport().set_input_as_handled()
return
if is_gameplay_input_blocked():
return
if event.is_action_pressed("toggle_chat"):
toggle_chat()
elif chat_visible and multiplayer_chat.message.has_focus():
pass
elif event.is_action_pressed("inventory"):
toggle_inventory()
elif event is InputEventKey and event.pressed and event.keycode == KEY_F1:
_debug_add_item()
elif event is InputEventKey and event.pressed and event.keycode == KEY_F2:
_debug_print_inventory()
func _on_chat_message_sent(message_text: String) -> void:
chat_visible = multiplayer_chat.is_chat_visible()
_update_mouse_mode()
var trimmed_message = _sanitize_chat_message(message_text)
if trimmed_message.is_empty():
return
if multiplayer.is_server():
_broadcast_chat_message(multiplayer.get_unique_id(), trimmed_message)
else:
submit_chat_message.rpc_id(1, trimmed_message)
@rpc("any_peer", "reliable")
func submit_chat_message(message_text: String):
if not multiplayer.is_server():
return
var sender_id = multiplayer.get_remote_sender_id()
var trimmed_message = _sanitize_chat_message(message_text)
if trimmed_message.is_empty():
return
_broadcast_chat_message(sender_id, trimmed_message)
func _broadcast_chat_message(sender_id: int, message_text: String):
var player_info = Network.players.get(sender_id, {})
var nick = Network.sanitize_nickname(str(player_info.get("nick", "")), "Player_" + str(sender_id))
show_chat_message.rpc(nick, message_text)
@rpc("authority", "call_local", "reliable")
func show_chat_message(nick: String, msg: String):
multiplayer_chat.add_message(nick, msg)
func _sanitize_chat_message(message_text: String) -> String:
var clean = message_text.strip_edges()
if clean.length() > MAX_CHAT_MESSAGE_LENGTH:
clean = clean.substr(0, MAX_CHAT_MESSAGE_LENGTH)
return clean
func toggle_inventory():
if main_menu.is_menu_visible() or is_gameplay_input_blocked():
return
var local_player = _get_local_player()
if not local_player:
return
inventory_visible = !inventory_visible
if inventory_visible:
inventory_ui.open_inventory(local_player)
else:
inventory_ui.close_inventory()
_update_mouse_mode()
func is_inventory_visible() -> bool:
return inventory_visible
func _show_player_list() -> void:
if main_menu.is_menu_visible() or pause_menu.is_menu_visible() or not multiplayer.has_multiplayer_peer():
return
player_list_visible = true
player_list_ui.show_players(Network.players, multiplayer.get_unique_id())
func _hide_player_list() -> void:
player_list_visible = false
if player_list_ui:
player_list_ui.hide_players()
func _refresh_player_list() -> void:
if player_list_visible and player_list_ui:
player_list_ui.refresh_players(Network.players, multiplayer.get_unique_id())
func register_player_nickname_height(player_id: int, height: float) -> void:
if not multiplayer.is_server() or player_id <= 0:
return
var normalized_height := _normalize_nickname_height(height)
_store_and_apply_player_nickname_height(player_id, normalized_height)
sync_player_nickname_height.rpc(player_id, normalized_height)
@rpc("authority", "reliable")
func sync_player_nickname_height(player_id: int, height: float) -> void:
if multiplayer.get_remote_sender_id() != 1:
return
_store_and_apply_player_nickname_height(player_id, _normalize_nickname_height(height))
func _request_nickname_heights() -> void:
if multiplayer.is_server() or not multiplayer.has_multiplayer_peer():
return
request_nickname_heights.rpc_id(1)
@rpc("any_peer", "reliable")
func request_nickname_heights() -> void:
if not multiplayer.is_server():
return
var requester_id := multiplayer.get_remote_sender_id()
if requester_id <= 0 or _nickname_height_requesters.has(requester_id):
return
_nickname_height_requesters[requester_id] = true
_sync_nickname_heights_to_peer(requester_id)
func _sync_nickname_heights_to_peer(peer_id: int) -> void:
if not multiplayer.is_server() or peer_id <= 0:
return
for child in players_container.get_children():
var player := child as Character
if not player:
continue
var player_id := str(player.name).to_int()
var height := float(_player_nickname_heights.get(player_id, player.get_current_nickname_height()))
sync_player_nickname_height.rpc_id(peer_id, player_id, _normalize_nickname_height(height))
func _store_and_apply_player_nickname_height(player_id: int, height: float) -> void:
if player_id <= 0:
return
_player_nickname_heights[player_id] = height
_apply_player_nickname_height(player_id)
func _apply_player_nickname_height(player_id: int) -> void:
if not _player_nickname_heights.has(player_id):
return
var player := players_container.get_node_or_null(str(player_id)) as Character
if player:
player.apply_synced_nickname_height(float(_player_nickname_heights[player_id]))
func _normalize_nickname_height(height: float) -> float:
if is_nan(height) or is_inf(height):
return MIN_NICKNAME_HEIGHT
return clampf(height, MIN_NICKNAME_HEIGHT, MAX_NICKNAME_HEIGHT)
func _on_inventory_closed():
inventory_visible = false
_update_mouse_mode()
func is_gameplay_input_blocked() -> bool:
return pause_menu.is_menu_visible()
func is_camera_input_blocked() -> bool:
return is_gameplay_input_blocked() or inventory_visible or multiplayer_chat.is_chat_visible()
func _handle_pause_action() -> void:
if inventory_visible:
_close_inventory()
return
if multiplayer_chat.is_chat_visible():
_close_chat()
return
if pause_menu.is_menu_visible():
_hide_pause_menu()
return
if main_menu.is_menu_visible() or not multiplayer.has_multiplayer_peer():
return
_show_pause_menu()
func _show_pause_menu() -> void:
pause_menu.show_menu()
_update_mouse_mode()
func _hide_pause_menu(restore_mouse_mode: bool = true) -> void:
if not pause_menu.is_menu_visible():
return
pause_menu.hide_menu()
if restore_mouse_mode:
_update_mouse_mode()
func _close_chat() -> void:
multiplayer_chat.close_chat()
chat_visible = false
_update_mouse_mode()
func _close_inventory() -> void:
inventory_ui.close_inventory()
inventory_visible = false
_update_mouse_mode()
func _update_mouse_mode() -> void:
if DisplayServer.get_name() == "headless":
return
var ui_requires_cursor := (
main_menu.is_menu_visible()
or pause_menu.is_menu_visible()
or inventory_visible
or not multiplayer.has_multiplayer_peer()
)
if ui_requires_cursor:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
else:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _on_pause_resume_pressed() -> void:
_hide_pause_menu()
func _on_pause_main_menu_pressed() -> void:
_hide_pause_menu(false)
Network.leave_game()
if not main_menu.is_menu_visible():
_reset_session_ui()
func _on_pause_quit_pressed() -> void:
Network.leave_game()
get_tree().quit()
func update_local_inventory_display():
if inventory_ui:
inventory_ui.refresh_display()
func _get_local_player() -> Character:
var local_player_id = multiplayer.get_unique_id()
if players_container.has_node(str(local_player_id)):
return players_container.get_node(str(local_player_id)) as Character
return null
func _debug_add_item():
if not OS.is_debug_build() or not multiplayer.is_server():
return
var local_player = _get_local_player()
if local_player:
var test_items: Array[String] = []
for item_id in ItemDatabase.get_all_items():
test_items.append(str(item_id))
if test_items.is_empty():
return
var random_item = test_items[randi() % test_items.size()]
local_player.request_add_item.rpc_id(1, random_item, 1)
func _debug_print_inventory():
var local_player = _get_local_player()
if local_player and local_player.get_inventory():
var inventory = local_player.get_inventory()
print("=== Inventory Debug ===")
for i in range(inventory.slots.size()):
var slot = inventory.get_slot(i)
if slot and not slot.is_empty():
print("Slot ", i, ": ", slot.item_id, " x", slot.quantity)
print("=====================")
else:
print("No inventory found for local player")
+1
View File
@@ -0,0 +1 @@
uid://d0dgljwwl463n