This commit is contained in:
Patrick_Pluto 2025-07-18 19:32:37 +01:00
commit fadea22b9d
26 changed files with 741 additions and 0 deletions

4
.editorconfig Normal file
View file

@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

3
.gitignore vendored Normal file
View file

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

22
entities/coffin.gd Normal file
View file

@ -0,0 +1,22 @@
class_name Coffin
extends StaticBody3D
var caught_player: Player = null
func _on_dropoff(body: Node3D) -> void:
if multiplayer.is_server() and body is Player:
var player: Player = body
if player.caught_player and !caught_player:
caught_player = player.caught_player
player.caught_player.get_imprisoned.rpc_id(player.caught_player.get_multiplayer_authority(), get_path())
player.released_player.rpc_id(player.get_multiplayer_authority())
(player.get_node("ReleaseTimer") as Timer).stop()
($DamageTimer as Timer).start()
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 = null
($DamageTimer as Timer).stop()
func _on_damage_timer_timeout() -> void:
if multiplayer.is_server() and caught_player.health > 0:
caught_player.damage_player.rpc_id(caught_player.get_multiplayer_authority())

1
entities/coffin.gd.uid Normal file
View file

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

32
entities/coffin.tscn Normal file
View file

@ -0,0 +1,32 @@
[gd_scene load_steps=4 format=3 uid="uid://dx0yaqaccakh"]
[ext_resource type="Script" uid="uid://oaxiyitfsq2q" path="res://entities/coffin.gd" id="1_0x4dk"]
[sub_resource type="BoxShape3D" id="BoxShape3D_0x4dk"]
size = Vector3(3, 1.2, 1.4)
[sub_resource type="BoxShape3D" id="BoxShape3D_cvqjw"]
size = Vector3(4, 1.4, 2.4)
[node name="Coffin" type="StaticBody3D"]
script = ExtResource("1_0x4dk")
[node name="Mesh" type="CSGBox3D" parent="."]
size = Vector3(3, 1.2, 1.4)
[node name="Collision" type="CollisionShape3D" parent="."]
shape = SubResource("BoxShape3D_0x4dk")
[node name="DropoffZone" type="Area3D" parent="."]
[node name="Collision" type="CollisionShape3D" parent="DropoffZone"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0)
shape = SubResource("BoxShape3D_cvqjw")
[node name="DamageTimer" type="Timer" parent="."]
[node name="RespawnPoint" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0.75, 0)
[connection signal="body_entered" from="DropoffZone" to="." method="_on_dropoff"]
[connection signal="timeout" from="DamageTimer" to="." method="_on_damage_timer_timeout"]

147
entities/player.gd Normal file
View file

@ -0,0 +1,147 @@
class_name Player
extends CharacterBody3D
const SPEED: float = 5.0
const JUMP_VELOCITY: float = 4.5
@onready var camera_x: Node3D = $ViewY/ViewX
@onready var camera: Camera3D = $ViewY/ViewX/View
@onready var syringe: Node3D = $Syringe
@onready var needle: Area3D = $Syringe/SyringeNeedle
@onready var bag: Node3D = $Bag
@onready var captured: Node3D = $Bag/Captured
var hunter: bool = true
var caught_player: Player = null
var new_spawn: Vector3
@export var health: int = 100
func _ready() -> void:
if is_multiplayer_authority():
camera.current = true
($HUD as Control).visible = true
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
if hunter:
syringe.visible = true
bag.visible = true
else:
needle.monitoring = false
func _physics_process(delta: float) -> void:
# Zoom
if Input.is_action_just_released("zoom_in") and camera.position.z > 0:
camera.position.z += -1
elif Input.is_action_just_released("zoom_out"):
camera.position.z += 1
if is_multiplayer_authority():
# Focus
if Input.is_action_just_pressed("unfocus"):
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
elif Input.is_action_just_pressed("focus"):
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
pass
if visible:
# Gravity
if not is_on_floor():
velocity += get_gravity() * delta
# Jumping
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Movement
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()
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
# Hunter specific
if hunter:
if Input.is_action_pressed("stab"):
syringe.position.z -= 0.1
else:
syringe.position.z += 0.1
syringe.position.z = clampf(syringe.position.z, -0.8, -0.3)
if move_and_slide():
pass
func _unhandled_input(event: InputEvent) -> void:
# Camera Movement
if event is InputEventMouseMotion:
var mouse_event: InputEventMouseMotion = event
if is_multiplayer_authority():
rotate_y(deg_to_rad(-mouse_event.relative.x))
else:
($ViewY as Node3D).rotate_y(deg_to_rad(-mouse_event.relative.x))
camera_x.rotate_x(deg_to_rad(-mouse_event.relative.y))
camera_x.rotation.x = clampf(camera_x.rotation.x, -1.6, 1.6)
# Gets called when the Player gets stabbed by the syringe.
func _on_stab(body: Node3D) -> void:
if multiplayer.is_server() and body is Player and body.visible == true:
var player: Player = body
caught_player = player
player.get_caught.rpc_id(player.get_multiplayer_authority(), camera.get_path())
capture_player.rpc_id(get_multiplayer_authority(), player.get_path())
($ReleaseTimer as Timer).start()
# Player gets released through the timer
func _on_release() -> void:
if multiplayer.is_server():
caught_player.go_free.rpc_id(caught_player.get_multiplayer_authority())
released_player.rpc_id(get_multiplayer_authority())
# This is called on any player that was just stabbed/caught.
@rpc("any_peer", "call_local", "reliable")
func get_caught(spectator_camera_path: NodePath) -> void:
if multiplayer.get_remote_sender_id() == 1:
var spectator_camera: Camera3D = get_node(spectator_camera_path)
visible = false
($Collision as CollisionShape3D).disabled = true
spectator_camera.current = true
new_spawn = position
# This is called on any player when they go free.
@rpc("any_peer", "call_local", "reliable")
func go_free() -> void:
if multiplayer.get_remote_sender_id() == 1:
camera.current = true
visible = true
($Collision as CollisionShape3D).disabled = false
position = new_spawn
# This is called on any player when they go free.
@rpc("any_peer", "call_local", "reliable")
func get_imprisoned(coffin_path: NodePath) -> void:
if multiplayer.get_remote_sender_id() == 1:
var coffin: Coffin = get_node(coffin_path)
camera.current = true
position = coffin.position
new_spawn = (coffin.get_node("RespawnPoint") as Node3D).position
# This is called on any player that has just stabbed/caught another.
@rpc("any_peer", "call_local", "reliable")
func capture_player(player_path: NodePath) -> void:
if multiplayer.get_remote_sender_id() == 1:
var player: Player = get_node(player_path)
caught_player = player
captured.visible = true
# This is called on any player that was just stabbed/caught.
@rpc("any_peer", "call_local", "reliable")
func released_player() -> void:
if multiplayer.get_remote_sender_id() == 1:
caught_player = null
captured.visible = false
# Damages the player
@rpc("any_peer", "call_local", "reliable")
func damage_player() -> void:
if multiplayer.get_remote_sender_id() == 1:
health -= 2
($HUD/Health as Label).text = "Health: " + str(health)

1
entities/player.gd.uid Normal file
View file

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

99
entities/player.tscn Normal file
View file

@ -0,0 +1,99 @@
[gd_scene load_steps=6 format=3 uid="uid://4hmftsrl305n"]
[ext_resource type="Script" uid="uid://dtlssj0xohnpc" path="res://entities/player.gd" id="1_merdl"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_u3f3p"]
radius = 0.4
height = 1.8
[sub_resource type="CapsuleMesh" id="CapsuleMesh_merdl"]
radius = 0.4
height = 1.8
[sub_resource type="CylinderShape3D" id="CylinderShape3D_merdl"]
height = 0.2
radius = 0.05
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_merdl"]
properties/0/path = NodePath(".:position")
properties/0/spawn = true
properties/0/replication_mode = 1
properties/1/path = NodePath(".:rotation")
properties/1/spawn = true
properties/1/replication_mode = 1
properties/2/path = NodePath("Syringe:position")
properties/2/spawn = true
properties/2/replication_mode = 1
properties/3/path = NodePath(".:visible")
properties/3/spawn = true
properties/3/replication_mode = 1
properties/4/path = NodePath(".:health")
properties/4/spawn = true
properties/4/replication_mode = 1
[node name="Player" type="CharacterBody3D"]
script = ExtResource("1_merdl")
[node name="Collision" type="CollisionShape3D" parent="."]
shape = SubResource("CapsuleShape3D_u3f3p")
[node name="Mesh" type="MeshInstance3D" parent="."]
mesh = SubResource("CapsuleMesh_merdl")
[node name="Syringe" type="CSGCylinder3D" parent="."]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0.4, 0, -0.3)
visible = false
radius = 0.05
height = 1.0
[node name="SyringeNeedle" type="Area3D" parent="Syringe"]
[node name="Collision" type="CollisionShape3D" parent="Syringe/SyringeNeedle"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4, 0)
shape = SubResource("CylinderShape3D_merdl")
[node name="Bag" type="CSGCylinder3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.8)
visible = false
radius = 0.4
height = 1.0
[node name="Captured" type="CSGSphere3D" parent="Bag"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
visible = false
radius = 0.3
[node name="ViewY" type="Node3D" parent="."]
[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)
[node name="View" type="Camera3D" parent="ViewY/ViewX"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 4)
size = 5.0
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
replication_config = SubResource("SceneReplicationConfig_merdl")
[node name="ReleaseTimer" type="Timer" parent="."]
wait_time = 30.0
one_shot = true
[node name="HUD" type="Control" parent="."]
visible = false
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 1
[node name="Health" type="Label" parent="HUD"]
layout_mode = 1
offset_right = 88.0
offset_bottom = 23.0
text = "Health: 100"
[connection signal="body_entered" from="Syringe/SyringeNeedle" to="." method="_on_stab"]
[connection signal="timeout" from="ReleaseTimer" to="." method="_on_release"]

View file

@ -0,0 +1,21 @@
class_name PlayerSpawner
extends Node3D
const PLAYER_SCENE: PackedScene = preload("res://entities/player.tscn")
func _spawn_player(player: Dictionary) -> CharacterBody3D:
var player_instance: Player = PLAYER_SCENE.instantiate()
var id: int = player.id
player_instance.position = position
player_instance.hunter = player.hunter
player_instance.set_multiplayer_authority(id)
return player_instance
func _ready() -> void:
if $MultiplayerSpawner is MultiplayerSpawner:
($MultiplayerSpawner as MultiplayerSpawner).spawn_function = _spawn_player
func spawn_player(player: Dictionary) -> void:
if $MultiplayerSpawner is MultiplayerSpawner:
if ($MultiplayerSpawner as MultiplayerSpawner).spawn(player) == null:
print("Failed to spawn player.")

View file

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

View file

@ -0,0 +1,9 @@
[gd_scene load_steps=2 format=3 uid="uid://cu1d6uwha7jm2"]
[ext_resource type="Script" uid="uid://rhk1kmfu2hjo" path="res://entities/player_spawner.gd" id="1_q56n2"]
[node name="PlayerSpawner" type="Node3D"]
script = ExtResource("1_q56n2")
[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."]
spawn_path = NodePath("../..")

41
export_presets.cfg Normal file
View file

@ -0,0 +1,41 @@
[preset.0]
name="Linux"
platform="Linux"
runnable=true
advanced_options=false
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../FreeFTF.x86_64"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.0.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"
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="#!/usr/bin/env bash
export DISPLAY=:0
unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
\"{temp_dir}/{exe_name}\" {cmd_args}"
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
rm -rf \"{temp_dir}\""

81
global/multiplayer.gd Normal file
View file

@ -0,0 +1,81 @@
class_name MultiplayerClass
extends Node
const ADDRESS: String = "127.0.0.1"
const PORT: int = 4516
var peer: ENetMultiplayerPeer = null
var players: Array = []
## Host Specific
# Hosts server
func host_server() -> void:
peer = ENetMultiplayerPeer.new()
if peer.create_server(PORT) != OK:
peer = null
return
multiplayer.multiplayer_peer = peer
if multiplayer.peer_connected.connect(_on_player_joined) != OK:
print("Failed to connect signal")
if multiplayer.peer_disconnected.connect(_on_player_left) != OK:
print("Failed to connect signal")
_on_player_joined(multiplayer.get_unique_id())
# Runs when player joins
func _on_player_joined(id: int) -> void:
players.push_back({
"id": id,
"loaded": false,
"hunter": false,
})
# Runs when player leaves
func _on_player_left(id: int) -> void:
players = players.filter(func (player: Dictionary) -> bool: return player.id != id)
# Switches the scene, and waits until everyone is fully loaded.
@rpc("authority", "call_local", "reliable")
func switch_scene(path: String) -> void:
if multiplayer.is_server():
_scene_loaded.rpc_id(1)
while true:
var loaded: bool = true
for player: Dictionary in players:
if player.loaded == false:
loaded = false
break
if loaded:
break
await get_tree().create_timer(0.1).timeout
if get_tree().change_scene_to_file(path) != OK:
print("Failed to change to scene.")
else:
if get_tree().change_scene_to_file(path) != OK:
print("Failed to change to scene.")
_scene_loaded.rpc_id(1)
# Callback to switch_scene from the remote peer
@rpc("any_peer", "call_local", "reliable")
func _scene_loaded() -> void:
var id: int = multiplayer.get_remote_sender_id()
if multiplayer.is_server():
for i: int in range(players.size()):
if players[i].id == multiplayer.get_remote_sender_id():
players[i].loaded = true
## Client Specific
# Joins a server
func join_server() -> void:
peer = ENetMultiplayerPeer.new()
if peer.create_client(ADDRESS, PORT) != OK:
peer = null
return
multiplayer.multiplayer_peer = peer
## Shared
# Terminates all Multiplayer functionality.
func terminate_multiplayer() -> void:
multiplayer.multiplayer_peer = null

View file

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

12
maps/map.gd Normal file
View file

@ -0,0 +1,12 @@
class_name Map
extends Node3D
func _ready() -> void:
if multiplayer.is_server():
randomize()
Multiplayer.players[randi_range(0, Multiplayer.players.size() - 1)].hunter = true
for i: int in $PlayerSpawners.get_children().size():
if i < Multiplayer.players.size():
var spawner: PlayerSpawner = $PlayerSpawners.get_child(i)
var player: Dictionary = Multiplayer.players[i]
spawner.spawn_player(player)

1
maps/map.gd.uid Normal file
View file

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

33
maps/test_map.tscn Normal file
View file

@ -0,0 +1,33 @@
[gd_scene load_steps=4 format=3 uid="uid://dn53itmhhlru8"]
[ext_resource type="Script" uid="uid://btt55t6akawwc" path="res://maps/map.gd" id="1_6slyc"]
[ext_resource type="PackedScene" uid="uid://cu1d6uwha7jm2" path="res://entities/player_spawner.tscn" id="2_b71ab"]
[ext_resource type="PackedScene" uid="uid://dx0yaqaccakh" path="res://entities/coffin.tscn" id="3_pwco0"]
[node name="TestMap" type="Node3D"]
script = ExtResource("1_6slyc")
[node name="PlayerSpawners" type="Node3D" parent="."]
[node name="0" parent="PlayerSpawners" instance=ExtResource("2_b71ab")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10, 3, 0)
[node name="1" parent="PlayerSpawners" instance=ExtResource("2_b71ab")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10, 3, 0)
[node name="2" parent="PlayerSpawners" instance=ExtResource("2_b71ab")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, 10)
[node name="3" parent="PlayerSpawners" instance=ExtResource("2_b71ab")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3, -10)
[node name="Map" type="CSGBox3D" parent="."]
use_collision = true
size = Vector3(25, 1, 25)
[node name="Sky" type="DirectionalLight3D" parent="."]
transform = Transform3D(0.866025, 0.433012, -0.25, 0, 0.5, 0.866025, 0.5, -0.75, 0.433013, 0, 0, 0)
shadow_enabled = true
[node name="Coffin" parent="." instance=ExtResource("3_pwco0")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.1, 0)

131
project.godot Normal file
View file

@ -0,0 +1,131 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[application]
config/name="FreeFTF"
run/main_scene="uid://dbap0wfqfu12h"
config/features=PackedStringArray("4.4", "Forward Plus")
boot_splash/bg_color=Color(0, 0, 0, 1)
boot_splash/show_image=false
[autoload]
Multiplayer="*res://global/multiplayer.gd"
[debug]
gdscript/warnings/unassigned_variable=2
gdscript/warnings/unassigned_variable_op_assign=2
gdscript/warnings/unused_variable=2
gdscript/warnings/unused_local_constant=2
gdscript/warnings/unused_private_class_variable=2
gdscript/warnings/unused_parameter=2
gdscript/warnings/unused_signal=2
gdscript/warnings/shadowed_variable=2
gdscript/warnings/shadowed_variable_base_class=2
gdscript/warnings/shadowed_global_identifier=2
gdscript/warnings/unreachable_code=2
gdscript/warnings/unreachable_pattern=2
gdscript/warnings/standalone_expression=2
gdscript/warnings/standalone_ternary=2
gdscript/warnings/incompatible_ternary=2
gdscript/warnings/untyped_declaration=2
gdscript/warnings/inferred_declaration=2
gdscript/warnings/unsafe_property_access=2
gdscript/warnings/unsafe_method_access=2
gdscript/warnings/unsafe_cast=2
gdscript/warnings/unsafe_call_argument=2
gdscript/warnings/unsafe_void_return=2
gdscript/warnings/return_value_discarded=2
gdscript/warnings/static_called_on_instance=2
gdscript/warnings/missing_tool=2
gdscript/warnings/redundant_static_unload=2
gdscript/warnings/redundant_await=2
gdscript/warnings/assert_always_true=2
gdscript/warnings/assert_always_false=2
gdscript/warnings/integer_division=2
gdscript/warnings/narrowing_conversion=2
gdscript/warnings/int_as_enum_without_cast=2
gdscript/warnings/int_as_enum_without_match=2
gdscript/warnings/enum_variable_without_default=2
gdscript/warnings/empty_file=2
gdscript/warnings/deprecated_keyword=2
gdscript/warnings/confusable_identifier=2
gdscript/warnings/confusable_local_declaration=2
gdscript/warnings/confusable_local_usage=2
gdscript/warnings/confusable_capture_reassignment=2
gdscript/warnings/property_used_as_function=2
gdscript/warnings/constant_used_as_function=2
gdscript/warnings/function_used_as_property=2
[display]
window/size/viewport_width=640
window/size/viewport_height=480
window/stretch/mode="canvas_items"
window/stretch/aspect="expand"
[input]
move_left={
"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":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
]
}
move_right={
"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":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
]
}
move_forwards={
"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":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
]
}
move_backwards={
"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":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
]
}
jump={
"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":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
]
}
zoom_in={
"deadzone": 0.2,
"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":4,"canceled":false,"pressed":false,"double_click":false,"script":null)
]
}
zoom_out={
"deadzone": 0.2,
"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":5,"canceled":false,"pressed":false,"double_click":false,"script":null)
]
}
stab={
"deadzone": 0.2,
"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)
]
}
unfocus={
"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":4194305,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
focus={
"deadzone": 0.2,
"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)
]
}
[rendering]
environment/defaults/default_clear_color=Color(0, 0, 0, 1)

5
ui/host_game.gd Normal file
View file

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

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

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

29
ui/host_game.tscn Normal file
View file

@ -0,0 +1,29 @@
[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"]
[node name="HostGame" 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_fg6en")
[node name="StartGameButton" type="Button" 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 = -48.0
offset_top = -15.5
offset_right = 48.0
offset_bottom = 15.5
grow_horizontal = 2
grow_vertical = 2
text = "Start Game"
[connection signal="pressed" from="StartGameButton" to="." method="_on_start_game_button_pressed"]

9
ui/join_game.tscn Normal file
View file

@ -0,0 +1,9 @@
[gd_scene format=3 uid="uid://dpxam8lwgsd6q"]
[node name="JoinGame" type="Control"]
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2

12
ui/main_menu.gd Normal file
View file

@ -0,0 +1,12 @@
class_name MainMenu
extends Control
func _on_host_game_button_pressed() -> void:
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:
Multiplayer.join_server()
if get_tree().change_scene_to_file("res://ui/join_game.tscn") != OK:
print("Failed to change to scene.")

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

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

42
ui/main_menu.tscn Normal file
View file

@ -0,0 +1,42 @@
[gd_scene load_steps=2 format=3 uid="uid://dbap0wfqfu12h"]
[ext_resource type="Script" uid="uid://d1s1dnvvro0ij" path="res://ui/main_menu.gd" id="1_t7e0r"]
[node name="MainMenu" 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_t7e0r")
[node name="MainContainer" 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 = -47.0
offset_top = -33.0
offset_right = 47.0
offset_bottom = 33.0
grow_horizontal = 2
grow_vertical = 2
[node name="Label" type="Label" parent="MainContainer"]
layout_mode = 2
text = "FreeFTF"
horizontal_alignment = 1
[node name="HostGameButton" type="Button" parent="MainContainer"]
layout_mode = 2
text = "Host Game"
[node name="JoinGameButton" type="Button" parent="MainContainer"]
layout_mode = 2
text = "Join Game"
[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"]