0.0.1
This commit is contained in:
commit
fadea22b9d
26 changed files with 741 additions and 0 deletions
4
.editorconfig
Normal file
4
.editorconfig
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Normalize EOL for all files that Git considers text files.
|
||||||
|
* text=auto eol=lf
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Godot 4+ specific ignores
|
||||||
|
.godot/
|
||||||
|
/android/
|
22
entities/coffin.gd
Normal file
22
entities/coffin.gd
Normal 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
1
entities/coffin.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://oaxiyitfsq2q
|
32
entities/coffin.tscn
Normal file
32
entities/coffin.tscn
Normal 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
147
entities/player.gd
Normal 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
1
entities/player.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://dtlssj0xohnpc
|
99
entities/player.tscn
Normal file
99
entities/player.tscn
Normal 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"]
|
21
entities/player_spawner.gd
Normal file
21
entities/player_spawner.gd
Normal 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.")
|
1
entities/player_spawner.gd.uid
Normal file
1
entities/player_spawner.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://rhk1kmfu2hjo
|
9
entities/player_spawner.tscn
Normal file
9
entities/player_spawner.tscn
Normal 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
41
export_presets.cfg
Normal 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
81
global/multiplayer.gd
Normal 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
|
1
global/multiplayer.gd.uid
Normal file
1
global/multiplayer.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://dabrayl0y4v2g
|
12
maps/map.gd
Normal file
12
maps/map.gd
Normal 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
1
maps/map.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://btt55t6akawwc
|
33
maps/test_map.tscn
Normal file
33
maps/test_map.tscn
Normal 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
131
project.godot
Normal 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
5
ui/host_game.gd
Normal 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
1
ui/host_game.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://cuumy3t2hkgcu
|
29
ui/host_game.tscn
Normal file
29
ui/host_game.tscn
Normal 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
9
ui/join_game.tscn
Normal 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
12
ui/main_menu.gd
Normal 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
1
ui/main_menu.gd.uid
Normal file
|
@ -0,0 +1 @@
|
||||||
|
uid://d1s1dnvvro0ij
|
42
ui/main_menu.tscn
Normal file
42
ui/main_menu.tscn
Normal 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"]
|
Loading…
Add table
Add a link
Reference in a new issue