Added a basic test map, options menu, bug fixes, and much more.
This commit is contained in:
Patrick 2025-08-08 10:57:42 +02:00
parent d72b5ac57b
commit 2dce012535
34 changed files with 1268 additions and 84 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
# Godot 4+ specific ignores # Godot 4+ specific ignores
.godot/ .godot/
/android/ /android/
releases/

View file

@ -12,9 +12,7 @@ func _on_dropoff(body: Node3D) -> void:
player.released_player.rpc_id(player.get_multiplayer_authority()) player.released_player.rpc_id(player.get_multiplayer_authority())
(player.get_node("ReleaseTimer") as Timer).stop() (player.get_node("ReleaseTimer") as Timer).stop()
($DamageTimer as Timer).start() ($DamageTimer as Timer).start()
for i: int in range(Multiplayer.players.size()): Multiplayer.players[caught_player.get_multiplayer_authority()].captured = true
if Multiplayer.players[i].id == caught_player.get_multiplayer_authority():
Multiplayer.players[i].captured = true
elif !player.hunter and player.visible and caught_player and caught_player.health > 0: elif !player.hunter and player.visible and caught_player and caught_player.health > 0:
caught_player.go_free.rpc_id(caught_player.get_multiplayer_authority()) caught_player.go_free.rpc_id(caught_player.get_multiplayer_authority())
caught_player = null caught_player = null

View file

@ -1,10 +0,0 @@
extends Area3D
func _on_body_entered(body: Node3D) -> void:
if multiplayer.is_server() and body is Player:
var player: Player = body
if !player.hunter:
for i: int in range(Multiplayer.players.size()):
if Multiplayer.players[i].id == player.get_multiplayer_authority():
Multiplayer.players[i].escaped = true
player.get_caught.rpc_id(player.get_multiplayer_authority(), $"../SpectatorCamera".get_path())

View file

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

View file

@ -1,12 +1,16 @@
[gd_scene load_steps=3 format=3 uid="uid://dbgqg1wtqmhw3"] [gd_scene load_steps=3 format=3 uid="uid://bncmv3p36y2qg"]
[ext_resource type="Script" uid="uid://bvjyyj4m56b7b" path="res://entities/escape_area.gd" id="1_ubptc"] [sub_resource type="GDScript" id="GDScript_yuux7"]
script/source = "class_name EscapeArea
extends Area3D
"
[sub_resource type="BoxShape3D" id="BoxShape3D_eij3b"] [sub_resource type="BoxShape3D" id="BoxShape3D_eij3b"]
size = Vector3(10, 10, 10) size = Vector3(10, 10, 10)
[node name="EscapeArea" type="Area3D"] [node name="EscapeArea" type="Area3D"]
script = ExtResource("1_ubptc") script = SubResource("GDScript_yuux7")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."] [node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("BoxShape3D_eij3b") shape = SubResource("BoxShape3D_eij3b")

View file

@ -5,7 +5,7 @@ var done: bool = false
func _ready() -> void: func _ready() -> void:
if multiplayer.is_server(): if multiplayer.is_server():
Multiplayer.fuseboxes += 1 Multiplayer.add_fusebox.rpc()
func _on_fixing_started(body: Node3D) -> void: func _on_fixing_started(body: Node3D) -> void:
if multiplayer.is_server() and !done and body is Player: if multiplayer.is_server() and !done and body is Player:
@ -16,4 +16,4 @@ func _on_fixing_started(body: Node3D) -> void:
func complete_fusebox() -> void: func complete_fusebox() -> void:
($Hinge as Node3D).rotation.y = 0 ($Hinge as Node3D).rotation.y = 0
done = true done = true
Multiplayer.fuseboxes -= 1 Multiplayer.remove_fusebox.rpc()

View file

@ -5,16 +5,25 @@ const SPEED: float = 5.0
const JUMP_VELOCITY: float = 4.5 const JUMP_VELOCITY: float = 4.5
@onready var camera_x: Node3D = $ViewY/ViewX @onready var camera_x: Node3D = $ViewY/ViewX
@onready var camera: Camera3D = $ViewY/ViewX/View @onready var spring_arm: SpringArm3D = $ViewY/ViewX/Arm
@onready var camera: Camera3D = $ViewY/ViewX/Arm/View
@onready var syringe: Node3D = $Syringe @onready var syringe: Node3D = $Syringe
@onready var needle: Area3D = $Syringe/SyringeNeedle @onready var needle: Area3D = $Syringe/SyringeNeedle
@onready var bag: Node3D = $Bag @onready var bag: Node3D = $Bag
@onready var captured: Node3D = $Bag/Captured @onready var captured: Node3D = $Bag/Captured
@onready var mesh: MeshInstance3D = $Mesh
@onready var collision: CollisionShape3D = $Collision
var hunter: bool = true var hunter: bool = true
var caught_player: Player = null var caught_player: Player = null
var new_spawn: Vector3 var new_spawn: Vector3
var crouching: bool = false
var speed_penalty: float = 0.0
@export var health: int = 100 @export var health: int = 100
func _ready() -> void: func _ready() -> void:
@ -31,9 +40,9 @@ func _ready() -> void:
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:
# Zoom # Zoom
if Input.is_action_just_released("zoom_in") and camera.position.z > 0: if Input.is_action_just_released("zoom_in") and camera.position.z > 0:
camera.position.z += -1 spring_arm.spring_length -= 1
elif Input.is_action_just_released("zoom_out"): elif Input.is_action_just_released("zoom_out"):
camera.position.z += 1 spring_arm.spring_length += 1
if is_multiplayer_authority(): if is_multiplayer_authority():
# Focus # Focus
@ -49,15 +58,31 @@ func _physics_process(delta: float) -> void:
velocity += get_gravity() * delta velocity += get_gravity() * delta
# Jumping # Jumping
if Input.is_action_just_pressed("jump") and is_on_floor(): if Input.is_action_just_pressed("jump") and is_on_floor() and !crouching:
velocity.y = JUMP_VELOCITY velocity.y = JUMP_VELOCITY
if hunter:
speed_penalty += 2.0
if speed_penalty > SPEED - 1.0:
speed_penalty = SPEED - 1.0
# Crouching
if Input.is_action_just_pressed("crouch") and is_on_floor() and !hunter:
if crouching:
mesh.rotation_degrees.x = 0.0
collision.rotation_degrees.x = 0.0
camera_x.position.y = 0.5
else:
mesh.rotation_degrees.x = -90.0
collision.rotation_degrees.x = -90.0
camera_x.position.y = 0.0
crouching = !crouching
# Movement # Movement
var input_dir: Vector2 = Input.get_vector("move_left", "move_right", "move_forwards", "move_backwards") var input_dir: Vector2 = Input.get_vector("move_left", "move_right", "move_forwards", "move_backwards")
var direction: Vector3 = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() var direction: Vector3 = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
velocity.x = direction.x * SPEED velocity.x = direction.x * (SPEED - speed_penalty)
velocity.z = direction.z * SPEED velocity.z = direction.z * (SPEED - speed_penalty)
# Hunter specific # Hunter specific
if hunter: if hunter:
@ -67,6 +92,11 @@ func _physics_process(delta: float) -> void:
syringe.position.z += 0.1 syringe.position.z += 0.1
syringe.position.z = clampf(syringe.position.z, -0.8, -0.3) syringe.position.z = clampf(syringe.position.z, -0.8, -0.3)
if speed_penalty > 0.0:
speed_penalty -= delta / 3.0
if speed_penalty <= 0.0:
speed_penalty = 0.0
if move_and_slide(): if move_and_slide():
pass pass
@ -127,7 +157,7 @@ func get_imprisoned(coffin_path: NodePath) -> void:
# This is called on any player that has just stabbed/caught another. # This is called on any player that has just stabbed/caught another.
@rpc("any_peer", "call_local", "reliable") @rpc("any_peer", "call_local", "reliable")
func capture_player(player_path: NodePath) -> void: func capture_player(player_path: NodePath) -> void:
if multiplayer.get_remote_sender_id() == 1: if multiplayer.get_remote_sender_id() == 1 and !caught_player:
var player: Player = get_node(player_path) var player: Player = get_node(player_path)
caught_player = player caught_player = player
captured.visible = true captured.visible = true

View file

@ -1,4 +1,4 @@
[gd_scene load_steps=6 format=3 uid="uid://4hmftsrl305n"] [gd_scene load_steps=7 format=3 uid="uid://4hmftsrl305n"]
[ext_resource type="Script" uid="uid://dtlssj0xohnpc" path="res://entities/player.gd" id="1_merdl"] [ext_resource type="Script" uid="uid://dtlssj0xohnpc" path="res://entities/player.gd" id="1_merdl"]
@ -14,6 +14,8 @@ height = 1.8
height = 0.2 height = 0.2
radius = 0.05 radius = 0.05
[sub_resource type="SeparationRayShape3D" id="SeparationRayShape3D_merdl"]
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_merdl"] [sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_merdl"]
properties/0/path = NodePath(".:position") properties/0/path = NodePath(".:position")
properties/0/spawn = true properties/0/spawn = true
@ -30,6 +32,15 @@ properties/3/replication_mode = 1
properties/4/path = NodePath(".:health") properties/4/path = NodePath(".:health")
properties/4/spawn = true properties/4/spawn = true
properties/4/replication_mode = 1 properties/4/replication_mode = 1
properties/5/path = NodePath("Bag/Captured:visible")
properties/5/spawn = true
properties/5/replication_mode = 1
properties/6/path = NodePath("Mesh:rotation")
properties/6/spawn = true
properties/6/replication_mode = 1
properties/7/path = NodePath("Collision:rotation")
properties/7/spawn = true
properties/7/replication_mode = 1
[node name="Player" type="CharacterBody3D"] [node name="Player" type="CharacterBody3D"]
script = ExtResource("1_merdl") script = ExtResource("1_merdl")
@ -68,8 +79,11 @@ radius = 0.3
[node name="ViewX" type="Node3D" parent="ViewY"] [node name="ViewX" type="Node3D" parent="ViewY"]
transform = Transform3D(1, 0, 0, 0, 0.866025, 0.5, 0, -0.5, 0.866025, 0, 0.5, 0) transform = Transform3D(1, 0, 0, 0, 0.866025, 0.5, 0, -0.5, 0.866025, 0, 0.5, 0)
[node name="View" type="Camera3D" parent="ViewY/ViewX"] [node name="Arm" type="SpringArm3D" parent="ViewY/ViewX"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 4) shape = SubResource("SeparationRayShape3D_merdl")
spring_length = 10.0
[node name="View" type="Camera3D" parent="ViewY/ViewX/Arm"]
size = 5.0 size = 5.0
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."] [node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]

View file

@ -9,7 +9,7 @@ custom_features=""
export_filter="all_resources" export_filter="all_resources"
include_filter="" include_filter=""
exclude_filter="" exclude_filter=""
export_path="../../FreeFTF.x86_64" export_path="releases/FreeFTF.x86_64"
patches=PackedStringArray() patches=PackedStringArray()
encryption_include_filters="" encryption_include_filters=""
encryption_exclude_filters="" encryption_exclude_filters=""
@ -39,3 +39,70 @@ unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
rm -rf \"{temp_dir}\"" rm -rf \"{temp_dir}\""
[preset.1]
name="Windows Desktop"
platform="Windows Desktop"
runnable=true
advanced_options=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="releases/FreeFTF.x86_64.exe"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.1.options]
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=1
binary_format/embed_pck=true
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
binary_format/architecture="x86_64"
codesign/enable=false
codesign/timestamp=true
codesign/timestamp_server_url=""
codesign/digest_algorithm=1
codesign/description=""
codesign/custom_options=PackedStringArray()
application/modify_resources=false
application/icon=""
application/console_wrapper_icon=""
application/icon_interpolation=4
application/file_version=""
application/product_version=""
application/company_name=""
application/product_name=""
application/file_description=""
application/copyright=""
application/trademarks=""
application/export_angle=0
application/export_d3d12=0
application/d3d12_agility_sdk_multiarch=true
ssh_remote_deploy/enabled=false
ssh_remote_deploy/host="user@host_ip"
ssh_remote_deploy/port="22"
ssh_remote_deploy/extra_args_ssh=""
ssh_remote_deploy/extra_args_scp=""
ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
$trigger = New-ScheduledTaskTrigger -Once -At 00:00
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
Start-ScheduledTask -TaskName godot_remote_debug
while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
Remove-Item -Recurse -Force '{temp_dir}'"

View file

@ -1,15 +1,16 @@
class_name MultiplayerClass class_name MultiplayerClass
extends Node extends Node
const ADDRESS: String = "127.0.0.1"
const PORT: int = 4516 const PORT: int = 4516
var peer: ENetMultiplayerPeer = null var peer: ENetMultiplayerPeer = null
var players: Array[Dictionary] = [] var players: Dictionary = {}
var fuseboxes: int = 0 var fuseboxes: int = 0
var playerlist_label: Label = null var playerlist_label: Label = null
var entered_username: String = ""
## Host Specific ## Host Specific
# Hosts server # Hosts server
@ -19,39 +20,58 @@ func host_server() -> void:
peer = null peer = null
return return
multiplayer.multiplayer_peer = peer multiplayer.multiplayer_peer = peer
if multiplayer.peer_connected.connect(_on_player_joined) != OK: if multiplayer.peer_connected.connect(_on_player_joined) != OK:
print("Failed to connect signal") print("Failed to connect signal")
if multiplayer.peer_disconnected.connect(_on_player_left) != OK: if multiplayer.peer_disconnected.connect(_on_player_left) != OK:
print("Failed to connect signal") print("Failed to connect signal")
if get_tree().change_scene_to_file("res://ui/lobby.tscn") != OK:
print("Failed to change to scene.")
_on_player_joined(multiplayer.get_unique_id()) _on_player_joined(multiplayer.get_unique_id())
player_username.rpc_id(1, Options.username)
# Runs when player joins # Runs when player joins
func _on_player_joined(id: int) -> void: func _on_player_joined(id: int) -> void:
players.push_back({ players[id] = {
"id": id, "id": id,
"username": "Waiting...",
"loaded": false, "loaded": false,
"hunter": false, "hunter": false,
"captured": false, "captured": false,
"escaped": false, "escaped": false,
}) }
send_playerlist() send_playerlist()
# Runs when player leaves # Runs when player leaves
func _on_player_left(id: int) -> void: func _on_player_left(id: int) -> void:
players = players.filter(func (player: Dictionary) -> bool: return player.id != id) if !players.erase(id):
print("Player was never registered!")
send_playerlist()
# Receives the players username
@rpc("any_peer", "call_local", "reliable")
func player_username(username: String) -> void:
players[multiplayer.get_remote_sender_id()].username = username
send_playerlist() send_playerlist()
# Prepares the label text for the playerlist on the join/host screen # Prepares the label text for the playerlist on the join/host screen
func send_playerlist() -> void: func send_playerlist() -> void:
var playerlist: String = "" var playerlist: String = ""
for player: Dictionary in players: for id: int in players:
playerlist += str(player.id) playerlist += players[id].username
playerlist += "\n" playerlist += "\n"
update_playerlist.rpc(playerlist) update_playerlist.rpc(playerlist)
@rpc("any_peer", "call_local", "reliable")
func request_playerlist() -> void:
if multiplayer.is_server():
send_playerlist()
# Switches the scene, and waits until everyone is fully loaded. # Switches the scene, and waits until everyone is fully loaded.
@rpc("authority", "call_local", "reliable") @rpc("authority", "call_local", "reliable")
func switch_scene(path: String) -> void: func switch_scene(path: String) -> void:
@ -60,8 +80,8 @@ func switch_scene(path: String) -> void:
_scene_loaded.rpc_id(1) _scene_loaded.rpc_id(1)
while true: while true:
var loaded: bool = true var loaded: bool = true
for player: Dictionary in players: for id: int in players:
if player.loaded == false: if players[id].loaded == false:
loaded = false loaded = false
break break
if loaded: if loaded:
@ -77,17 +97,14 @@ func switch_scene(path: String) -> void:
# Callback to switch_scene from the remote peer # Callback to switch_scene from the remote peer
@rpc("any_peer", "call_local", "reliable") @rpc("any_peer", "call_local", "reliable")
func _scene_loaded() -> void: func _scene_loaded() -> void:
var id: int = multiplayer.get_remote_sender_id()
if multiplayer.is_server(): if multiplayer.is_server():
for i: int in range(players.size()): players[multiplayer.get_remote_sender_id()].loaded = true
if players[i].id == multiplayer.get_remote_sender_id():
players[i].loaded = true
func _process(_delta: float) -> void: func _process(_delta: float) -> void:
if multiplayer.multiplayer_peer and multiplayer.is_server() and !players.is_empty(): if multiplayer.multiplayer_peer and multiplayer.is_server() and !players.is_empty():
var lost: bool = true var lost: bool = true
for player: Dictionary in players: for id: int in players:
if (!player.captured or !player.escaped) and !player.hunter: if !(players[id].captured or players[id].escaped) and !players[id].hunter:
lost = false lost = false
if lost: if lost:
terminate_multiplayer.rpc() terminate_multiplayer.rpc()
@ -95,20 +112,42 @@ func _process(_delta: float) -> void:
## Client Specific ## Client Specific
# Joins a server # Joins a server
func join_server() -> void: func join_server(username: String = Options.username, ip_address: String = Options.ip_address) -> void:
if !username:
username = Options.username
if !ip_address:
ip_address = Options.ip_address
entered_username = username
peer = ENetMultiplayerPeer.new() peer = ENetMultiplayerPeer.new()
if peer.create_client(ADDRESS, PORT) != OK: if peer.create_client(ip_address, PORT) != OK:
peer = null terminate_multiplayer()
return return
multiplayer.multiplayer_peer = peer multiplayer.multiplayer_peer = peer
if multiplayer.connected_to_server.connect(_on_server_joined) != OK:
print("Failed to connect signal")
if multiplayer.connection_failed.connect(_on_server_join_failed) != OK:
print("Failed to connect signal")
# After successful join
func _on_server_joined() -> void:
if get_tree().change_scene_to_file("res://ui/lobby.tscn") != OK:
print("Failed to change to scene.")
player_username.rpc_id(1, entered_username)
# After unsuccessful join
func _on_server_join_failed() -> void:
terminate_multiplayer()
## Shared ## Shared
# Terminates all Multiplayer functionality. # Terminates all Multiplayer functionality.
@rpc("authority", "call_local", "reliable") @rpc("authority", "call_local", "reliable")
func terminate_multiplayer() -> void: func terminate_multiplayer() -> void:
multiplayer.multiplayer_peer = null multiplayer.multiplayer_peer = null
players = [] players = {}
fuseboxes = 0 fuseboxes = 0
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
if get_tree().change_scene_to_file("res://ui/main_menu.tscn") != OK: if get_tree().change_scene_to_file("res://ui/main_menu.tscn") != OK:
@ -119,3 +158,13 @@ func terminate_multiplayer() -> void:
func update_playerlist(list: String) -> void: func update_playerlist(list: String) -> void:
if playerlist_label != null: if playerlist_label != null:
playerlist_label.text = list playerlist_label.text = list
# Adds Fusebox
@rpc("authority", "call_local", "reliable")
func add_fusebox() -> void:
fuseboxes += 1
# Removes Fusebox
@rpc("authority", "call_local", "reliable")
func remove_fusebox() -> void:
fuseboxes -= 1

34
global/options.gd Normal file
View file

@ -0,0 +1,34 @@
class_name OptionsClass
extends Node
var username: String = "DefaultUsername"
var ip_address: String = "127.0.0.1"
var data_collection: bool = false
func _ready() -> void:
load_options()
func save_options() -> void:
var save_data: String = JSON.stringify({
"username": username,
"ip_address": ip_address,
"data_collection": data_collection,
})
var file: FileAccess = FileAccess.open("user://options.json", FileAccess.WRITE)
if file and !file.store_string(save_data):
print("Failed to save settings.")
func load_options() -> void:
var file: FileAccess = FileAccess.open("user://options.json", FileAccess.READ)
if file:
var save_data: Dictionary = JSON.parse_string(file.get_as_text())
if save_data:
if save_data.has("username"):
username = save_data.username
if save_data.has("ip_address"):
ip_address = save_data.ip_address
if save_data.has("data_collection"):
data_collection = save_data.data_collection

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

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

242
maps/assets/map.gltf Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,37 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bago2uuxpae22"
path="res://.godot/imported/map.gltf-c3a1ee560168fb25b24f2e9bfd0cd850.scn"
[deps]
source_file="res://maps/assets/map.gltf"
dest_files=["res://.godot/imported/map.gltf-c3a1ee560168fb25b24f2e9bfd0cd850.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1

BIN
maps/assets/map_0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 B

View file

@ -0,0 +1,38 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bv1exi2cc5vji"
path.s3tc="res://.godot/imported/map_0.png-bde32f7cbb719c1cd3adb7b4e7780a34.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
generator_parameters={
"md5": "28ac435753fa790c09d222ee0f776064"
}
[deps]
source_file="res://maps/assets/map_0.png"
dest_files=["res://.godot/imported/map_0.png-bde32f7cbb719c1cd3adb7b4e7780a34.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

BIN
maps/assets/test_map.exr Normal file

Binary file not shown.

View file

@ -0,0 +1,27 @@
[remap]
importer="2d_array_texture"
type="CompressedTexture2DArray"
uid="uid://bh7jk5uhw4bqg"
path.bptc="res://.godot/imported/test_map.exr-8ee8e6411c22dfab2a271ee543ced25b.bptc.ctexarray"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://maps/assets/test_map.exr"
dest_files=["res://.godot/imported/test_map.exr-8ee8e6411c22dfab2a271ee543ced25b.bptc.ctexarray"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/channel_pack=1
mipmaps/generate=false
mipmaps/limit=-1
slices/horizontal=1
slices/vertical=2

BIN
maps/assets/test_map.lmbake Normal file

Binary file not shown.

Binary file not shown.

View file

@ -4,9 +4,25 @@ extends Node3D
func _ready() -> void: func _ready() -> void:
if multiplayer.is_server(): if multiplayer.is_server():
randomize() randomize()
Multiplayer.players[randi_range(0, Multiplayer.players.size() - 1)].hunter = true var random_id: int = Multiplayer.players.keys()[randi_range(0, Multiplayer.players.size() - 1)]
Multiplayer.players[random_id].hunter = true
for i: int in $PlayerSpawners.get_children().size(): for i: int in $PlayerSpawners.get_children().size():
if i < Multiplayer.players.size(): if i < Multiplayer.players.size():
var spawner: PlayerSpawner = $PlayerSpawners.get_child(i) var spawner: PlayerSpawner = $PlayerSpawners.get_child(i)
var player: Dictionary = Multiplayer.players[i] var player: Dictionary = Multiplayer.players[Multiplayer.players.keys()[i]]
spawner.spawn_player(player) spawner.spawn_player(player)
func _on_exit_entered(body: Node3D) -> void:
if multiplayer.is_server() and (body is Player):
var player: Player = body
if !player.hunter and Multiplayer.fuseboxes <= 0:
Multiplayer.players[player.get_multiplayer_authority()].escaped = true
player.get_caught.rpc_id(player.get_multiplayer_authority(), $SpectatorCamera.get_path())
func _process(_delta: float) -> void:
if Multiplayer.fuseboxes <= 0:
(($EscapeArea/Light as CSGBox3D).material as StandardMaterial3D).albedo_color.r8 = 0
(($EscapeArea/Light as CSGBox3D).material as StandardMaterial3D).albedo_color.g8 = 255
else:
(($EscapeArea/Light as CSGBox3D).material as StandardMaterial3D).albedo_color.r8 = 255
(($EscapeArea/Light as CSGBox3D).material as StandardMaterial3D).albedo_color.g8 = 0

File diff suppressed because one or more lines are too long

View file

@ -19,6 +19,7 @@ boot_splash/show_image=false
[autoload] [autoload]
Multiplayer="*res://global/multiplayer.gd" Multiplayer="*res://global/multiplayer.gd"
Options="*res://global/options.gd"
[debug] [debug]
@ -125,6 +126,11 @@ focus={
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null)
] ]
} }
crouch={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":67,"key_label":0,"unicode":99,"location":0,"echo":false,"script":null)
]
}
[rendering] [rendering]

View file

@ -1,5 +0,0 @@
class_name HostGame
extends JoinGame
func _on_start_game_button_pressed() -> void:
Multiplayer.switch_scene.rpc("res://maps/test_map.tscn")

View file

@ -1,5 +1,10 @@
class_name JoinGame class_name JoinGame
extends Control extends Control
func _ready() -> void: @onready var options_list: VBoxContainer = $OptionsList
Multiplayer.playerlist_label = $PlayerList @onready var username_input: LineEdit = $OptionsList/UsernameOption/OptionInput
@onready var ip_address_input: LineEdit = $OptionsList/IpAddressOption/OptionInput
func _on_join_button_pressed() -> void:
options_list.visible = false
Multiplayer.join_server(username_input.text, ip_address_input.text)

View file

@ -12,8 +12,56 @@ grow_vertical = 2
script = ExtResource("1_ju2tq") script = ExtResource("1_ju2tq")
[node name="PlayerList" type="Label" parent="."] [node name="PlayerList" type="Label" parent="."]
visible = false
layout_mode = 1 layout_mode = 1
anchors_preset = 9 anchors_preset = 9
anchor_bottom = 1.0 anchor_bottom = 1.0
offset_right = 1.0 offset_right = 1.0
grow_vertical = 2 grow_vertical = 2
text = "Connecting to the server..."
[node name="OptionsList" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -20.0
offset_top = -20.0
offset_right = 20.0
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2
[node name="UsernameOption" type="HBoxContainer" parent="OptionsList"]
layout_mode = 2
size_flags_horizontal = 8
[node name="OptionName" type="Label" parent="OptionsList/UsernameOption"]
layout_mode = 2
text = "Username:"
[node name="OptionInput" type="LineEdit" parent="OptionsList/UsernameOption"]
custom_minimum_size = Vector2(175, 0)
layout_mode = 2
max_length = 20
[node name="IpAddressOption" type="HBoxContainer" parent="OptionsList"]
layout_mode = 2
size_flags_horizontal = 8
[node name="OptionName" type="Label" parent="OptionsList/IpAddressOption"]
layout_mode = 2
text = "IP Address:"
[node name="OptionInput" type="LineEdit" parent="OptionsList/IpAddressOption"]
custom_minimum_size = Vector2(175, 0)
layout_mode = 2
max_length = 20
[node name="JoinButton" type="Button" parent="OptionsList"]
layout_mode = 2
text = "Join Server"
[connection signal="pressed" from="OptionsList/JoinButton" to="." method="_on_join_button_pressed"]

15
ui/lobby.gd Normal file
View file

@ -0,0 +1,15 @@
class_name HostGame
extends JoinGame
@onready var start_button: Button = $StartGameButton
func _ready() -> void:
Multiplayer.playerlist_label = $PlayerList
if multiplayer.is_server():
start_button.disabled = false
start_button.visible = true
Multiplayer.request_playerlist.rpc_id(1)
func _on_start_game_button_pressed() -> void:
Multiplayer.switch_scene.rpc("res://maps/test_map.tscn")

View file

@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://corjvfihg047u"] [gd_scene load_steps=2 format=3 uid="uid://corjvfihg047u"]
[ext_resource type="Script" uid="uid://cuumy3t2hkgcu" path="res://ui/host_game.gd" id="1_fg6en"] [ext_resource type="Script" uid="uid://cuumy3t2hkgcu" path="res://ui/lobby.gd" id="1_nmq56"]
[node name="HostGame" type="Control"] [node name="HostGame" type="Control"]
layout_mode = 3 layout_mode = 3
@ -9,9 +9,10 @@ anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
script = ExtResource("1_fg6en") script = ExtResource("1_nmq56")
[node name="StartGameButton" type="Button" parent="."] [node name="StartGameButton" type="Button" parent="."]
visible = false
layout_mode = 1 layout_mode = 1
anchors_preset = 8 anchors_preset = 8
anchor_left = 0.5 anchor_left = 0.5
@ -24,6 +25,7 @@ offset_right = 48.0
offset_bottom = 15.5 offset_bottom = 15.5
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
disabled = true
text = "Start Game" text = "Start Game"
[node name="PlayerList" type="Label" parent="."] [node name="PlayerList" type="Label" parent="."]
@ -32,6 +34,5 @@ anchors_preset = 9
anchor_bottom = 1.0 anchor_bottom = 1.0
offset_right = 1.0 offset_right = 1.0
grow_vertical = 2 grow_vertical = 2
text = "1"
[connection signal="pressed" from="StartGameButton" to="." method="_on_start_game_button_pressed"] [connection signal="pressed" from="StartGameButton" to="." method="_on_start_game_button_pressed"]

View file

@ -3,10 +3,12 @@ extends Control
func _on_host_game_button_pressed() -> void: func _on_host_game_button_pressed() -> void:
Multiplayer.host_server() Multiplayer.host_server()
if get_tree().change_scene_to_file("res://ui/host_game.tscn") != OK:
print("Failed to change to scene.")
func _on_join_game_button_pressed() -> void: func _on_join_game_button_pressed() -> void:
Multiplayer.join_server()
if get_tree().change_scene_to_file("res://ui/join_game.tscn") != OK: if get_tree().change_scene_to_file("res://ui/join_game.tscn") != OK:
print("Failed to change to scene.") print("Failed to change to scene.")
func _on_options_button_pressed() -> void:
if get_tree().change_scene_to_file("res://ui/options_menu.tscn") != OK:
print("Failed to change to scene.")

View file

@ -38,5 +38,23 @@ text = "Host Game"
layout_mode = 2 layout_mode = 2
text = "Join Game" text = "Join Game"
[node name="OptionsButton" type="Button" parent="MainContainer"]
layout_mode = 2
text = "Options"
[node name="Version" type="Label" parent="."]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -40.0
offset_top = -23.0
grow_horizontal = 0
grow_vertical = 0
text = "0.1.0"
[connection signal="pressed" from="MainContainer/HostGameButton" to="." method="_on_host_game_button_pressed"] [connection signal="pressed" from="MainContainer/HostGameButton" to="." method="_on_host_game_button_pressed"]
[connection signal="pressed" from="MainContainer/JoinGameButton" to="." method="_on_join_game_button_pressed"] [connection signal="pressed" from="MainContainer/JoinGameButton" to="." method="_on_join_game_button_pressed"]
[connection signal="pressed" from="MainContainer/OptionsButton" to="." method="_on_options_button_pressed"]

27
ui/options_menu.gd Normal file
View file

@ -0,0 +1,27 @@
class_name OptionsMenu
extends Control
@onready var username_input: LineEdit = $OptionsList/UsernameOption/OptionInput
@onready var ip_address_input: LineEdit = $OptionsList/IpAddressOption/OptionInput
@onready var data_collection_input: CheckBox = $OptionsList/DataCollectionOption/OptionInput
func _ready() -> void:
username_input.text = Options.username
ip_address_input.text = Options.ip_address
data_collection_input.button_pressed = Options.data_collection
func _username_changed(text: String) -> void:
Options.username = text
Options.save_options()
func _ip_address_changed(text: String) -> void:
Options.ip_address = text
Options.save_options()
func _data_collection_changed(new_option: bool) -> void:
Options.data_collection = new_option
Options.save_options()
func _main_menu() -> void:
if get_tree().change_scene_to_file("res://ui/main_menu.tscn") != OK:
print("Failed to change to scene.")

1
ui/options_menu.gd.uid Normal file
View file

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

78
ui/options_menu.tscn Normal file
View file

@ -0,0 +1,78 @@
[gd_scene load_steps=2 format=3 uid="uid://bxeejl1m7w5mn"]
[ext_resource type="Script" uid="uid://fpohmcdqnk35" path="res://ui/options_menu.gd" id="1_ibk6l"]
[node name="OptionsMenu" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_ibk6l")
[node name="OptionsList" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 13
anchor_left = 0.5
anchor_right = 0.5
anchor_bottom = 1.0
offset_left = -78.5
offset_right = 78.5
grow_horizontal = 2
grow_vertical = 2
[node name="UsernameOption" type="HBoxContainer" parent="OptionsList"]
layout_mode = 2
size_flags_horizontal = 8
[node name="OptionName" type="Label" parent="OptionsList/UsernameOption"]
layout_mode = 2
text = "Username:"
[node name="OptionInput" type="LineEdit" parent="OptionsList/UsernameOption"]
custom_minimum_size = Vector2(175, 0)
layout_mode = 2
max_length = 20
[node name="IpAddressOption" type="HBoxContainer" parent="OptionsList"]
layout_mode = 2
size_flags_horizontal = 8
[node name="OptionName" type="Label" parent="OptionsList/IpAddressOption"]
layout_mode = 2
text = "IP Address:"
[node name="OptionInput" type="LineEdit" parent="OptionsList/IpAddressOption"]
custom_minimum_size = Vector2(175, 0)
layout_mode = 2
max_length = 20
[node name="DataCollectionOption" type="HBoxContainer" parent="OptionsList"]
layout_mode = 2
size_flags_horizontal = 8
[node name="OptionName" type="Label" parent="OptionsList/DataCollectionOption"]
layout_mode = 2
text = "Server data collection allowed:"
[node name="OptionInput" type="CheckBox" parent="OptionsList/DataCollectionOption"]
layout_mode = 2
[node name="Button" type="Button" parent="."]
layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = -157.0
offset_top = -31.0
grow_horizontal = 0
grow_vertical = 0
text = "Back to Main Menu"
[connection signal="text_changed" from="OptionsList/UsernameOption/OptionInput" to="." method="_username_changed"]
[connection signal="text_changed" from="OptionsList/IpAddressOption/OptionInput" to="." method="_ip_address_changed"]
[connection signal="toggled" from="OptionsList/DataCollectionOption/OptionInput" to="." method="_data_collection_changed"]
[connection signal="pressed" from="Button" to="." method="_main_menu"]