liblast/Game/Main.gd

357 lines
11 KiB
GDScript

extends Node
enum GameFocus {MENU, GAME, CHAT, AWAY}
enum MultiplayerRole {NONE, CLIENT, SERVER, DEDICATED_SERVER, RELAY_SERVER}
const NET_PORT = 12597
#const NET_SERVER = "liblast.unfa.xyz"
const NET_SERVER = "localhost"
var peer = ENetMultiplayerPeer.new()
var role = MultiplayerRole.NONE:
set(new_role):
role = new_role
print("Multiplayer Role changed to ", MultiplayerRole.keys()[new_role])
var player_scene = preload("res://Assets/Characters/Player.tscn")
@onready var gui = $GUI
@onready var hud = $HUD
@onready var chat = hud.get_node("Chat")
var local_player: Node = null
class PlayerInfo:
var name: String
var team: int
var color: Color
var focus: GameFocus
var health: int
var score: int
func _init():#name: String, team: int, color: Color):
var player_name = ""
for i in range(0, 4):
player_name += ['a','b','c', 'd', 'e', 'f'][randi() % 5]
self.name = player_name
self.color = Color(randf(),randf(),randf())
self.team = 0
self.focus = 999
self.health = 100
self.score = 0
func serialize():
return {
'name': self.name,
'team': str(self.team),
'color': self.color.to_html(),
'focus': self.focus,
'health': self.health,
'score': self.score,
}
func set(name: String, team: int, color: Color, focus: int, health: int, score: int):
self.name = name
self.team = team
self.color = color
self.focus = focus
self.health = health
self.score = score
func deserialize(info):
self.name = info['name']
self.team = info['team'].to_int()
self.color = Color.html(info['color'])
self.focus = info['focus']
self.health = info['health']
self.score = info['score']
#func generate():
var uptime = 0 # seconds
const respawn_delay = 5 # seconds
var spawn_queue = {}
var game_score_limit = 15
func _process(delta):
uptime += delta
$Label.text = "player_list: \n"
for i in player_list.players.keys():
if player_list.players[i]:
$Label.text += str(i) + " = " + str(player_list.get(i).serialize()) + "\n"
else:
$Label.text += str(i) + " = ???"
# poll respawn queue
$Label.text += "\n\nspawn_queue: \n"
$Label.text += str(spawn_queue)
for i in spawn_queue.keys():
if spawn_queue[i] <= uptime:
var is_local = true if i == get_tree().multiplayer.get_unique_id() else false
create_player(i, is_local, true)
spawn_queue.erase(i)
class PlayerList:
var players = {}
func erase(pid):
players.erase(pid)
func set(pid: int, info: PlayerInfo):
# if info is PlayerInfo:
players[pid] = info
func get(pid: int) -> PlayerInfo:
return players[pid]
@onready var player_list = PlayerList.new()
var focus = GameFocus.MENU :
set(new_focus):
if local_player != null:
assert(local_player != null, "local_player is not null but it is null, WTF?!")
match new_focus:
0:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
gui.show()
if local_player:
local_player.input_active = false
1:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
gui.hide()
if local_player:
local_player.input_active = true
2:
if local_player:
local_player.input_active = false
3:
if local_player:
local_player.input_active = true
focus = new_focus
func _input(_event) -> void:
if Input.is_action_just_pressed("ui_cancel"):
if focus == GameFocus.GAME:
focus = GameFocus.MENU
elif focus == GameFocus.MENU:
focus = GameFocus.GAME
if Input.is_action_just_pressed("show_scoretab"):
hud.scoretab(true)
elif Input.is_action_just_released("show_scoretab"):
hud.scoretab(false)
func game_over():
pass
func check_game_win_condition():
for i in player_list.players.keys():
if player_list.players[i].score >= game_score_limit:
chat.rpc(&'chat_notification', "Player " + player_list.players[i].name + " has won this round!")
game_over()
func update_hud():
hud.get_node("ScoreRank").text = "SCORE: " + str(player_list.get( get_tree().multiplayer.get_unique_id() ).score)
var score = player_list.get( get_tree().multiplayer.get_unique_id() ).score
var scores = []
for i in player_list.players.values():
scores.append(i.score)
scores.sort()
scores.reverse()
var rank = scores.find(score) + 1
scores.remove(scores.find(score))
scores.sort()
scores.reverse()
var lead = score - scores[0]
hud.get_node("ScoreRank").text = "SCORE: " + str(score) + "\nRANK: "
if lead > 0:
hud.get_node("ScoreRank").text += str(rank) + "\nLEAD: " + str(lead)
else:
hud.get_node("ScoreRank").text += str(rank) + "\nGAP: " + str(-lead)
@rpc(any, reliable) func player_list_update(info, pid = get_tree().get_rpc_sender_id()):
var new_info = PlayerInfo.new()
new_info.deserialize(info)
if player_list.players.has(pid):
var old_name = player_list.get(pid).name
if old_name != new_info.name:
chat.chat_notification("Player [b]" + old_name + "[/b] changed name to [b]" + new_info.name + "[/b]")
if $Players.has_node(str(pid)):
var player = $Players.get_node(str(pid))
player.update_player(new_info)
player_list.set(pid, new_info) # server relays other PID's data
# update local HUD
update_hud()
func push_local_player_info(): #
var id = get_tree().multiplayer.get_unique_id()
rpc(&'player_list_update', player_list.get(id).serialize(), get_tree().multiplayer.get_unique_id())
@rpc func destroy_player(pid: int):
print("DESTROY_PLAYER called on ", get_tree().multiplayer.get_unique_id(), " by ", get_tree().multiplayer.get_remote_sender_id(), " regarding ", pid)
#assert($Players.has_node(str(pid)), "Attempting to destroy a player that does not exist")
var player_node
if $Players.has_node(str(pid)):
player_node = $Players.get_node(str(pid))
else:
print("Destroying a player node that's not there")
return
#assert(player_node != null, "Attempting to delete a player node that does not exist")
player_node.name = str(player_node.name) + "_dead" # avoids name collision when instancing another player scene
print("before free")
player_node.free()
print("after free")
spawn_queue[pid] = uptime + respawn_delay
func create_player(pid: int, is_local:= false, respawn:= false) -> void:
var new_player
new_player = player_scene.instantiate()
var spawnpoint = $Map/SpawnPoints.get_children()[randi() % len($Map/SpawnPoints.get_children())]
new_player.name = str(pid)
new_player.global_transform = spawnpoint.global_transform
new_player.set_multiplayer_authority(pid)
$Players.add_child(new_player)
var new_info: PlayerInfo
if not respawn: # first spawn
new_info = PlayerInfo.new() # generate name, color etc
else: # respawn
new_info = player_list.players[pid] # reuse previous name, color etc
new_info.health = 100 # give the respawned player full health
focus = GameFocus.GAME # make sure the player has no menu displayed when they respawn
player_list.set(pid, new_info)
# superfluous and harmful code that messes up player data:
# if get_tree().multiplayer.get_multiplayer_unique_id() != 1: # if we're not the server - update the server
# rpc_id(1, &'player_list_update', new_info.serialize(), pid) # send local player info to the server
# else: # if we're the server, update the clients
# rpc(&'player_list_update', new_info.serialize(), pid)
if is_local:
local_player = new_player #$Players.get_node(str(id))
# local_player.get_node("Head/Camera").current = true
$NetworkTesting/TextEdit.text = new_info.name
$NetworkTesting/ColorPickerButton.color = new_info.color
#local_player.rpc(&'set_color', new_info.color)
# elif local_player: # if there is a local player, make sure we keep using it's camera
# new_player.get_node("Head/Camera").current = false
# local_player.get_node("Head/Camera").current = true
#new_player.rpc(&'set_color', player_list.get(pid).color)
func start_dedicated_server(): # start server without creating a local player
role = MultiplayerRole.DEDICATED_SERVER
peer.create_server(NET_PORT, 16)
get_tree().multiplayer_peer = peer
#create_player(1, true)
func _on_Host_pressed(): # start server and create a local player
role = MultiplayerRole.SERVER
$NetworkTesting/Host.disabled = true
$NetworkTesting/Connect.disabled = true
peer.create_server(NET_PORT, 16)
get_tree().multiplayer.multiplayer_peer = peer
create_player(1, true)
focus = GameFocus.GAME
chat.chat_notification("Started server")
func _on_Connect_pressed():
$NetworkTesting/Host.disabled = true
$NetworkTesting/Connect.disabled = true
peer.create_client(NET_SERVER, NET_PORT)
get_tree().multiplayer.multiplayer_peer = peer
func _player_connected(pid) -> void:
print("player connected, id: ", pid)
create_player(pid, false)
if get_tree().multiplayer.is_server(): # if we're the server
for i in player_list.players.keys(): # send the player_list to the new client
#pass
rpc_id(pid, &'player_list_update', player_list.get(i).serialize(), i) # send local player info to the server
# if local_player:
# local_player.rpc(&"update_info")
#
# if role in [MultiplayerRole.SERVER, MultiplayerRole.DEDICATED_SERVER]:
# rpc_id(id, "player_list_update", player_list)
func _player_disconnected(pid) -> void:
print("player disconnected, id: ", pid)
if get_tree().multiplayer.is_server(): # if we're the server, broadcast that a player left
chat.rpc(&'chat_notification', "Player [b]" + player_list.get(pid).name + "[/b] left")
func _connected_ok() -> void:
print("connected to server")
chat.chat_notification("Connected to server")
var pid = get_tree().multiplayer.get_unique_id()
create_player(pid, true)
focus = GameFocus.GAME
role = MultiplayerRole.CLIENT
func _connected_fail() -> void:
print("connection to server failed")
chat.chat_notification("Connection failed")
func _server_disconnected() -> void:
print("server disconnected")
role = MultiplayerRole.NONE
chat.chat_notification("Server disconnected")
func _ready() -> void:
#peer.compression_mode = NetworkedMultiplayerENet.COMPRESS_ZSTD
print("Commandline arguments: ", OS.get_cmdline_args())
get_tree().multiplayer.connect("peer_connected", self._player_connected)
get_tree().multiplayer.connect("peer_disconnected", self._player_disconnected)
get_tree().multiplayer.connect("connected_to_server", self._connected_ok)
get_tree().multiplayer.connect("connection_failed", self._connected_fail)
get_tree().multiplayer.connect("server_disconnected", self._server_disconnected)
if OS.get_cmdline_args().has("dedicated_server"):
start_dedicated_server()
func _on_TextEdit_text_submitted(new_text):
player_list.players[get_tree().multiplayer.get_unique_id()].name = new_text
push_local_player_info()
#chat_announcement("Player " + old_name + " is now known as " + new_name)
func _on_ColorPickerButton_color_changed(color):
player_list.players[get_tree().multiplayer.get_unique_id()].color = color
push_local_player_info()
#local_player.rpc(&'set_color', color)
func _on_CheckButton_toggled(button_pressed):
AudioServer.set_bus_mute(0, button_pressed)
func _on_Button_pressed():
destroy_player(get_tree().multiplayer.get_unique_id())