Initial push

This commit is contained in:
N0ble 2025-07-21 20:38:11 -06:00
commit 3240f07946
335 changed files with 11248 additions and 0 deletions

246
Scripts/Chat/chat_menu.gd Normal file
View file

@ -0,0 +1,246 @@
class_name ChatMenu
extends Control
#region Onready Variables
@onready var scroll_container = $AspectRatioContainer/MarginContainer/VBoxContainer/ScrollContainer
@onready var command_text_edit = $AspectRatioContainer/MarginContainer/VBoxContainer/CommandScrollContainer/CommandTextEdit
@onready var message_container = $AspectRatioContainer/MarginContainer/VBoxContainer/ScrollContainer/MessageContainer
@onready var visible_timer = $VisibleTimer
#endregion
#region Variables
var opened : bool = false
var clear_command_text : bool = false
#endregion
#region Signals
signal chat_opened()
signal spawn_item(item_name : String, x : String, y : String, z : String)
signal lobby_move(x : String, z : String)
#endregion
#region Godot Functions
func _ready() -> void:
hide()
opened = false
command_text_edit.gui_input.connect(_command_text_edit_gui_input)
visible_timer.timeout.connect(hide)
func _process(_delta : float):
if clear_command_text == true:
command_text_edit.text = ""
clear_command_text = false
if opened == true:
if Input.is_action_just_pressed("pause"):
remove_focus()
visible_timer.start()
return
if Input.is_action_just_pressed("open_chat"):
visible_timer.stop()
chat_opened.emit()
show()
focus()
if Input.is_action_just_pressed("open_chat_as_command"):
visible_timer.stop()
command_text_edit.text = "/"
chat_opened.emit()
show()
focus()
command_text_edit.set_caret_column(1, false, 0)
#endregion
#region Public Functions
func focus():
mouse_filter = Control.MOUSE_FILTER_STOP
opened = true
Global.in_menu = true
command_text_edit.grab_focus()
func remove_focus():
mouse_filter = Control.MOUSE_FILTER_IGNORE
opened = false
command_text_edit.release_focus()
Global.in_menu = false
func send_message(message : String, keep_focus : bool, label_color : Color = Color(1, 1, 1, 1)):
visible_timer.stop()
show()
var message_label : Label = Label.new()
message_label.text = message
message_label.label_settings = LabelSettings.new()
message_label.label_settings.font_color = label_color
message_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
message_container.add_child(message_label)
if keep_focus == false:
remove_focus()
visible_timer.start()
get_tree().create_timer(0.01).timeout.connect(
func() -> void:
scroll_container.scroll_vertical = scroll_container.get_v_scroll_bar().max_value
)
#endregion
#region Signal Functions
func _command_text_edit_gui_input(event : InputEvent):
if event.is_action_pressed("chat_send"):
send_message(command_text_edit.text, false)
check_command(command_text_edit.text)
clear_command_text = true
#endregion
#region "Private" Functions
func check_command(command : String):
command.strip_edges(true, true)
if command.begins_with("/") == false:
return
if Global.is_admin == false:
send_message("Cannot run command without admin status", false)
return
command = command.lstrip("/")
# ~ SPAWN ~ #
if command.begins_with("spawn ") or command == "spawn":
command = command.lstrip("spawn ")
command = command.trim_prefix(" ")
#get the item name and check that it exists
var item_next_space : int = command.find(" ")
if item_next_space == -1:
send_message("Spawn Syntax error, spawn command looks like `spawn <entity_name> x y z`", false, Color(1, 0, 0, 1))
return
var item_name : String = command.substr(0, item_next_space)
command = command.lstrip(item_name)
command = command.trim_prefix(" ")
# if EntityResource.ids_by_name.has(item_name) == false:
# var entity_names = ""
# for key in EntityResource.data:
# if EntityResource.data[key].spawnable == false:
# continue
# entity_names += EntityResource.data[key].name + " "
# send_message("Entity: \"" + item_name + "\" was not found, cannot spawn. Entity List is: " + entity_names, false, Color(1, 0, 0, 1))
# return
#get x
item_next_space = command.find(" ")
if item_next_space == -1:
send_message("Spawn Syntax error, spawn command looks like `spawn <entity_name> x y z`", false, Color(1, 0, 0, 1))
return
var x_name : String = command.substr(0, item_next_space)
command = command.lstrip(x_name)
command = command.trim_prefix(" ")
if x_name != "~" and x_name.is_valid_int() == false:
send_message("Spawn Syntax error: x value was not ~ or int", false, Color(1, 0, 0, 1))
return
#get y
item_next_space = command.find(" ")
if item_next_space == -1:
send_message("Spawn Syntax error, spawn command looks like `spawn <entity_name> x y z`", false, Color(1, 0, 0, 1))
return
var y_name : String = command.substr(0, item_next_space)
command = command.lstrip(y_name)
command = command.trim_prefix(" ")
if y_name != "~" and y_name.is_valid_int() == false:
send_message("Spawn Syntax error: y value was not ~ or int", false, Color(1, 0, 0, 1))
return
#check z (which is what is left in command)
command = command.trim_prefix(" ")
command = command.trim_suffix(" ")
if command == "":
send_message("Spawn Syntax error, spawn command looks like `spawn <entity_name> x y z`", false, Color(1, 0, 0, 1))
if command != "~" and command.is_valid_int() == false:
send_message("Item Spawn Syntax error: z value was not ~ or int", false, Color(1, 0, 0, 1))
return
#call spawn item
spawn_item.emit(item_name, x_name, y_name, command)
return
# ~ LOBBY ~ #
if command.begins_with("lobby ") or command == "lobby":
command = command.lstrip("lobby ")
command = command.trim_prefix(" ")
#get the type name and check that it exists
var type_next_space : int = command.find(" ")
if type_next_space == -1:
send_message("Lobby Syntax error, lobby command looks like `lobby <action> [args]`", false, Color(1, 0, 0, 1))
return
# ~ move ~ #
if command.begins_with("move ") or command == "move":
command = command.lstrip("move ")
command = command.trim_prefix(" ")
#error check for if there is no position
if command == "":
send_message("Lobby Move Syntax error, lobby move command looks like `lobby move x z`", false, Color(1, 0, 0, 1))
return
#get x
var position_space : int = command.find(" ")
if position_space == -1:
send_message("Lobby Move Syntax error, lobby move command looks like `lobby move x z`", false, Color(1, 0, 0, 1))
return
var x_name : String = command.substr(0, position_space)
command = command.lstrip(x_name)
command = command.trim_prefix(" ")
if x_name != "~" and x_name.is_valid_int() == false:
send_message("Lobby Move Syntax error: x value was not ~ or int", false, Color(1, 0, 0, 1))
return
#check z (which is what is left in command)
command = command.trim_prefix(" ")
command = command.trim_suffix(" ")
if command == "":
send_message("Lobby Move Syntax error, lobby move command looks like `lobby move x z`", false, Color(1, 0, 0, 1))
if command != "~" and command.is_valid_int() == false:
send_message("Lobby Move Syntax error: z value was not ~ or int", false, Color(1, 0, 0, 1))
return
#call spawn item
lobby_move.emit(x_name, command)
return
#endregion

View file

@ -0,0 +1 @@
uid://c8k0usfm5rdm3

5
Scripts/Items/goblet.gd Normal file
View file

@ -0,0 +1,5 @@
extends RigidBody3D
@export var item : Item
@onready var interactable : Interactable = $Interactable
@onready var is_interactable : bool = true

View file

@ -0,0 +1 @@
uid://bntxwxn6ns5l0

View file

@ -0,0 +1,5 @@
extends Node3D
class_name Interactable
@onready var outline : MeshInstance3D = $outline

View file

@ -0,0 +1 @@
uid://bahmrqvs4pafg

60
Scripts/Level/level.gd Normal file
View file

@ -0,0 +1,60 @@
class_name Level
extends Node
var player_scene : PackedScene = preload("res://Scenes/Mobs/character.tscn")
@export var lobby : Lobby
var spawnid : int = 0
func _ready() -> void:
spawn_player.rpc_id(1, 1)
@rpc("any_peer", "call_local")
func request_spawn_player(id : int) -> void:
#only run on the multiplayer instance
if not multiplayer.is_server():
return
#don't spawn an already spawned player
if get_node_or_null("Player" + str(id)) != null:
return
spawn_player.rpc_id(1, id)
@rpc("any_peer", "call_local")
func spawn_player(id: int) -> void:
var new_player : Player = player_scene.instantiate()
new_player.name = "Player" + str(id)
print("Spawning ID: ", id)
add_child(new_player)
var spawn_point: Marker3D = lobby.spawnpoints[spawnid]
print(spawn_point.global_position)
new_player.teleport_player.rpc(spawn_point.global_position, spawn_point.rotation)
spawnid += 1
@rpc("authority", "call_local")
func remove_client_from_server(id : int) -> void:
var disconnected_player : Player = get_node("Player" + str(id))
spawnid = 1
remove_child.call_deferred(disconnected_player)
disconnected_player.queue_free()
@rpc("any_peer", "call_local")
func disconnect_from_server():
remove_players()
spawnid = 0
# reset map
print("Disconnect: ", multiplayer.get_unique_id())
request_spawn_player.rpc(1)
func remove_players(remove_host : bool = true) -> void:
for child in get_children():
if child is Player:
if !remove_host and child.name == "Player1":
continue
remove_child(child)
child.queue_free()

View file

@ -0,0 +1 @@
uid://bsyvwqveefopb

5
Scripts/Level/lobby.gd Normal file
View file

@ -0,0 +1,5 @@
class_name Lobby
extends Node3D
@onready var spawnpoints : Array[Marker3D] = [$spawnpoint1, $spawnpoint2, $spawnpoint3, $spawnpoint4]

View file

@ -0,0 +1 @@
uid://cdc5npqxn0eda

View file

@ -0,0 +1,33 @@
extends Node
class_name InventoryMenu
@onready var inventory : Inventory
@onready var item_list : VBoxContainer = $CanvasLayer/PanelContainer/VBoxContainer/MarginContainer/ScrollContainer/VBoxContainer
var item_scene : PackedScene = preload("res://Scenes/Menu/InventoryUiItem.tscn")
func _ready() -> void:
$CanvasLayer.hide()
pass
func toggle_inventory() -> bool:
var canvas = $CanvasLayer
if canvas.visible == true:
$CanvasLayer.hide()
return false
else:
update_inventory()
$CanvasLayer.show()
return true
func update_inventory():
for i in inventory.items:
var new_item = item_scene.instantiate()
# new_item.set_values(i.item.name, i.item.value, i.item.weight)
item_list.add_child(new_item)
new_item.set_values(i.item.name, 10, 10)
print(i.item.name)
print(i)
pass

View file

@ -0,0 +1 @@
uid://ddwvtegkiite7

View file

@ -0,0 +1,11 @@
extends Node
@onready var title : Label = $Name
@onready var value : Label = $Value
@onready var weight : Label = $Weight
func set_values(new_title : String, new_value : int, new_weight : int) -> void:
title.text = new_title
value.text = str(new_value)
weight.text = str(new_weight)

View file

@ -0,0 +1 @@
uid://bv3glh0xi771m

169
Scripts/Menu/lobby_menu.gd Normal file
View file

@ -0,0 +1,169 @@
class_name LobbyMenu
extends Node
#region Onready Variables
@onready var lobby_container : Container = $LobbyContainer
@onready var username : LineEdit = $VBoxContainer/Name
@onready var lobby_label : Label = $VBoxContainer/HBoxContainer/LobbyLabel
@onready var steam_lobby_id_button : Button = $VBoxContainer/HBoxContainer/SteamLobbyIdButton
@onready var disconnect_button : Button = $VBoxContainer/HBoxContainer/Disconnect
@onready var tab_container : TabContainer = $VBoxContainer/TabContainer
@onready var steam_tab : Control = $VBoxContainer/TabContainer/Steam
@onready var steam_address_entry : LineEdit = $VBoxContainer/TabContainer/Steam/PanelContainer/MarginContainer/VBoxContainer/Address
@onready var steam_host_button : Button = $VBoxContainer/TabContainer/Steam/PanelContainer/MarginContainer/VBoxContainer/Host
@onready var steam_join_button : Button = $VBoxContainer/TabContainer/Steam/PanelContainer/MarginContainer/VBoxContainer/Join
@onready var enet_address_entry : LineEdit = $VBoxContainer/TabContainer/ENet/PanelContainer/MarginContainer/VBoxContainer/Address
@onready var enet_host_toggle : CheckButton = $VBoxContainer/TabContainer/ENet/PanelContainer/MarginContainer/VBoxContainer/HostToggleContainer/Host
@onready var enet_join_button : Button = $VBoxContainer/TabContainer/ENet/PanelContainer/MarginContainer/VBoxContainer/Join
#endregion
#region Variables
var steam_lobby_id : String = ""
#endregion
#region Signals
signal disconnect_button_pressed()
signal steam_host_pressed(new_username : String)
signal steam_join_pressed(new_username : String)
signal enet_host_toggled_on(new_username : String)
signal enet_host_toggled_off()
signal enet_join_pressed(new_username : String)
#endregion
#region Godot Functions
func _ready() -> void:
#set username to steam username
username.text = "username"
disconnect_button.pressed.connect(_on_disconnet_button_pressed)
#steam
if Global.steam_connected == true:
username.text = Steam.getPersonaName()
steam_lobby_id_button.pressed.connect(_on_steam_lobby_id_button_pressed)
steam_host_button.pressed.connect(_on_steam_host_pressed)
steam_join_button.pressed.connect(_on_steam_join_pressed)
#enet
enet_host_toggle.toggled.connect(_on_enet_host_toggled)
enet_join_button.pressed.connect(_on_enet_join_pressed)
if Global.steam_connected == false:
tab_container.remove_child(steam_tab)
#endregion
#region Public Functions
func reset_visible() -> void:
steam_lobby_id_button.hide()
disconnect_button.hide()
clear_lobby_text()
username.show()
#steam
steam_join_button.show()
steam_address_entry.show()
steam_host_button.show()
#enet
tab_container.tabs_visible = true
tab_container.show()
enet_address_entry.show()
enet_join_button.show()
func request_lobby_list() -> void:
for child in lobby_container.get_children():
child.queue_free()
Steam.requestLobbyList()
func set_lobby_text(new_names : Array[String]) -> void:
clear_lobby_text()
for new_name in new_names:
var name_label : Label = Label.new()
name_label.text = new_name
lobby_container.add_child(name_label)
func clear_lobby_text() -> void:
var lobby_container_children = lobby_container.get_children()
for child in lobby_container_children:
child.free()
func update_steam_lobby_id_button():
steam_lobby_id_button.show()
steam_lobby_id = Global.steam_lobby_id
#endregion
#region Signal Functions
func _on_disconnet_button_pressed() -> void:
disconnect_button_pressed.emit()
func _on_steam_host_pressed() -> void:
steam_host_pressed.emit(username.text)
steam_join_button.hide()
tab_container.tabs_visible = false
steam_address_entry.hide()
steam_host_button.hide()
username.hide()
func _on_steam_join_pressed() -> void:
Global.steam_lobby_id = steam_address_entry.text
steam_join_pressed.emit(username.text)
steam_join_button.hide()
tab_container.tabs_visible = false
steam_address_entry.hide()
steam_host_button.hide()
username.hide()
disconnect_button.show()
func _on_enet_host_toggled(toggled_on : bool) -> void:
if toggled_on:
enet_address_entry.hide()
enet_join_button.hide()
username.hide()
enet_host_toggled_on.emit(username.text)
tab_container.tabs_visible = false
return
tab_container.tabs_visible = true
enet_host_toggled_off.emit()
enet_address_entry.show()
enet_join_button.show()
username.show()
func _on_enet_join_pressed() -> void:
if enet_address_entry.text.is_empty() == false:
Global.enet_address = enet_address_entry.text
tab_container.tabs_visible = false
enet_join_pressed.emit(username.text)
enet_join_button.hide()
tab_container.hide()
username.hide()
disconnect_button.show()
func _on_steam_lobby_id_button_pressed():
DisplayServer.clipboard_set(steam_lobby_id)
#endregion

View file

@ -0,0 +1 @@
uid://b2pee67ics25u

View file

@ -0,0 +1,15 @@
extends Resource
class_name Inventory
var items : Array[RigidBody3D]
func get_current_slot(index : int) -> RigidBody3D:
return items[index]
func add_item(item : RigidBody3D) -> void:
items.append(item)
func remove_items(item : RigidBody3D) -> void:
items.erase(item)

View file

@ -0,0 +1 @@
uid://darcj1vokaiv2

View file

@ -0,0 +1,9 @@
extends Resource
class_name Item
@export var value : int
@export var name : String
@export var weight : int
@export var primary_objective : bool
@export var secondary_objective : bool

View file

@ -0,0 +1 @@
uid://ctwdmnqaxkty

View file

@ -0,0 +1,12 @@
class_name PlayerData
extends Resource
@export var id : int
@export var lobby_index : int
@export var name : String
func _init(new_id : int = -1, new_lobby_index : int = -1, new_name : String = "username"):
id = new_id
lobby_index = new_lobby_index
name = new_name

View file

@ -0,0 +1 @@
uid://b8fojvchw16hl

273
Scripts/Server/server.gd Normal file
View file

@ -0,0 +1,273 @@
class_name Server
extends Node
#region Constants
const DEFAULT_PORT = 10567
const MAX_CLIENTS = 3
#endregion
#region Onready Variables
# @onready var microphone : AudioStreamPlayer3D = $Microphone
#endregion
#region Variables
var players_ready : Array = []
var peer : MultiplayerPeer = null
#var mic_capture : AudioEffectOpusChunked
var lobby_id : int = -1
#endregion
#region RPC
# @rpc("any_peer", "unreliable")
# func voice_packet_recieved(packet):
# var sender_id = multiplayer.get_remote_sender_id()
# push_opus_packet_to_player.emit(sender_id, packet)
#endregion
#region Signals
signal game_log(message : String, color : Color)
signal server_close()
signal client_connected_to_server()
signal client_disconnected_from_server()
signal remove_client_from_server(id : int)
signal steam_lobby_created()
# signal push_opus_packet_to_player(id : int, packet)
#endregion
#region Godot Functions
func _ready() -> void:
for argument in OS.get_cmdline_args():
if argument == "-steam":
ready_steam()
# ready_voip()
#set multiplayer signals
multiplayer.peer_connected.connect(_multiplayer_peer_connected)
multiplayer.peer_disconnected.connect(_multiplayer_peer_disconnected)
multiplayer.connected_to_server.connect(_multiplayer_connected_to_server)
multiplayer.connection_failed.connect(_multiplayer_connection_failed)
multiplayer.server_disconnected.connect(_multiplayer_server_disconnected)
#set steam signals
if Global.steam_connected == true:
Steam.lobby_joined.connect(_steam_lobby_joined)
Steam.lobby_created.connect(_steam_lobby_created)
func _process(_delta : float):
# process_voip()
Steam.run_callbacks()
#endregion
#region Ready Functions
func ready_steam() -> void:
var steam_response : Dictionary = Steam.steamInitEx()
Global.steam_connected = steam_response["status"] == 0
if Global.steam_connected == false:
get_tree().create_timer(3.0).timeout.connect(
func():
game_log.emit("Could Not Connect To Steam", Color(1, 0, 0, 1))
)
# func ready_voip() -> void:
# assert(microphone.bus == "MicrophoneBus")
# var mic_bus = AudioServer.get_bus_index("MicrophoneBus")
# mic_capture = AudioServer.get_bus_effect(mic_bus, 0)
#endregion
#region Process Functions
# func process_voip() -> void:
# if mic_capture == null:
# return
# while mic_capture.chunk_available():
# var packet = mic_capture.read_opus_packet(PackedByteArray())
# mic_capture.drop_chunk()
# if Global.voip_on == false:
# continue
# if Global.is_multiplayer and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
# voice_packet_recieved.rpc(packet)
#endregion
#region Default Multiplayer Functions
func destroy_lobby() -> void:
Global.is_multiplayer = false
multiplayer.multiplayer_peer.close()
peer = OfflineMultiplayerPeer.new()
multiplayer.multiplayer_peer = peer
server_close.emit()
func disconnect_client() -> void:
Global.is_multiplayer = false
multiplayer.multiplayer_peer.close()
peer = OfflineMultiplayerPeer.new()
multiplayer.multiplayer_peer = peer
client_disconnected_from_server.emit()
#endregion
#region Enet Functions
func enet_create_host() -> void:
#set multiplayer to true
Global.is_multiplayer = true
#init multiplayer host
peer = ENetMultiplayerPeer.new()
(peer as ENetMultiplayerPeer).create_server(DEFAULT_PORT, MAX_CLIENTS)
multiplayer.multiplayer_peer = peer
func enet_create_client(address : String) -> void:
#set multiplayer to true
Global.is_multiplayer = true
#init client and wait for connection
peer = ENetMultiplayerPeer.new()
(peer as ENetMultiplayerPeer).create_client(address, DEFAULT_PORT)
multiplayer.set_multiplayer_peer(peer)
await multiplayer.connected_to_server
#endregion
#region Steam Functions
func steam_create_host() -> void:
#players[1] = PlayerData.new(1, 0, new_username)
Steam.createLobby(Steam.LOBBY_TYPE_FRIENDS_ONLY, MAX_CLIENTS)
func steam_join_lobby(new_lobby_id : int) -> void:
Steam.joinLobby(new_lobby_id)
func steam_create_socket():
Global.is_multiplayer = true
peer = SteamMultiplayerPeer.new()
peer.create_host(0)
multiplayer.set_multiplayer_peer(peer)
func steam_connect_socket(steam_id : int) -> void:
Global.is_multiplayer = true
peer = SteamMultiplayerPeer.new()
peer.create_client(steam_id, 0)
multiplayer.set_multiplayer_peer(peer)
#endregion
#region Signal Functions
func _multiplayer_peer_connected(_id : int):
pass
func _multiplayer_peer_disconnected(id : int):
if multiplayer.is_server():
game_log.emit("Player \"" + str(id) + "\" disconnected", Color(1, 0, 0, 1))
remove_client_from_server.emit(id)
func _multiplayer_connected_to_server() -> void:
client_connected_to_server.emit()
func _multiplayer_connection_failed() -> void:
Global.is_multiplayer = false
peer = OfflineMultiplayerPeer.new()
multiplayer.multiplayer_peer = peer
client_disconnected_from_server.emit()
game_log.emit("Server connection failed", Color(1, 0, 0, 1))
func _multiplayer_server_disconnected() -> void:
Global.is_multiplayer = false
peer = OfflineMultiplayerPeer.new()
multiplayer.multiplayer_peer = peer
client_disconnected_from_server.emit()
game_log.emit("Server disconnected", Color(1, 0, 0, 1))
func _steam_lobby_joined(new_lobby_id : int, _permissions : int, _locked : bool, response : int) -> void:
if response == Steam.CHAT_ROOM_ENTER_RESPONSE_SUCCESS:
lobby_id = new_lobby_id
var id : int = Steam.getLobbyOwner(new_lobby_id)
if id != Steam.getSteamID():
steam_connect_socket(id)
#player_register.rpc(username)
#players[multiplayer.get_unique_id()] = username
return
var message : String
match response:
Steam.CHAT_ROOM_ENTER_RESPONSE_DOESNT_EXIST:
message = "This lobby no longer exists."
Steam.CHAT_ROOM_ENTER_RESPONSE_NOT_ALLOWED:
message = "You don't have permission to join this lobby."
Steam.CHAT_ROOM_ENTER_RESPONSE_FULL:
message = "The lobby is now full."
Steam.CHAT_ROOM_ENTER_RESPONSE_ERROR:
message = "Response errored."
Steam.CHAT_ROOM_ENTER_RESPONSE_BANNED:
message = "You are banned from this lobby."
Steam.CHAT_ROOM_ENTER_RESPONSE_LIMITED:
message = "You cannot join due to having a limited account."
Steam.CHAT_ROOM_ENTER_RESPONSE_CLAN_DISABLED:
message = "This lobby is locked or disabled."
Steam.CHAT_ROOM_ENTER_RESPONSE_COMMUNITY_BAN:
message = "This lobby is community locked."
Steam.CHAT_ROOM_ENTER_RESPONSE_MEMBER_BLOCKED_YOU:
message = "A user in the lobby has blocked you from joining."
Steam.CHAT_ROOM_ENTER_RESPONSE_YOU_BLOCKED_MEMBER:
message = "A user you have blocked is in the lobby."
game_log.emit(message, Color(1, 0, 0, 1))
func _steam_lobby_created(connected: int, this_lobby_id: int) -> void:
if connected == 1:
Global.steam_lobby_id = str(this_lobby_id)
Steam.setLobbyData(this_lobby_id, "name", str(Steam.getPersonaName(), "'s test server"))
steam_create_socket()
steam_lobby_created.emit()
return
game_log.emit("Error on create lobby!", Color(1, 0, 0, 1))
#endregion

View file

@ -0,0 +1 @@
uid://dwwtrox3lp2jp

View file

@ -0,0 +1,71 @@
class_name ServerLobby
extends Node
#region Exports
@export var players : Dictionary[int, PlayerData] = {}
#endregion
#region Variables
var free_lobby_indexs : Array[int] = []
#endregion
#region Signals
signal player_registered()
#endregion
#region Client RPC
@rpc("any_peer", "call_local")
func request_player_register(id: int, username) -> void:
#only run on the multiplayer instance
if not multiplayer.is_server():
return
player_register.rpc_id(1, id, username)
@rpc("any_peer", "call_local")
func update_client_players(id : int, username : String) -> void:
if not players.has(id):
players[id] = PlayerData.new(id, players.size(), username)
player_registered.emit()
#endregion
#region Server RPC
@rpc("authority", "call_local")
func player_register(id : int, username : String) -> void:
var lobby_index : int = players.size()
if free_lobby_indexs.size() != 0:
lobby_index = free_lobby_indexs.pop_front()
players[id] = PlayerData.new(id, lobby_index, username)
#send the entire list for clients that join later
for player_id in players:
update_client_players.rpc(player_id, players[player_id].name)
@rpc("authority", "call_local")
func player_unregister(id) -> void:
free_lobby_indexs.append(players[id].lobby_index)
players.erase(id)
player_registered.emit()
@rpc("authority", "call_local")
func players_clear() -> void:
players.clear()
player_registered.emit()
#endregion

View file

@ -0,0 +1 @@
uid://k0xvfdmvtyrt

View file

@ -0,0 +1,49 @@
@tool
extends Node
# This does not effect runtime yet but will in the future.
@export_category("Controller Editor Module")
@export_range(-360.0, 360.0, 0.01, "or_greater", "or_less") var head_y_rotation : float = 0.0:
set(new_rotation):
if HEAD:
head_y_rotation = new_rotation
HEAD.rotation.y = deg_to_rad(head_y_rotation)
update_configuration_warnings()
@export_range(-90.0, 90.0, 0.01, "or_greater", "or_less") var head_x_rotation : float = 0.0:
set(new_rotation):
if HEAD:
head_x_rotation = new_rotation
HEAD.rotation.x = deg_to_rad(head_x_rotation)
update_configuration_warnings()
@export_group("Nodes")
@export var CHARACTER : CharacterBody3D
@export var head_path : String = "Head" # Relative to the parent node
#@export var CAMERA : Camera3D
#@export var HEADBOB_ANIMATION : AnimationPlayer
#@export var JUMP_ANIMATION : AnimationPlayer
#@export var CROUCH_ANIMATION : AnimationPlayer
#@export var COLLISION_MESH : CollisionShape3D
@onready var HEAD = get_node("../" + head_path)
func _ready():
if !Engine.is_editor_hint():
#print("not editor")
HEAD.rotation.y = deg_to_rad(head_y_rotation)
HEAD.rotation.x = deg_to_rad(head_x_rotation)
func _get_configuration_warnings():
var warnings = []
if head_y_rotation > 360:
warnings.append("The head rotation is greater than 360")
if head_y_rotation < -360:
warnings.append("The head rotation is less than -360")
# Returning an empty array gives no warnings
return warnings

View file

@ -0,0 +1 @@
uid://c5g0jt1apb2al

551
Scripts/fpc/character.gd Normal file
View file

@ -0,0 +1,551 @@
# COPYRIGHT Colormatic Studios
# MIT license
# Quality Godot First Person Controller v2
class_name Player
extends CharacterBody3D
#region Character Export Group
## The settings for the character's movement and feel.
@export_category("Character")
## The speed that the character moves at without crouching or sprinting.
@export var base_speed : float = 3.0
## The speed that the character moves at when sprinting.
@export var sprint_speed : float = 6.0
## The speed that the character moves at when crouching.
@export var crouch_speed : float = 1.0
## How fast the character speeds up and slows down when Motion Smoothing is on.
@export var acceleration : float = 10.0
## How high the player jumps.
@export var jump_velocity : float = 4.5
## How far the player turns when the mouse is moved.
@export var mouse_sensitivity : float = 0.1
## Invert the X axis input for the camera.
@export var invert_camera_x_axis : bool = false
## Invert the Y axis input for the camera.
@export var invert_camera_y_axis : bool = false
## Whether the player can use movement inputs. Does not stop outside forces or jumping. See Jumping Enabled.
@export var immobile : bool = false
## The reticle file to import at runtime. By default are in res://addons/fpc/reticles/. Set to an empty string to remove.
@export_file var default_reticle = "res://Scenes/reticles/reticle_1.tscn"
#endregion
#region Nodes Export Group
@export_group("Nodes")
## A reference to the camera for use in the character script. This is the parent node to the camera and is rotated instead of the camera for mouse input.
@export var HEAD : Node3D
## A reference to the camera for use in the character script.
@export var CAMERA : Camera3D
# A reference to the Camera Ray Cast
@export var CAMERA_RAYCAST : RayCast3D
# current item player is looking at
@onready var HIGHLIGHTED_ITEM : RigidBody3D
# A reference to the inventory
@export var INVENTORY : Inventory
# A reference to the inventory menu
@onready var inventory_menu : InventoryMenu
## A reference to the headbob animation for use in the character script.
@export var HEADBOB_ANIMATION : AnimationPlayer
## A reference to the jump animation for use in the character script.
@export var JUMP_ANIMATION : AnimationPlayer
## A reference to the crouch animation for use in the character script.
@export var CROUCH_ANIMATION : AnimationPlayer
## A reference to the the player's collision shape for use in the character script.
@export var COLLISION_MESH : CollisionShape3D
#endregion
#region Controls Export Group
# We are using UI controls because they are built into Godot Engine so they can be used right away
@export_group("Controls")
## Use the Input Map to map a mouse/keyboard input to an action and add a reference to it to this dictionary to be used in the script.
@export var controls : Dictionary = {
LEFT = "move_left",
RIGHT = "move_right",
FORWARD = "move_forward",
BACKWARD = "move_back",
JUMP = "jump",
CROUCH = "crouch",
SPRINT = "sprint",
PAUSE = "pause",
INTERACT = "interact",
INVENTORY = "inventory"
}
@export_subgroup("Controller Specific")
## This only affects how the camera is handled, the rest should be covered by adding controller inputs to the existing actions in the Input Map.
@export var controller_support : bool = false
## Use the Input Map to map a controller input to an action and add a reference to it to this dictionary to be used in the script.
@export var controller_controls : Dictionary = {
LOOK_LEFT = "look_left",
LOOK_RIGHT = "look_right",
LOOK_UP = "look_up",
LOOK_DOWN = "look_down"
}
## The sensitivity of the analog stick that controls camera rotation. Lower is less sensitive and higher is more sensitive.
@export_range(0.001, 1, 0.001) var look_sensitivity : float = 0.035
#endregion
#region Feature Settings Export Group
@export_group("Feature Settings")
## Enable or disable jumping. Useful for restrictive storytelling environments.
@export var jumping_enabled : bool = true
## Whether the player can move in the air or not.
@export var in_air_momentum : bool = true
## Smooths the feel of walking.
@export var motion_smoothing : bool = true
## Enables or disables sprinting.
@export var sprint_enabled : bool = true
## Toggles the sprinting state when button is pressed or requires the player to hold the button down to remain sprinting.
@export_enum("Hold to Sprint", "Toggle Sprint") var sprint_mode : int = 0
## Enables or disables crouching.
@export var crouch_enabled : bool = true
## Toggles the crouch state when button is pressed or requires the player to hold the button down to remain crouched.
@export_enum("Hold to Crouch", "Toggle Crouch") var crouch_mode : int = 0
## Wether sprinting should effect FOV.
@export var dynamic_fov : bool = true
## If the player holds down the jump button, should the player keep hopping.
@export var continuous_jumping : bool = true
## Enables the view bobbing animation.
@export var view_bobbing : bool = true
## Enables an immersive animation when the player jumps and hits the ground.
@export var jump_animation : bool = true
## This determines wether the player can use the pause button, not wether the game will actually pause.
@export var pausing_enabled : bool = true
## Use with caution.
@export var gravity_enabled : bool = true
## If your game changes the gravity value during gameplay, check this property to allow the player to experience the change in gravity.
@export var dynamic_gravity : bool = false
#endregion
#region Member Variable Initialization
# These are variables used in this script that don't need to be exposed in the editor.
var speed : float = base_speed
var current_speed : float = 0.0
# States: normal, crouching, sprinting
var state : String = "normal"
var low_ceiling : bool = false # This is for when the ceiling is too low and the player needs to crouch.
var was_on_floor : bool = true # Was the player on the floor last frame (for landing animation)
# The reticle should always have a Control node as the root
var RETICLE : Control
# Get the gravity from the project settings to be synced with RigidBody nodes
var gravity : float = ProjectSettings.get_setting("physics/3d/default_gravity") # Don't set this as a const, see the gravity section in _physics_process
# Stores mouse input for rotating the camera in the physics process
var mouseInput : Vector2 = Vector2(0,0)
#endregion
#region Godot Functions
func _enter_tree() -> void:
#get the id off the player name and set authority
var new_id = name.lstrip("Player")
set_multiplayer_authority(new_id.to_int())
func _ready():
#It is safe to comment this line if your game doesn't start with the mouse captured
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
# If the controller is rotated in a certain direction for game design purposes, redirect this rotation into the head.
HEAD.rotation.y = rotation.y
rotation.y = 0
if default_reticle:
change_reticle(default_reticle)
initialize_animations()
check_controls()
enter_normal_state()
if is_multiplayer_authority():
CAMERA.current = true
inventory_menu = $UserInterface/InventoryMenu
inventory_menu.inventory = INVENTORY
func _process(_delta):
# if pausing_enabled:
# handle_pausing()
update_debug_menu_per_frame()
handle_open_inventory()
func _physics_process(delta): # Most things happen here.
# Gravity
if dynamic_gravity:
gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
if not is_on_floor() and gravity and gravity_enabled:
velocity.y -= gravity * delta
handle_jumping()
var input_dir = Vector2.ZERO
if not immobile && !Global.in_menu: # Immobility works by interrupting user input, so other forces can still be applied to the player
input_dir = Input.get_vector(controls.LEFT, controls.RIGHT, controls.FORWARD, controls.BACKWARD)
handle_movement(delta, input_dir)
handle_head_rotation()
# The player is not able to stand up if the ceiling is too low
low_ceiling = $CrouchCeilingDetection.is_colliding()
handle_state(input_dir)
if dynamic_fov: # This may be changed to an AnimationPlayer
update_camera_fov()
if view_bobbing:
play_headbob_animation(input_dir)
if jump_animation:
play_jump_animation()
handle_interact()
update_debug_menu_per_tick()
was_on_floor = is_on_floor() # This must always be at the end of physics_process
#endregion
#region Server Functions
@rpc("any_peer", "call_local")
func teleport_player(location: Vector3, new_rotation: Vector3) -> void:
if not is_multiplayer_authority():
return
global_position = location
HEAD.rotation = new_rotation
velocity = Vector3.ZERO
#endregion
#region Input Handling
func handle_jumping():
if jumping_enabled:
if continuous_jumping: # Hold down the jump button
if Input.is_action_pressed(controls.JUMP) and is_on_floor() and !low_ceiling and !Global.in_menu:
if jump_animation:
JUMP_ANIMATION.play("jump", 0.25)
velocity.y += jump_velocity # Adding instead of setting so jumping on slopes works properly
else:
if Input.is_action_just_pressed(controls.JUMP) and is_on_floor() and !low_ceiling and !Global.in_menu:
if jump_animation:
JUMP_ANIMATION.play("jump", 0.25)
velocity.y += jump_velocity
func handle_movement(delta, input_dir):
var direction = input_dir.rotated(-HEAD.rotation.y)
direction = Vector3(direction.x, 0, direction.y)
move_and_slide()
if in_air_momentum:
if is_on_floor():
if motion_smoothing:
velocity.x = lerp(velocity.x, direction.x * speed, acceleration * delta)
velocity.z = lerp(velocity.z, direction.z * speed, acceleration * delta)
else:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
else:
if motion_smoothing:
velocity.x = lerp(velocity.x, direction.x * speed, acceleration * delta)
velocity.z = lerp(velocity.z, direction.z * speed, acceleration * delta)
else:
velocity.x = direction.x * speed
velocity.z = direction.z * speed
func handle_head_rotation():
if invert_camera_x_axis:
HEAD.rotation_degrees.y -= mouseInput.x * mouse_sensitivity * -1
else:
HEAD.rotation_degrees.y -= mouseInput.x * mouse_sensitivity
if invert_camera_y_axis:
HEAD.rotation_degrees.x -= mouseInput.y * mouse_sensitivity * -1
else:
HEAD.rotation_degrees.x -= mouseInput.y * mouse_sensitivity
if controller_support:
var controller_view_rotation = Input.get_vector(controller_controls.LOOK_DOWN, controller_controls.LOOK_UP, controller_controls.LOOK_RIGHT, controller_controls.LOOK_LEFT) * look_sensitivity # These are inverted because of the nature of 3D rotation.
if invert_camera_x_axis:
HEAD.rotation.x += controller_view_rotation.x * -1
else:
HEAD.rotation.x += controller_view_rotation.x
if invert_camera_y_axis:
HEAD.rotation.y += controller_view_rotation.y * -1
else:
HEAD.rotation.y += controller_view_rotation.y
mouseInput = Vector2(0,0)
HEAD.rotation.x = clamp(HEAD.rotation.x, deg_to_rad(-90), deg_to_rad(90))
func handle_interact():
if CAMERA_RAYCAST.is_colliding():
var interactable = CAMERA_RAYCAST.get_collider()
if "interactable" in interactable:
HIGHLIGHTED_ITEM = interactable
interactable.interactable.outline.show()
if "item" in interactable:
if Input.is_action_just_pressed(controls.INTERACT):
print(CAMERA_RAYCAST.get_collider())
var item = interactable as RigidBody3D
item.freeze = true
item.collision_layer = 0
item.hide()
INVENTORY.add_item(item)
print("This is a item")
elif HIGHLIGHTED_ITEM != null:
HIGHLIGHTED_ITEM.get_node("Interactable").outline.hide()
HIGHLIGHTED_ITEM = null
func check_controls(): # If you add a control, you might want to add a check for it here.
# The actions are being disabled so the engine doesn't halt the entire project in debug mode
if !InputMap.has_action(controls.JUMP):
push_error("No control mapped for jumping. Please add an input map control. Disabling jump.")
jumping_enabled = false
if !InputMap.has_action(controls.LEFT):
push_error("No control mapped for move left. Please add an input map control. Disabling movement.")
immobile = true
if !InputMap.has_action(controls.RIGHT):
push_error("No control mapped for move right. Please add an input map control. Disabling movement.")
immobile = true
if !InputMap.has_action(controls.FORWARD):
push_error("No control mapped for move forward. Please add an input map control. Disabling movement.")
immobile = true
if !InputMap.has_action(controls.BACKWARD):
push_error("No control mapped for move backward. Please add an input map control. Disabling movement.")
immobile = true
if !InputMap.has_action(controls.PAUSE):
push_error("No control mapped for pause. Please add an input map control. Disabling pausing.")
pausing_enabled = false
if !InputMap.has_action(controls.CROUCH):
push_error("No control mapped for crouch. Please add an input map control. Disabling crouching.")
crouch_enabled = false
if !InputMap.has_action(controls.SPRINT):
push_error("No control mapped for sprint. Please add an input map control. Disabling sprinting.")
sprint_enabled = false
func handle_open_inventory(force_toggle : bool = false):
if Input.is_action_just_pressed(controls.INVENTORY) || force_toggle:
Global.inventory_showing = inventory_menu.toggle_inventory()
match Input.mouse_mode:
Input.MOUSE_MODE_CAPTURED:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
#get_tree().paused = false
Input.MOUSE_MODE_VISIBLE:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
#get_tree().paused = false
#endregion
#region State Handling
func handle_state(moving):
if sprint_enabled:
if sprint_mode == 0:
if Input.is_action_pressed(controls.SPRINT) and state != "crouching":
if moving:
if state != "sprinting":
enter_sprint_state()
else:
if state == "sprinting":
enter_normal_state()
elif state == "sprinting":
enter_normal_state()
elif sprint_mode == 1:
if moving:
# If the player is holding sprint before moving, handle that scenario
if Input.is_action_pressed(controls.SPRINT) and state == "normal":
enter_sprint_state()
if Input.is_action_just_pressed(controls.SPRINT):
match state:
"normal":
enter_sprint_state()
"sprinting":
enter_normal_state()
elif state == "sprinting":
enter_normal_state()
if crouch_enabled:
if crouch_mode == 0:
if Input.is_action_pressed(controls.CROUCH) and state != "sprinting":
if state != "crouching":
enter_crouch_state()
elif state == "crouching" and !$CrouchCeilingDetection.is_colliding():
enter_normal_state()
elif crouch_mode == 1:
if Input.is_action_just_pressed(controls.CROUCH):
match state:
"normal":
enter_crouch_state()
"crouching":
if !$CrouchCeilingDetection.is_colliding():
enter_normal_state()
# Any enter state function should only be called once when you want to enter that state, not every frame.
func enter_normal_state():
#print("entering normal state")
var prev_state = state
if prev_state == "crouching":
CROUCH_ANIMATION.play_backwards("crouch")
state = "normal"
speed = base_speed
func enter_crouch_state():
#print("entering crouch state")
state = "crouching"
speed = crouch_speed
CROUCH_ANIMATION.play("crouch")
func enter_sprint_state():
#print("entering sprint state")
var prev_state = state
if prev_state == "crouching":
CROUCH_ANIMATION.play_backwards("crouch")
state = "sprinting"
speed = sprint_speed
#endregion
#region Animation Handling
func initialize_animations():
# Reset the camera position
# If you want to change the default head height, change these animations.
HEADBOB_ANIMATION.play("RESET")
JUMP_ANIMATION.play("RESET")
CROUCH_ANIMATION.play("RESET")
func play_headbob_animation(moving):
if moving and is_on_floor():
var use_headbob_animation : String
match state:
"normal","crouching":
use_headbob_animation = "walk"
"sprinting":
use_headbob_animation = "sprint"
var was_playing : bool = false
if HEADBOB_ANIMATION.current_animation == use_headbob_animation:
was_playing = true
HEADBOB_ANIMATION.play(use_headbob_animation, 0.25)
HEADBOB_ANIMATION.speed_scale = (current_speed / base_speed) * 1.75
if !was_playing:
HEADBOB_ANIMATION.seek(float(randi() % 2)) # Randomize the initial headbob direction
# Let me explain that piece of code because it looks like it does the opposite of what it actually does.
# The headbob animation has two starting positions. One is at 0 and the other is at 1.
# randi() % 2 returns either 0 or 1, and so the animation randomly starts at one of the starting positions.
# This code is extremely performant but it makes no sense.
else:
if HEADBOB_ANIMATION.current_animation == "sprint" or HEADBOB_ANIMATION.current_animation == "walk":
HEADBOB_ANIMATION.speed_scale = 1
HEADBOB_ANIMATION.play("RESET", 1)
func play_jump_animation():
if !was_on_floor and is_on_floor(): # The player just landed
var facing_direction : Vector3 = CAMERA.get_global_transform().basis.x
var facing_direction_2D : Vector2 = Vector2(facing_direction.x, facing_direction.z).normalized()
var velocity_2D : Vector2 = Vector2(velocity.x, velocity.z).normalized()
# Compares velocity direction against the camera direction (via dot product) to determine which landing animation to play.
var side_landed : int = round(velocity_2D.dot(facing_direction_2D))
if side_landed > 0:
JUMP_ANIMATION.play("land_right", 0.25)
elif side_landed < 0:
JUMP_ANIMATION.play("land_left", 0.25)
else:
JUMP_ANIMATION.play("land_center", 0.25)
#endregion
#region Debug Menu
func update_debug_menu_per_frame():
$UserInterface/DebugPanel.add_property("FPS", Performance.get_monitor(Performance.TIME_FPS), 0)
var status : String = state
if !is_on_floor():
status += " in the air"
$UserInterface/DebugPanel.add_property("State", status, 4)
func update_debug_menu_per_tick():
# Big thanks to github.com/LorenzoAncora for the concept of the improved debug values
current_speed = Vector3.ZERO.distance_to(get_real_velocity())
$UserInterface/DebugPanel.add_property("Speed", snappedf(current_speed, 0.001), 1)
$UserInterface/DebugPanel.add_property("Target speed", speed, 2)
var cv : Vector3 = get_real_velocity()
var vd : Array[float] = [
snappedf(cv.x, 0.001),
snappedf(cv.y, 0.001),
snappedf(cv.z, 0.001)
]
var readable_velocity : String = "X: " + str(vd[0]) + " Y: " + str(vd[1]) + " Z: " + str(vd[2])
$UserInterface/DebugPanel.add_property("Velocity", readable_velocity, 3)
func _unhandled_input(event : InputEvent):
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
mouseInput.x += event.relative.x
mouseInput.y += event.relative.y
# Toggle debug menu
elif event is InputEventKey:
if event.is_released():
# Where we're going, we don't need InputMap
if event.keycode == 4194338: # F7
$UserInterface/DebugPanel.visible = !$UserInterface/DebugPanel.visible
#endregion
#region Misc Functions
func change_reticle(reticle): # Yup, this function is kinda strange
if not is_multiplayer_authority():
return
if RETICLE:
RETICLE.queue_free()
RETICLE = load(reticle).instantiate()
RETICLE.character = self
$UserInterface.add_child(RETICLE)
func update_camera_fov():
if state == "sprinting":
CAMERA.fov = lerp(CAMERA.fov, 85.0, 0.3)
else:
CAMERA.fov = lerp(CAMERA.fov, 75.0, 0.3)
func handle_pausing():
if Global.inventory_showing:
handle_open_inventory()
if Input.is_action_just_pressed(controls.PAUSE):
# You may want another node to handle pausing, because this player may get paused too.
match Input.mouse_mode:
Input.MOUSE_MODE_CAPTURED:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
#get_tree().paused = false
Input.MOUSE_MODE_VISIBLE:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
#get_tree().paused = false
#endregion

View file

@ -0,0 +1 @@
uid://c1um4tmthbbpk

18
Scripts/fpc/debug.gd Normal file
View file

@ -0,0 +1,18 @@
extends PanelContainer
func _process(_delta):
if visible:
pass
func add_property(title : String, value, order : int): # This can either be called once for a static property or called every frame for a dynamic property
var target
target = $MarginContainer/VBoxContainer.find_child(title, true, false) # I have no idea what true and false does here, the function should be more specific
if !target:
target = Label.new() # Debug lines are of type Label
$MarginContainer/VBoxContainer.add_child(target)
target.name = title
target.text = title + ": " + str(value)
elif visible:
target.text = title + ": " + str(value)
$MarginContainer/VBoxContainer.move_child(target, order)

1
Scripts/fpc/debug.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://bhfftu01dsfk7

29
Scripts/global.gd Normal file
View file

@ -0,0 +1,29 @@
extends Node
#region Constants
const DEFAULT_ENET_ADDRESS = "127.0.0.1"
#endregion
#region Variables
var is_admin : bool = true
var is_multiplayer : bool = false
var steam_connected : bool = false
var in_game : bool = false
var in_menu : bool = false
var inventory_showing : bool = false
var enet_address : String = DEFAULT_ENET_ADDRESS
var steam_lobby_id : String = ""
var controller_connected : bool = false
var voip_on : bool = false
#endregion

1
Scripts/global.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://bma0n1wnb0yxd

86
Scripts/world.gd Normal file
View file

@ -0,0 +1,86 @@
extends Node
@export var lobby_menu : LobbyMenu
var menu_showing : bool = false;
@export var server : Server
@export var level: Level
@onready var chat : ChatMenu = $ChatMenu
func _ready() -> void:
lobby_menu.enet_host_toggled_on.connect(_enet_host_on)
lobby_menu.enet_join_pressed.connect(_enet_join_pressed)
lobby_menu.disconnect_button_pressed.connect(_disconnect_button_pressed)
lobby_menu.enet_host_toggled_off.connect(_menu_toggle_host_off)
server.client_connected_to_server.connect(_client_joined_server)
server.remove_client_from_server.connect(_remove_client_from_server)
server.client_disconnected_from_server.connect(_disconnect_from_server)
server.server_close.connect(_server_close)
server.game_log.connect(_server_send_chat_message)
func _physics_process(_delta: float) -> void:
pause_toggle()
func pause_toggle():
if Input.is_action_just_pressed("pause"):
if Global.inventory_showing:
var player_id : int = multiplayer.get_unique_id()
var player : Player = level.get_node("Player"+str(player_id))
player.handle_open_inventory(true)
return
if !menu_showing:
lobby_menu.show()
menu_showing = true
Global.in_menu = true
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
elif menu_showing:
lobby_menu.hide()
menu_showing = false
Global.in_menu = false
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _enet_host_on(username: String) -> void:
print("Server started, Host: " + username);
server.enet_create_host()
func _enet_join_pressed(_username: String) -> void:
print("... Joining " + Global.enet_address)
var playername : Player = level.get_node("Player1")
level.remove_child(playername)
server.enet_create_client(Global.enet_address)
func _client_joined_server() -> void:
var player_id : int = multiplayer.get_unique_id()
level.request_spawn_player.rpc(player_id)
lobby_menu.hide()
menu_showing = false
Global.in_menu = false
func _menu_toggle_host_off() -> void:
server.destroy_lobby()
lobby_menu.reset_visible()
func _server_close() -> void:
level.remove_players(false)
lobby_menu.reset_visible()
func _disconnect_button_pressed():
server.disconnect_client()
lobby_menu.reset_visible()
func _remove_client_from_server(id : int) -> void:
level.remove_client_from_server.rpc_id(1, id)
func _disconnect_from_server():
level.disconnect_from_server()
func _server_send_chat_message(message : String, color : Color) -> void:
chat.send_message(message, false, color)

1
Scripts/world.gd.uid Normal file
View file

@ -0,0 +1 @@
uid://klp20wju1i26