This commit is contained in:
2026-01-21 23:40:20 +01:00
commit d1f8068081
478 changed files with 24902 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
extends Timer
class_name ButtonLogicImpulse
@export var interactBox: InteractBox
signal onPressed
func _ready() -> void:
interactBox.interactedWith.connect(onInteraction)
one_shot = true
func onInteraction(_playerReference: PlayerCharacter) -> void:
if is_stopped():
onPressed.emit()
start()

View File

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

View File

@@ -0,0 +1,12 @@
extends Node
class_name ButtonLogicToggle
@export var interactBox: InteractBox
@export var state: bool = false #Is it on or off? :) hope this helps it is currently 1:42 am
func _ready() -> void:
interactBox.interactedWith.connect(onInteraction)
func onInteraction(_playerReference: PlayerCharacter) -> void:
state = !state

View File

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

42
script/MapLogic.gd Normal file
View File

@@ -0,0 +1,42 @@
extends Node3D
class_name MapLogic
signal onCollision
signal missionLost
@export var debugObj: PackedScene
@export var pathPivot: Node3D
@export var drawDebugCubesPathfinding:= false
@export var pathfindingGridLoader: PathfindingGridLoader
var astar : AStar2D
@export var playerStartPos: Vector3 = Vector3(0,0,0)
func _ready() -> void:
if pathfindingGridLoader: pathfindingGridLoader.loadGrid()
onCollision.emit() #TODO: REMOVE THIS LATER ITS ONLY HERE TO STOP A WARNING
func looseMission() -> void:
missionLost.emit()
func finishAstarSetup() -> void: ##Run this in process or physics process of the child map class
if pathfindingGridLoader:
if pathfindingGridLoader.loading_done:
astar = pathfindingGridLoader.astar
print(astar.get_point_count())
pathfindingGridLoader.queue_free()
if drawDebugCubesPathfinding: #Only for debugging
for point in astar.get_point_ids():
var point_position: Vector2 = astar.get_point_position(point)
addObject(debugObj,pathPivot,Vector3(point_position.x,0.2,point_position.y))
func addObject(AddedObject:PackedScene, Parent: Node, Position: Vector3, Rotation: Vector3= Vector3(0,0,0)):
var obj = AddedObject.instantiate()
Parent.add_child(obj)
obj.position = Position
obj.rotation = Rotation
return obj

1
script/MapLogic.gd.uid Normal file
View File

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

12
script/MissionTask.gd Normal file
View File

@@ -0,0 +1,12 @@
extends Object
class_name MissionTask
var taskName: String = ""
var description: String = "?"
var completed: bool = false
var taskLabel: Label
func _init(nameOfTask: String, descriptionOfTask: String = "?") -> void:
taskName = nameOfTask
description = descriptionOfTask

View File

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

116
script/ShipLogic.gd Normal file
View File

@@ -0,0 +1,116 @@
extends MapLogic
class_name ShipLogic
var main: Main
var generatorBiomeExists: bool = true
var levelGenSeed: int = 0
var lobbyScene: String = "res://Maps/Lobby/Lobby.tscn"
var roomList: Array[BasicRoom]
var generatorRoomList: Array[BasicRoom]
var hallwayLights: Array[OmniLight3D]
var power: bool = false
var hullBreached: bool = false
var finishingMission: bool = false
const HULL_BREACHED_CONDITION_DROP_SPEED: float = 0.8
var shipCondition: float = 100.0
var shipFuel: float = 100.0
var taskDict: Dictionary = {0: MissionTask.new("FixGenerator","Get the Generator back running.")}
@export var levelGenerator: LevelGenerator
#@export var controllRoom: ControlRoom
#@export var breakableRoom: CorridorDeadEndBreakable
func _ready() -> void:
main = get_node("/root/Main")
levelGenerator.generate(levelGenSeed)
main.mapScreen.addTask(taskDict[0])
func _process(_delta: float) -> void:
finishAstarSetup()
if Multiplayer.alivePlayerDict.size() == 0:
looseMission()
if taskDict[0].completed:
power = true
updateLights()
#if hullBreached:
#shipCondition -= HULL_BREACHED_CONDITION_DROP_SPEED * delta
#updateShipConditionScreen()
func finishMission() -> void:
if finishingMission: return
finishingMission = true
#Might be temp solution
var missionSucces: bool = true
for key in taskDict:
if !taskDict[key].completed:
missionSucces = false
break
if missionSucces:
main.missionEndScreen.label.text = "Success!"
else:
main.missionEndScreen.label.text = "Failure!"
main.displayMissionEndScreen = true
await get_tree().create_timer(8).timeout
main.displayMissionEndScreen = false
main.changeMenu(main.hud)
main.changeMap.rpc(lobbyScene)
func setSeed(mapSeed: int) -> void:
levelGenSeed = mapSeed
func addRoomToRoomList(room: BasicRoom) -> void:
roomList.push_back(room)
if room.biomeName == "generator":
generatorRoomList.push_back(room)
func updateLights() -> void:
for room in roomList:
room.showLights(power)
for light in hallwayLights:
light.visible = power
#Julian was here
func solveTask(id: int) -> void:
taskDict[id].completed = true
main.mapScreen.completeTask(taskDict[id])
##TODO
#func unSolveTask(id: int) -> void:
#taskDict[id].completed = false
#main.mapScreen.completeTask(taskDict[id])
func onLevelGenFinished() -> void:
if generatorBiomeExists:
var mainRoom: Generator = generatorRoomList[0]
for n in generatorRoomList.size()-1:
mainRoom.cellSpawners.append_array(generatorRoomList[n+1].powerCellSpawners)
mainRoom.wireSpawners.append_array(generatorRoomList[n+1].wireSpawners)
mainRoom.Ready()
#func collisionWithAstroid():
#onCollision.emit()
#hullBreached = true
#shipCondition -= 10
#if breakableRoom:
#breakableRoom.updateWallVisibility(false)
#
#func updateShipConditionScreen():
#if controllRoom:
#controllRoom.shipConditionDisplayNumber = clamp(shipCondition + 1,0,100)

1
script/ShipLogic.gd.uid Normal file
View File

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

180
script/audioManager.gd Normal file
View File

@@ -0,0 +1,180 @@
extends Node
var current_sample_rate: int = 48000
var has_loopback: bool = false
var max_buffer_size: int = 1000
var voicechat_player_path : NodePath = ^"VoiceChat"
var sample_rate_min : int = 11025
var sample_rate_max : int = 48000
var local_playback: AudioStreamGeneratorPlayback = null
var local_voice_buffer: PackedByteArray = PackedByteArray()
var network_playback: Dictionary[int, AudioStreamGeneratorPlayback] = {}
var network_voice_buffer: Dictionary[int, PackedByteArray] = {}
@onready var loopback_player : AudioStreamPlayer= $/root/Main/VoiceLoopback
func _ready() -> void:
if not Steamworks.is_initiallized:
push_warning("[VoiceChat] VoiceChat disabled due to SteamAPI.")
return
if has_loopback:
print("[VoiceChat] Loopback on")
record_voice(true)
loopback_player.stream.mix_rate = current_sample_rate
loopback_player.play()
local_playback = loopback_player.get_stream_playback()
multiplayer.peer_disconnected.connect(remove_disconnected_peer)
Multiplayer.player_ready.connect(add_joined_peer)
func _process(_delta: float) -> void:
if not Steamworks.is_initiallized:
return
check_for_voice_local()
func add_joined_peer(id): # Fix calling this on yourself
var player : Node3D = Multiplayer.playerDict.get(id)
var remote_audio_stream_playback = player.get_node_or_null(voicechat_player_path).get_stream_playback()
network_playback.set(id, remote_audio_stream_playback)
network_voice_buffer.set(id,PackedByteArray())
current_sample_rate = Steam.getVoiceOptimalSampleRate()
send_sample_rate.rpc_id(id, current_sample_rate)
func remove_disconnected_peer(id):
network_playback.erase(id)
network_voice_buffer.erase(id)
func check_for_voice_local() -> void:
var available_voice: Dictionary = Steam.getAvailableVoice()
if !available_voice:
return
# Seems there is voice data
if available_voice['result'] == Steam.VOICE_RESULT_OK and available_voice['buffer'] > 0:
# Valve's getVoice uses 1024 but GodotSteam's is set at 8192?
# Our sizes might be way off; internal GodotSteam notes that Valve suggests 8kb
# However, this is not mentioned in the header nor the SpaceWar example but -is- in Valve's docs which are usually wrong
var voice_data: Dictionary = Steam.getVoice()
if voice_data['result'] == Steam.VOICE_RESULT_OK and voice_data['written']:
#print("[VoiceChat] Voice message has data: %s / %s" % [voice_data['result'], voice_data['written']])
# Here we can pass this voice data off on the network
#TODO: Only execute if multiplayer connected
get_sample_rate()
send_voice.rpc(voice_data['buffer'])
# If loopback is enable, play it back at this point
if has_loopback:
# Our sample rate function above without toggling
process_voice_data(voice_data['buffer'], 1)
@rpc("any_peer","call_remote","unreliable")
func send_voice(voice_buffer : PackedByteArray):
var sender_id : int = multiplayer.get_remote_sender_id()
if not network_playback.has(sender_id):
push_error("[VoiceChat] Received audio data, but there is no AudioStreamPlayer available for that user. Emitter: " + str(sender_id))
return
if not network_voice_buffer.has(sender_id):
push_error("[VoiceChat] Received audio data without allocated voice buffer for that user. Emitter: " + str(sender_id))
return
var local_peer_buffer : PackedByteArray = network_voice_buffer.get(sender_id)
if local_peer_buffer.size() >= max_buffer_size:
push_error("[VoiceChat] AudioBuffer exeeding size limit. Emitter: " + str(sender_id))
process_voice_data(voice_buffer, sender_id)
@rpc("any_peer","call_remote","reliable")
func send_sample_rate(sample_rate : int):
var sender_id : int = multiplayer.get_remote_sender_id()
if sample_rate < sample_rate_min || sample_rate > sample_rate_max:
push_warning("[VoiceChat] Received invalid sample rate for peer. Emitter: " + str(sender_id))
return
if not network_playback.has(sender_id):
push_warning("[VoiceChat] Received sample rate for invalid peer. Emitter: " + str(sender_id)) # Race condition?
return
if not Multiplayer.playerDict.has(sender_id):
push_error("[VoiceChat] Player exists in VoiceChat variables, but not playerDict??? Emitter: " + str(sender_id))
return
if not Multiplayer.playerDict.has(sender_id): return
var player : Node3D = Multiplayer.playerDict.get(sender_id)
if not player.has_node(voicechat_player_path): return
player.get_node(voicechat_player_path).stream.mix_rate = sample_rate # Fix this (ref: process_voice_data TODO:_1
network_playback.get(sender_id).clear_buffer()
network_voice_buffer.get(sender_id).clear()
func get_sample_rate() -> void:
current_sample_rate = Steam.getVoiceOptimalSampleRate()
if loopback_player.stream.mix_rate != current_sample_rate:
send_sample_rate.rpc(current_sample_rate)
loopback_player.stream.mix_rate = current_sample_rate
print("[VoiceChat] Changed own sample rate to: " + str(current_sample_rate))
func process_voice_data(voice_data: PackedByteArray, voice_source: int) -> void:
#TODO:_1 Fix this with a mix rate dict
var decompressed_voice : Dictionary
if voice_source == 0:
decompressed_voice = Steam.decompressVoice(voice_data, current_sample_rate)
elif Multiplayer.playerDict.has(voice_source):
var player : Node3D = Multiplayer.playerDict.get(voice_source)
if not player.has_node(voicechat_player_path):
push_error("[VoiceChat] Player %s doesn't have a VoiceChat node", voice_source)
return
var mix_rate = player.get_node_or_null(voicechat_player_path).stream.mix_rate
assert(mix_rate >= sample_rate_min and mix_rate <= sample_rate_max)
decompressed_voice = Steam.decompressVoice(voice_data, mix_rate)
else:
push_error("[VoiceChat] Received voice data for non existing player")
return
if decompressed_voice['result'] == Steam.VOICE_RESULT_OK and decompressed_voice['size'] > 0:
if voice_source == 0:
local_voice_buffer = decompressed_voice['uncompressed']
local_voice_buffer.resize(decompressed_voice['size'])
# We now iterate through the local_voice_buffer and push the samples to the audio generator
for i: int in local_playback.get_frames_available():
if local_voice_buffer.size() == 0: break
# Steam's audio data is represented as 16-bit single channel PCM audio, so we need to convert it to amplitudes
# Combine the low and high bits to get full 16-bit value
var raw_value: int = local_voice_buffer[0] | (local_voice_buffer[1] << 8)
# Make it a 16-bit signed integer
raw_value = (raw_value + 32768) & 0xffff
# Convert the 16-bit integer to a float on from -1 to 1
var amplitude: float = float(raw_value - 32768) / 32768.0
# push_frame() takes a Vector2. The x represents the left channel and the y represents the right channel
local_playback.push_frame(Vector2(amplitude, amplitude))
# Delete the used samples
local_voice_buffer.remove_at(0)
local_voice_buffer.remove_at(0)
else:
#TODO: Better naming
#TODO: Better explanation for 16-bit conversions
var peer_voice_buffer : PackedByteArray = network_voice_buffer.get(voice_source)
peer_voice_buffer = decompressed_voice['uncompressed']
peer_voice_buffer.resize(decompressed_voice['size'])
var peer_playback : AudioStreamGeneratorPlayback = network_playback.get(voice_source)
for i: int in peer_playback.get_frames_available():
if peer_voice_buffer.size() == 0: break
var raw_value: int = peer_voice_buffer[0] | (peer_voice_buffer[1] << 8)
# Make it a 16-bit signed integer
raw_value = (raw_value + 32768) & 0xffff
# Convert the 16-bit integer to a float on from -1 to 1
var amplitude: float = float(raw_value - 32768) / 32768.0
# push_frame() takes a Vector2. The x represents the left channel and the y represents the right channel
peer_playback.push_frame(Vector2(amplitude, amplitude))
# Delete the used samples
peer_voice_buffer.remove_at(0)
peer_voice_buffer.remove_at(0)
func record_voice(is_recording: bool) -> void:
# If talking, suppress all other audio or voice comms from the Steam UI
Steam.setInGameVoiceSpeaking(Steamworks.steam_id, is_recording)
if is_recording:
Steam.startVoiceRecording()
else:
Steam.stopVoiceRecording()

View File

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

73
script/featuremanager.gd Normal file
View File

@@ -0,0 +1,73 @@
extends Node
@onready var syncedFeatures: ConfigFile = ConfigFile.new()
@onready var settingsmenu = $/root/Main/Settingsmenu/VBoxContainer/MarginContainer2/HBoxContainer/VBoxContainer
@onready var config : ConfigFile = ConfigFile.new()
@export var debug : bool = false
func _ready() -> void:
var ms = MultiplayerSynchronizer.new()
var spg = SceneReplicationConfig.new()
var ppath = name + ":syncedFeatures"
spg.add_property(ppath)
ms.name = "ConfigSynchronizer"
ms.replication_config = spg
$/root.add_child.call_deferred(ms)
loadConfig()
update_menu()
func update_menu():
for feature in config.get_sections():
var container = HBoxContainer.new()
container.size_flags_vertical = Control.SIZE_EXPAND_FILL
container.name = feature
var label = Label.new()
label.text = feature
container.add_child(label)
var control : Control
match config.get_value(feature,"mode","_"):
"check":
control = CheckButton.new()
"color:":
control = ColorPickerButton.new()
"text":
control = LineEdit.new()
"menu":
control = OptionButton.new()
"button":
control = Button.new()
_:
control = Button.new()
container.add_child(control)
settingsmenu.add_child(container)
func loadConfig():
var err : Error = config.load("user://config.cfg") if not debug else config.load("res://defaultconfig.cfg")
match err:
OK:
print("Config loaded.")
return
ERR_FILE_NOT_FOUND:
push_error("Config not found. Loading default config.")
_:
push_error("Failed to load config: error " + str(err))
var err2 : Error = config.load("res://defaultconfig.cfg")
match err2:
OK:
push_warning("Default config loaded successfully.")
ERR_PARSE_ERROR:
push_error("Invalid default config")
return
_:
push_error("Yay a bug " + str(err2))
return
if ResourceLoader.exists("user://config.cfg"):
return
var err3 : Error = config.save("user://config.cfg")
match err3:
OK:
print("Saved new config file")
ERR_FILE_NO_PERMISSION:
push_error("No permission to save new config file.")
_:
push_error("Could not save new config file: " + str(err3))
return

View File

@@ -0,0 +1 @@
uid://4r044bvpv02o

17
script/interactBox.gd Normal file
View File

@@ -0,0 +1,17 @@
extends Area3D
class_name InteractBox
signal interactedWith(playerRef: PlayerCharacter)
@export var object: Node #Logic for how the interaction works, look for the ButtonLogicToggle, or ButtonLogicImpulse nodes
@export var type: String = "default"
var playerRef: PlayerCharacter
func _ready() -> void:
set_collision_layer_value(6,true) #Enables Interaction colision layer
set_collision_layer_value(1,false) #Disables Default collision layer
set_collision_mask_value(1,false) #Disables Default collision mask
monitoring = false
func interact() -> void:
interactedWith.emit(playerRef)
playerRef = null

View File

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

257
script/main.gd Normal file
View File

@@ -0,0 +1,257 @@
extends Control
class_name Main
@onready var start_btn : Button = $Mainmenu/Menu/MarginContainer/VBoxContainer/HBoxContainer4/VBoxContainer3/Start
@onready var disconnect_btn : Button = $Pausemenu/Menu/MarginContainer/VBoxCurrentLobby/Disconnect
@onready var name_edit : = $Mainmenu/Menu/MarginContainer/VBoxContainer/HBoxContainer4/VBoxContainer2/NameEdit
@onready var disconnectDialog = $Dialogs/DisconnectDialog
@onready var pausemenu: Control = $Pausemenu
@onready var settingsmenu: Control = $Settingsmenu
@onready var mainmenu: Control = $Mainmenu
@onready var hud: Control = $Hud
@onready var mapScreen: MapScreen = $MapScreen
@onready var missionEndScreen: MissionEndScreen = $MissionEndScreen
@onready var loadingScreen: Control = $LoadingScreen
@onready var mapsParent: Node3D = $Maps
@onready var playersParent: Node3D = $Players
var peer = WebSocketMultiplayerPeer.new()
var lobby: String = "res://Maps/Lobby/Lobby.tscn"
var player: PackedScene = preload("res://actors/Player/player_character.tscn")
@onready var currentMenu: Control = $Mainmenu
var settingsPressed: bool = false #Flag for when Settings button is pressed, handled in process by Menu handeling function
var displayMissionEndScreen: bool = false #Display Mission end screen while true
var displayLoadingScreen: bool = false
func _init():
peer.supported_protocols = ["ludus"]
Steamworks.initialize()
func _ready():
await Multiplayer.noray_connected
multiplayer.peer_connected.connect(peer_connected)
multiplayer.peer_disconnected.connect(peer_disconnected)
multiplayer.server_disconnected.connect(close_network)
multiplayer.connection_failed.connect(close_network)
multiplayer.connected_to_server.connect(connected)
# Set the player name according to the system username. Fallback to the path.
if Steamworks.is_initiallized:
get_tree().auto_accept_quit = false
Steamworks.create_lobby()
name_edit.text = Steam.getPersonaName()
elif false: #TODO: Set text to saved value
name_edit.text = ""
disableMultiplayer()
else:
name_edit.text = "Spacebots Player"
disableMultiplayer()
func _process(_delta: float):
#Does all switching between Menus
handleMenus()
func changeMenu(changeTo: Control) -> void:
if currentMenu == changeTo: return
var thisPlayer := Multiplayer.thisPlayer
if thisPlayer:
if changeTo == hud or changeTo == missionEndScreen:
thisPlayer.currentControlState = thisPlayer.controls.DEFAULT
else:
thisPlayer.currentControlState = thisPlayer.controls.STANDARD_MINIGAME
currentMenu.hide()
changeTo.show()
changeTo.grab_focus.call_deferred()
currentMenu = changeTo
func handleMenus() -> void: #This function returns after an Input is handled, ensuring one menu action per frame and avoiding conflicts
if !playersParent.get_child_count() > 0: return
#Flags for which Inputs are pressed
var pausePressed: bool = Input.is_action_just_pressed("pause")
var leaveMinigamePressed: bool = Input.is_action_just_pressed("leaveMinigame")
var mapPressed: bool = Input.is_action_just_pressed("map")
if displayLoadingScreen:
if currentMenu != loadingScreen:
changeMenu(loadingScreen)
return
if displayMissionEndScreen:
if currentMenu != missionEndScreen:
changeMenu(missionEndScreen)
return
if mapPressed:
if Minigames.minigameActive: Minigames.endMinigame(false)
if currentMenu != mapScreen:
changeMenu(mapScreen)
else:
changeMenu(hud)
return
if (leaveMinigamePressed or pausePressed) and Minigames.minigameActive:
Minigames.endMinigame(false)
return
if settingsPressed:
if currentMenu != settingsmenu:
changeMenu(settingsmenu)
else:
changeMenu(pausemenu)
settingsPressed = false
return
if pausePressed:
if currentMenu == hud:
changeMenu(pausemenu)
else:
changeMenu(hud)
return
func disableMultiplayer() -> void:
$/root/Main/Mainmenu/Menu/MarginContainer/VBoxContainer/HBoxContainer/VBoxMultiplayerDisabled.show()
$/root/Main/Mainmenu/Menu/MarginContainer/VBoxContainer/HBoxContainer/VBoxLobbylist.hide()
func start_game():
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
mainmenu.hide()
start_btn.disabled = true
disconnect_btn.disabled = false
hud.show()
@rpc("authority","call_local","reliable")
func changeMap(newMap: String, mapSeed: int = 0) -> void:
if !(load(newMap).instantiate() is MapLogic):
push_error("newMap: String is not a map scene")
return
var map : MapLogic = load(newMap).instantiate()
if newMap == "res://test/spaceship_test.tscn":
map.setSeed(hash(mapSeed))
if mapsParent.get_child_count() > 0:
if Multiplayer.getCurrentMapLogic().is_connected(StringName("missionLost"),missionLost):
Multiplayer.getCurrentMapLogic().disconnect(StringName("missionLost"),missionLost)
mapsParent.get_child(0).queue_free()
mapsParent.add_child(map)
initiatePlayerPosition(map.playerStartPos)
Multiplayer.setCurrentMapLogic(map)
map.missionLost.connect(missionLost)
func initiatePlayerPosition(pos: Vector3) -> void:
for p in Multiplayer.playerDict.values():
if p.is_multiplayer_authority():
p.global_position = pos
func missionLost() -> void:
changeMap(lobby)
func stop_game():
disconnect_btn.disabled = true
mainmenu.show()
pausemenu.hide()
hud.hide()
Input.mouse_mode = Input.MOUSE_MODE_CONFINED
#Steamworks.stop()
var currentMaps = mapsParent.get_children()
for child in currentMaps:
child.queue_free()
var players = playersParent.get_children()
for child in players:
child.set_process(false)
child.queue_free()
await get_tree().process_frame
start_btn.disabled = false
func close_network():
peer.close()
stop_game()
disconnectDialog.popup_centered()
disconnectDialog.get_ok_button().grab_focus()
multiplayer.multiplayer_peer = null
Multiplayer.currentMapLogic = null
Multiplayer.playerDict = {}
Multiplayer.alivePlayerDict = {}
Multiplayer.is_host = false
Steamworks.lobby_id = 0
Steamworks.lobby_members = []
Steamworks.lobby_members_max = 5
Steamworks.steam_id = 0
Steamworks.steam_username = ""
func connected():
if Multiplayer.is_host == false:
add_player.rpc_id(1, multiplayer.get_unique_id())
func peer_connected(_id):
pass
#Steamworks.on_peer_add(id)
func peer_disconnected(id):
if Multiplayer.is_host:
remove_player(id)
func on_server_disconnected():
on_Disconnect_pressed()
func on_Start_pressed():
Multiplayer.host()
start_game()
changeMap(lobby)
changeMenu(hud)
add_player(multiplayer.get_unique_id())
func on_Disconnect_pressed():
if Steamworks.lobby_members.size() > 1:
Steam.leaveLobby(Steamworks.lobby_id)
close_network()
func on_settings_pressed():
settingsPressed = true
func on_Connect(norayid: String):
start_btn.disabled = true
disconnect_btn.disabled = false
start_game()
changeMap(lobby)
Multiplayer.join(norayid)
@rpc("any_peer")
func add_player(id):
var player_instance: PlayerCharacter = player.instantiate()
player_instance.name = str(id)
playersParent.add_child(player_instance)
@rpc("any_peer")
func remove_player(id):
if playersParent.get_node(str(id)):
playersParent.get_node(str(id)).queue_free()

1
script/main.gd.uid Normal file
View File

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

14
script/mainmenu.gd Normal file
View File

@@ -0,0 +1,14 @@
extends Control
@onready var debugbtn : Button = $/root/Main/Mainmenu/Menu/MarginContainer/VBoxContainer/HBoxContainer/VBoxLobbylist/RefreshLobbies
@onready var main : Control = $/root/Main
func on_start_pressed() -> void:
main.on_Start_pressed()
func on_lobby_list_item_activated(index: int) -> void:
Steamworks.leave_lobby()
var lobby_id = Steamworks.lobby_index_list.get(index)
Steamworks.join_lobby(lobby_id)
func on_refresh_lobbies_pressed() -> void:
Steamworks.render_global_lobbies()

1
script/mainmenu.gd.uid Normal file
View File

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

59
script/map_screen.gd Normal file
View File

@@ -0,0 +1,59 @@
extends Control
class_name MapScreen
@onready var map: TextureRect = $TextureRect
@onready var playerIndicator: ColorRect = $PlayerIndicator
@onready var taskListContainer: VBoxContainer = $TaskList
@onready var taskLabelSettings: LabelSettings = preload("res://test/TaskListLabelSettingstest.tres")
var mapImmage: Image
var playerCharacter: PlayerCharacter
var mapDimensions: Vector2 = Vector2(0,0)
var trackPlayer: bool = false
var taskList: Array[MissionTask]
func _process(_delta: float) -> void:
#Update Player Pos on map
if trackPlayer and playerCharacter:
playerIndicator.position = Vector2(playerCharacter.position.x, playerCharacter.position.z) * map.size/mapDimensions + Vector2(map.size.x/2 + map.position.x,map.size.y/2 + map.position.y) - playerIndicator.size/2
func generateMap(grid: Array[Array]) -> void: #Seccond Array contains gridCells
mapImmage = Image.create_empty(grid.size(),grid[0].size(),false,Image.FORMAT_RGB8)
for x in grid.size():
for y in grid[x].size():
if grid[x][y].biomeConnection:
mapImmage.set_pixel(x,y,Color.DARK_GREEN)
elif grid[x][y].spaceTaken:
mapImmage.set_pixel(x,y,Color.GREEN)
else:
mapImmage.set_pixel(x,y,Color.DIM_GRAY)
map.texture = ImageTexture.create_from_image(mapImmage)
mapDimensions = Vector2(grid.size(),grid[0].size())
if !playerCharacter:
playerCharacter = Multiplayer.thisPlayer
trackPlayer = true
func addTask(task: MissionTask) -> void:
taskList.push_back(task)
var label := Label.new()
label.name = task.taskName
label.text = task.description
label.label_settings = taskLabelSettings
taskListContainer.add_child(label)
task.taskLabel = label
func completeTask(task: MissionTask) -> void:
if taskList.has(task):
task.taskLabel.queue_free()
taskList.erase(task)

1
script/map_screen.gd.uid Normal file
View File

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

36
script/minigames.gd Normal file
View File

@@ -0,0 +1,36 @@
extends Control
#TODO: After Minigame is completed next Pausemenu only opens after pressing twice
var minigameActive: bool = false
signal minigameCompleted
var currentInitiator: MinigameInitiator
func _ready() -> void:
pass
#func _process(_delta: float) -> void:
#if Input.is_action_just_pressed("leaveMinigame"): endMinigame()
func spawnMinigame(minigameName: String, initiator: MinigameInitiator) -> void:
if minigameActive: return
match minigameName:
"wires":
add_child(preload("res://Minigames/Minigames/Wires.tscn").instantiate())
minigameActive = true
currentInitiator = initiator
func endMinigame(outcome: bool) -> void:
if !minigameActive: return
if outcome:
currentInitiator.minigameCompleted = true
minigameCompleted.emit()
else:
get_child(0).queue_free()
minigameActive = false
currentInitiator = null
Multiplayer.thisPlayer.currentControlState = 0

1
script/minigames.gd.uid Normal file
View File

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

View File

@@ -0,0 +1,4 @@
extends Control
class_name MissionEndScreen
@onready var label: Label = $Label

View File

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

125
script/multiplayer.gd Normal file
View File

@@ -0,0 +1,125 @@
extends Node
signal noray_connected
signal player_ready(id : int)
const NORAY_ADDRESS = "tomfol.io"
const NORAY_PORT = 8890
var is_host = false
var external_oid = ""
var debug = false
var playerDict: Dictionary[int,PlayerCharacter]
var alivePlayerDict: Dictionary[int,PlayerCharacter]
var currentMapLogic: MapLogic
var thisPlayer: PlayerCharacter #Player Character playing on this machiene, this value is obviously different for every player
@onready var errordialog = $/root/Main/Dialogs/ErrorDialog
@onready var start_btn : Button = $/root/Main/Mainmenu/Menu/MarginContainer/VBoxContainer/HBoxContainer4/VBoxContainer3/Start
@onready var main : Main = $/root/Main
func _ready():
Noray.on_connect_to_host.connect(on_noray_connected)
Noray.on_connect_nat.connect(handle_nat_connection)
#Noray.on_connect_relay.connect(handle_relay_connection)
Noray.connect_to_host(NORAY_ADDRESS, NORAY_PORT)
player_ready.connect(peer_connected)
multiplayer.peer_disconnected.connect(peer_disconnected)
func getCurrentMapLogic() -> MapLogic:
return currentMapLogic
func setCurrentMapLogic(toMap: MapLogic) -> void:
currentMapLogic = toMap
func peer_connected(id: int) -> void:
if !get_node("/root/Main/Players").get_child_count(): return
playerDict.set(id,get_node("/root/Main/Players/" + str(id)))
alivePlayerDict.set(id,get_node("/root/Main/Players/" + str(id)))
if !playerDict.get(id): playerDict.erase(id)
if !alivePlayerDict.get(id): alivePlayerDict.erase(id)
func peer_disconnected(id: int) -> void:
playerDict.erase(id)
alivePlayerDict.erase(id)
func on_noray_connected():
print("Connected to Noray server")
Noray.register_host()
await Noray.on_pid
await Noray.register_remote()
start_btn.disabled = false
noray_connected.emit()
func host():
var peer = ENetMultiplayerPeer.new()
var error = OK
if debug:
error = peer.create_server(1069)
else:
error = peer.create_server(Noray.local_port)
if error:
errordialog.title = "Error"
errordialog.dialog_text = "Failed to create Server: " + str(error_string(error) + " (" + str(error) + ")")
errordialog.popup_centered()
errordialog.get_ok_button().grab_focus()
main.close_network()
else:
multiplayer.multiplayer_peer = peer
is_host = true
func join(oid: String):
if debug:
var peer = ENetMultiplayerPeer.new()
var error = peer.create_client("localhost", 1069)
print("Join: " + error_string(error) + " " + str(error))
multiplayer.multiplayer_peer = peer
return
Noray.connect_nat(oid)
external_oid = oid
func handle_nat_connection(address, port):
var err = await connect_to_server(address, port)
if err != OK && !is_host:
print("NAT failed, using relay")
err = Noray.connect_relay(external_oid)
return err
func handle_relay_connection(address, port):
return await connect_to_server(address, port)
func connect_to_server(address, port):
var err = OK
if !is_host:
var udp = PacketPeerUDP.new()
udp.bind(Noray.local_port)
udp.set_dest_address(address, port)
err = await PacketHandshake.over_packet_peer(udp)
udp.close()
if err != OK:
if err != ERR_BUSY:
print("Handshake failed")
return err
else:
print("Handshake success")
var peer = ENetMultiplayerPeer.new()
err = peer.create_client(address, port, 0, 0, 0, Noray.local_port)
if err != OK:
return err
multiplayer.multiplayer_peer = peer
return OK
else:
err = await PacketHandshake.over_enet(multiplayer.multiplayer_peer.host, address, port)
return err

View File

@@ -0,0 +1 @@
uid://77q1pjrfpua6

31
script/objectSpawner.gd Normal file
View File

@@ -0,0 +1,31 @@
extends Node3D
#class_name ObjectSpawner
@export var Objects: Array[RandomObject]
@export var Parent: Node
var weightArray: PackedFloat32Array
var rng = RandomNumberGenerator.new()
func _ready():
if Objects.is_empty(): return
var spaceship = get_node("/root/Main/Maps/Spaceship")
for obj in Objects: #Init weight array
weightArray.push_back(obj.probability)
var objectReference = load(Objects[rng.rand_weighted(weightArray)].dir) #Get random Object depending on weights
var ObjectInstance = objectReference.instantiate()
if Parent:
Parent.call_deferred("add_child",ObjectInstance) #Adds Object instance
elif spaceship:
spaceship.call_deferred("add_child",ObjectInstance)
else:
push_error("Object Spawner failed to spawn object because of missing parent node")
ObjectInstance.rotation = self.global_rotation
ObjectInstance.position = self.global_position
self.queue_free()

View File

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

View File

@@ -0,0 +1,89 @@
extends Node3D
class_name PathfindingGridLoader
var loading: bool = false # when this is true the grid starts loading
var loading_done: bool = false # when the grid is loaded, this becomes true
@export var height: float= 0.2 ##Choose a height that is just above the floor
@export var lengthX: int = 50 ##How far the grid generates in the x direction, generates as a rectangle
@export var lengthZ: int = 50 ##How far the grid generates in the z direction, generates as a rectangle
@export var point_density: float = 1 ##How many points per unit of grid, cannot be = 0
@export var minimum_connections_per_point: int = 3
@export var wallCollisionLayer: int = 0b100
var astar: AStar2D = AStar2D.new()
func _physics_process(_delta: float) -> void:
if loading == false: return
if loading_done == true: return
generatePoints()
connectPointsToNeighbors()
killPointsWithoutFriends(minimum_connections_per_point)
addWeightToPoints()
loading = false
loading_done = true
func loadGrid() -> void:
loading = true
func castRay(startPoint:Vector3,endPoint: Vector3,collisionMask:int = 0xFFFFFFFF,hitFromInside: bool = true) -> bool: ##Returns true if the ray collides and false if it doesn't
var space_state = get_world_3d().direct_space_state
var rayQueryParam = PhysicsRayQueryParameters3D.create(startPoint,endPoint)
rayQueryParam.collision_mask = collisionMask
rayQueryParam.hit_from_inside = hitFromInside
return space_state.intersect_ray(rayQueryParam).size()
func checkPoint(point: Vector2) -> bool: ## only ever call this from physics process as it uses physics for raycast and may not work on idle callback
var floorDetected: bool = castRay(Vector3(point.x,height,point.y),Vector3(point.x,-(height*1.5),point.y))
var wallDetected: bool = castRay(Vector3(point.x,height,point.y),Vector3(point.x,-(height*1.5),point.y),wallCollisionLayer) #Walls are on collision layer 3 (0b100)
if !wallDetected && floorDetected:
return true
else:
return false
func getPointsInRadius(origin: Vector2,radius: float, maxPoints: int = 9) -> Array[int]: #Origin point is always index 0 in the array
var pointIDs: Array[int]
for point in maxPoints:
var id = astar.get_closest_point(origin)
if (astar.get_point_position(id) - origin).length() <= radius:
pointIDs.push_back(id)
astar.set_point_disabled(id)
for id in pointIDs:
astar.set_point_disabled(id,false)
return pointIDs
func connectIfValid(id1: int, id2: int):
var point1 := astar.get_point_position(id1)
var point2 := astar.get_point_position(id2)
if !castRay(Vector3(point1.x,height,point1.y),Vector3(point2.x,height,point2.y)):
astar.connect_points(id1,id2)
func generatePoints() -> void:
var currentPoint: Vector2
var currentID: int = 0
for x in lengthX*point_density:
for z in lengthZ*point_density:
currentPoint = Vector2((x/point_density + self.global_position.x),z/point_density + self.global_position.z)
if checkPoint(currentPoint):
astar.add_point(currentID,currentPoint)
currentID += 1
func connectPointsToNeighbors() -> void:
for id in astar.get_point_ids():
var neighborPoints: Array[int]
neighborPoints = getPointsInRadius(astar.get_point_position(id),(1/point_density)*1.5)
for point in neighborPoints.size()-1:
connectIfValid(neighborPoints[0],neighborPoints[point+1])
func killPointsWithoutFriends(minConnections: int) -> void: ##Really mean and evil and fucked up function
var cullPoints: Array[int]
for id in astar.get_point_ids():
if astar.get_point_connections(id).size() < minConnections:
cullPoints.push_back(id)
for point in cullPoints:
astar.remove_point(point)
func addWeightToPoints() -> void: #Add weight depending on number of connections
for id in astar.get_point_ids():
astar.set_point_weight_scale(id,8 - astar.get_point_connections(id).size())

View File

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

15
script/pausemenu.gd Normal file
View File

@@ -0,0 +1,15 @@
extends Control
@onready var main : Main = $/root/Main
func on_disconnect_pressed() -> void:
main.on_Disconnect_pressed()
func _on_settings_pressed() -> void:
main.on_settings_pressed()
func on_lobby_member_list_item_activated(index: int) -> void:
if Steamworks.is_initiallized:
print("You clicked on: " + Steam.getFriendPersonaName(Steamworks.member_index_list.get(index)))
else:
print("You clicked on: index: " + str(index) + " (Steam unavailable)")

1
script/pausemenu.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://2niwcxdyq213

5
script/randomObject.gd Normal file
View File

@@ -0,0 +1,5 @@
extends Node
class_name RandomObject
@export var dir: String
@export var probability: float #Value between 0 and 1

View File

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

22
script/settingsmenu.gd Normal file
View File

@@ -0,0 +1,22 @@
extends Control
@onready var main : Main = $/root/Main
## Previous Code IDC about reading
#var settingsmenu : bool = false
#
#func _process(_delta: float) -> void:
#if Input.is_action_just_pressed("settings") and %Pausemenu.visible:
#if not %Settingsmenu.visible:
#%Settingsmenu.show()
#%Settingsmenu.grab_focus()
#else:
#%Settingsmenu.hide()
#%Pausemenu.grab_focus()
#settingsmenu = !settingsmenu
func _on_leave_pressed() -> void:
main.on_settings_pressed()
func _on_quit_pressed() -> void:
get_tree().quit()

View File

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

4
script/start_ik.gd Normal file
View File

@@ -0,0 +1,4 @@
extends SkeletonIK3D
func _ready() -> void:
start()

1
script/start_ik.gd.uid Normal file
View File

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

226
script/steamworks.gd Normal file
View File

@@ -0,0 +1,226 @@
extends Control
@onready var list : ItemList = $/root/Main/Mainmenu/Menu/MarginContainer/VBoxContainer/HBoxContainer/VBoxLobbylist/ItemList
@onready var lobby_members_list : ItemList = $/root/Main/Pausemenu/Menu/MarginContainer/VBoxCurrentLobby/LobbyMemberList
@onready var profileSprite : Sprite2D = $/root/Main/Mainmenu/Menu/MarginContainer/VBoxContainer/HBoxContainer4/VBoxContainer/ProfileSprite
@onready var main : Main = $/root/Main
const PACKET_READ_LIMIT: int = 32
const app_id: int = 480
var is_initiallized: bool = false #Steam initialized successfully
var lobby_index_list: Dictionary = {} # index -> lobbyid
var member_index_list: Dictionary = {} # index -> steam_id
var lobby_data
var lobby_id: int = 0
var lobby_members: Array = []
var lobby_members_max: int = 5
var steam_id: int = 0
var steam_username: String = ""
func initialize() -> void:
var initialize_response: Dictionary = Steam.steamInitEx(false, Steamworks.app_id)
if initialize_response["status"] != OK:
match initialize_response["status"]:
1:
push_error("[Steam] Initialization failed: Error")
2:
push_error("[Steam] Initialization failed: Can't connect to steam.")
3:
push_error("[Steam] Initialization failed: Steam client out of date.")
return
print("[Steam] Initialized successfully")
steam_id = Steam.getSteamID()
steam_username = Steam.getPersonaName()
#Load user avatar
Steam.getPlayerAvatar(Steam.AVATAR_SMALL,Steam.getSteamID())
Steam.avatar_loaded.connect(steam_avatar_loaded)
Steam.join_requested.connect(on_lobby_join_requested)
Steam.lobby_chat_update.connect(on_lobby_chat_update)
Steam.lobby_created.connect(on_lobby_created)
# Steam.lobby_data_update.connect(on_lobby_data_update)
#Steam.lobby_invite.connect(_on_lobby_invite)
Steam.lobby_joined.connect(on_lobby_joined)
Steam.lobby_match_list.connect(on_lobby_match_list)
Steam.lobby_message.connect(on_lobby_message)
Steam.persona_state_change.connect(on_persona_change)
# Check for command line arguments
check_command_line()
#Load lobbylist
render_global_lobbies()
is_initiallized = true
func _notification(what: int) -> void:
if what == NOTIFICATION_WM_CLOSE_REQUEST:
# do save stuff here
if lobby_id != 0:
Steam.leaveLobby(lobby_id)
print("[Steam] Left lobby " + str(lobby_id))
main.on_Disconnect_pressed()
print("Exiting")
get_tree().quit()
func check_command_line() -> void:
var args: Array = OS.get_cmdline_args()
# There are arguments to process
if args.size() <= 0:
return
# A Steam connection argument exists
if args[0] != "+connect_lobby":
return
# Lobby invite exists so try to connect to it
if int(args[1]) <= 0:
return
print("[Steam] Command line lobby ID: %s" % args[1])
join_lobby(int(args[1]))
func steam_avatar_loaded(user_id: int, avatar_size: int, avatar_buffer: PackedByteArray):
var avatar_image: Image = Image.create_from_data(avatar_size, avatar_size, false, Image.FORMAT_RGBA8, avatar_buffer)
var avatar_texture: ImageTexture = ImageTexture.create_from_image(avatar_image)
if user_id == steam_id:
profileSprite.set_texture(avatar_texture)
var index = member_index_list.find_key(user_id)
if index != null:
lobby_members_list.set_item_icon(index,avatar_texture)
func render_global_lobbies():
Steam.addRequestLobbyListDistanceFilter(Steam.LOBBY_DISTANCE_FILTER_WORLDWIDE)
Steam.requestLobbyList()
func on_lobby_match_list(lobbies: Array):
list.clear()
lobby_index_list = {}
await get_tree().create_timer(.05).timeout
for lobby in lobbies:
var lobby_name = Steam.getLobbyData(lobby,"name")
var lobby_mode = Steam.getLobbyData(lobby,"mode")
#TODO: Remove when own steam page is aquired
if lobby_name == "" or lobby_mode != "Spacebots":
continue
if Steam.getNumLobbyMembers(lobby) == 0 or lobby == lobby_id:
continue
var index = list.add_item("%s [%s/%s]" % [lobby_name, Steam.getNumLobbyMembers(lobby), Steam.getLobbyMemberLimit(lobby)] , null, false)
list.set_item_tooltip(index, str(lobby))
lobby_index_list.set(index,lobby)
func create_lobby() -> void:
if lobby_id != 0: # Make sure a lobby is not already set
return
print("[Steam] Creating Lobby")
Steam.createLobby(Steam.LOBBY_TYPE_PUBLIC, lobby_members_max)
func on_lobby_created(connect_: int, this_lobby_id: int) -> void:
if connect_ != 1:
push_error("[Steam] Error creating lobby:" + str(connect_))
return
# Set the lobby ID
lobby_id = this_lobby_id
print("[Steam] Created steam lobby: %s" % lobby_id)
Steam.setLobbyJoinable(lobby_id, true)
Steam.setLobbyData(lobby_id, "name", steam_username + "'s lobby")
Steam.setLobbyData(lobby_id, "mode", "Spacebots")
print("[Steam] Lobbydata set successfully")
lobby_members_list.add_item(Steam.getPlayerNickname(steam_id), profileSprite.get_texture(), true)
#Allow P2P connections to fallback to being relayed through Steam if needed
#var set_relay: bool = Steam.allowP2PPacketRelay(true)
#print("Allowing Steam to be relay backup: %s" % set_relay)
func join_lobby(this_lobby_id: int) -> void:
print("[Steam] Attempting to join lobby %s" % this_lobby_id)
# Make the lobby join request to Steam
Steam.joinLobby(this_lobby_id)
func on_lobby_joined(this_lobby_id: int, _permissions: int, _locked: bool, response: int) -> void:
# If joining was successful
if response != Steam.CHAT_ROOM_ENTER_RESPONSE_SUCCESS:
# Get the failure reason
var fail_reason: String
match response:
Steam.CHAT_ROOM_ENTER_RESPONSE_DOESNT_EXIST: fail_reason = "This lobby no longer exists."
Steam.CHAT_ROOM_ENTER_RESPONSE_NOT_ALLOWED: fail_reason = "You don't have permission to join this lobby."
Steam.CHAT_ROOM_ENTER_RESPONSE_FULL: fail_reason = "The lobby is now full."
Steam.CHAT_ROOM_ENTER_RESPONSE_ERROR: fail_reason = "Uh... something unexpected happened!"
Steam.CHAT_ROOM_ENTER_RESPONSE_BANNED: fail_reason = "You are banned from this lobby."
Steam.CHAT_ROOM_ENTER_RESPONSE_LIMITED: fail_reason = "You cannot join due to having a limited account."
Steam.CHAT_ROOM_ENTER_RESPONSE_CLAN_DISABLED: fail_reason = "This lobby is locked or disabled."
Steam.CHAT_ROOM_ENTER_RESPONSE_COMMUNITY_BAN: fail_reason = "This lobby is community locked."
Steam.CHAT_ROOM_ENTER_RESPONSE_MEMBER_BLOCKED_YOU: fail_reason = "A user in the lobby has blocked you from joining."
Steam.CHAT_ROOM_ENTER_RESPONSE_YOU_BLOCKED_MEMBER: fail_reason = "A user you have blocked is in the lobby."
print("[Steam] Failed to join lobby : %s" % fail_reason)
return
lobby_id = this_lobby_id
# Clear any previous lobby members lists, if you were in a previous lobby
get_lobby_members()
# TODO: Add failed to connect dialog
func on_lobby_join_requested(this_lobby_id: int, friend_id: int) -> void:
# Get the lobby owner's name
if this_lobby_id == lobby_id:
print("[Steam] Lobby join canceled: Already in that lobby")
return
if lobby_id != 0:
print("[Steam] Leaving lobby " + str(lobby_id))
leave_lobby()
var owner_name: String = Steam.getFriendPersonaName(friend_id)
print("[Steam] Joining %s's lobby..." % owner_name)
# Attempt to join the lobby
join_lobby(this_lobby_id)
func get_lobby_members() -> void:
# Clear your previous lobby list
lobby_members.clear()
lobby_members_list.clear()
# Get the number of members from this lobby from Steam
var num_of_members: int = Steam.getNumLobbyMembers(lobby_id)
# Get the data of these players from Steam
for this_member in range(0, num_of_members):
var member_steam_id: int = Steam.getLobbyMemberByIndex(lobby_id, this_member)
var member_steam_name: String = Steam.getFriendPersonaName(member_steam_id)
# Add them to the list
lobby_members.append({"steam_id":member_steam_id, "steam_name":member_steam_name})
Steam.getPlayerAvatar(Steam.AVATAR_SMALL,member_steam_id)
if member_steam_id == Steam.getLobbyOwner(lobby_id):
member_steam_name += " (Host)"
var index: int = lobby_members_list.add_item(member_steam_name, null, true)
if member_steam_id != Steam.getLobbyOwner(lobby_id):
lobby_members_list.set_item_tooltip_enabled(index, false)
else:
lobby_members_list.set_item_tooltip(index,str(lobby_id))
if Steam.getFriendRelationship(member_steam_id) == Steam.FRIEND_FLAG_IMMEDIATE:
list.set_item_custom_fg_color(index, Color(0.9,1,0.9,1))
member_index_list.set(index, member_steam_id)
func on_persona_change(this_steam_id: int, flag: int) -> void:
# Make sure you're in a lobby and this user is valid or Steam might spam your console log
if lobby_id > 0:
var hex : String = "0x%04X" % flag
var name_ : String = Steam.getFriendPersonaName(this_steam_id)
print("[Steam] %s (%s) had information change, updating member list: %s" % [name_, this_steam_id, hex])
# Update the player list
get_lobby_members()
func on_lobby_message(lobby_id_msg: int, user: int, buffer: String, _chat_type: Steam.ChatEntryType):
print("[Chat] " + Steam.getFriendPersonaName(user) + ": " + buffer)
if(user != Steam.getLobbyOwner(lobby_id)):
print("[Steam] Message not by Lobby owner: " + str(lobby_id_msg) + " " + str(Steam.getLobbyOwner(lobby_id)))
return
if(buffer.begins_with("NorayID:")):
buffer = buffer.trim_prefix("NorayID:")
print(buffer)
$/root/Main.on_Connect(buffer)
func on_lobby_chat_update(_lobby_id_msg_update: int, _changed_id: int, _changer_id: int, chat_state: Steam.ChatMemberStateChange):
print("lobby chat update: " + str(chat_state))
if Steam.CHAT_MEMBER_STATE_CHANGE_ENTERED and Steam.getLobbyOwner(lobby_id) == steam_id:
var result = Steam.sendLobbyChatMsg(lobby_id, "NorayID:" + str(Noray.oid))
print("lobby chat update2: " + str(chat_state))
if result == false:
print("Chat message couldn't be send")
func leave_lobby():
Steam.leaveLobby(lobby_id)
lobby_id = 0

1
script/steamworks.gd.uid Normal file
View File

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