From 0dc6247f9152cf52d47ca6a77e605594d10b11fc Mon Sep 17 00:00:00 2001 From: Rendo Date: Sat, 22 Nov 2025 01:07:18 +0500 Subject: [PATCH] multiplayer --- levels/prototype_scene.tscn | 15 +++--- project.godot | 3 +- scenes/main_menu.tscn | 60 ++++++++++++++++++++++ scenes/player.tscn | 22 +++++++- scenes/smoke.tscn | 50 +++++++++++++++++- scripts/gui/lobby/lobby_buttons.gd | 13 +++++ scripts/gui/lobby/lobby_buttons.gd.uid | 1 + scripts/gui/lobby/players_display.gd | 28 ++++++++++ scripts/gui/lobby/players_display.gd.uid | 1 + scripts/gui/main_menu_gui.gd | 14 +++++ scripts/gui/main_menu_gui.gd.uid | 1 + scripts/multiplayer/loaded_notifier.gd | 7 +++ scripts/multiplayer/loaded_notifier.gd.uid | 1 + scripts/multiplayer/lobby.gd | 29 +++++++++++ scripts/multiplayer/lobby.gd.uid | 1 + scripts/multiplayer/player_spawner.gd | 15 ++++++ scripts/multiplayer/player_spawner.gd.uid | 1 + scripts/player/crosshair.gd | 2 +- scripts/player/player.gd | 15 +++++- scripts/player/player_camera.gd | 10 +++- scripts/smoke_grenade.gd | 22 ++++++++ scripts/smoke_grenade.gd.uid | 1 + 22 files changed, 298 insertions(+), 14 deletions(-) create mode 100644 scenes/main_menu.tscn create mode 100644 scripts/gui/lobby/lobby_buttons.gd create mode 100644 scripts/gui/lobby/lobby_buttons.gd.uid create mode 100644 scripts/gui/lobby/players_display.gd create mode 100644 scripts/gui/lobby/players_display.gd.uid create mode 100644 scripts/gui/main_menu_gui.gd create mode 100644 scripts/gui/main_menu_gui.gd.uid create mode 100644 scripts/multiplayer/loaded_notifier.gd create mode 100644 scripts/multiplayer/loaded_notifier.gd.uid create mode 100644 scripts/multiplayer/lobby.gd create mode 100644 scripts/multiplayer/lobby.gd.uid create mode 100644 scripts/multiplayer/player_spawner.gd create mode 100644 scripts/multiplayer/player_spawner.gd.uid create mode 100644 scripts/smoke_grenade.gd create mode 100644 scripts/smoke_grenade.gd.uid diff --git a/levels/prototype_scene.tscn b/levels/prototype_scene.tscn index 9e82504..f67036e 100644 --- a/levels/prototype_scene.tscn +++ b/levels/prototype_scene.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=5 format=3 uid="uid://cqrh2cc7m2i7f"] [ext_resource type="Environment" uid="uid://d0cfgtx2yxw13" path="res://environments/prototype_environment.tres" id="1_i6jab"] -[ext_resource type="PackedScene" uid="uid://dpsr6ug3pkb40" path="res://scenes/player.tscn" id="2_ajphm"] +[ext_resource type="Script" uid="uid://ypgm3aplt78m" path="res://scripts/multiplayer/player_spawner.gd" id="4_pi0y7"] [ext_resource type="Material" uid="uid://bx3f5vx71ynh5" path="res://materials/OrangeMat.tres" id="4_y6i55"] [ext_resource type="Material" uid="uid://mlha6r17v2en" path="res://materials/Bluemat.tres" id="5_bno23"] @@ -14,11 +14,6 @@ shadow_enabled = true [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = ExtResource("1_i6jab") -[node name="Player" parent="." instance=ExtResource("2_ajphm")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.073462, 1.5000002, -0.5869317) -SPEED = 10.0 -TOGGLE_CROUCH = false - [node name="CSGCombiner3D" type="CSGCombiner3D" parent="."] use_collision = true @@ -40,3 +35,11 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.49652505, 4.840562) polygon = PackedVector2Array(0, 0, -0.061755046, 1.5814729, 2, 0) depth = 2.45 material = ExtResource("5_bno23") + +[node name="Spawner" type="Node3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5862943, 0) +script = ExtResource("4_pi0y7") + +[node name="MultiplayerSpawner" type="MultiplayerSpawner" parent="."] +_spawnable_scenes = PackedStringArray("uid://dpsr6ug3pkb40") +spawn_path = NodePath("..") diff --git a/project.godot b/project.godot index 62eb1c2..2b99fba 100644 --- a/project.godot +++ b/project.godot @@ -11,13 +11,14 @@ config_version=5 [application] config/name="Chelimbalo" -run/main_scene="uid://cqrh2cc7m2i7f" +run/main_scene="uid://cbtp4rvg66ba1" config/features=PackedStringArray("4.5", "Forward Plus") config/icon="res://icon.svg" [autoload] PlayerGlobal="*res://scripts/player/player_global.gd" +Lobby="*res://scripts/multiplayer/lobby.gd" [input] diff --git a/scenes/main_menu.tscn b/scenes/main_menu.tscn new file mode 100644 index 0000000..43620ea --- /dev/null +++ b/scenes/main_menu.tscn @@ -0,0 +1,60 @@ +[gd_scene load_steps=4 format=3 uid="uid://cbtp4rvg66ba1"] + +[ext_resource type="Script" uid="uid://bsyuos803g7qf" path="res://scripts/gui/main_menu_gui.gd" id="1_l6cm7"] +[ext_resource type="Script" uid="uid://cl3hhmw5666sj" path="res://scripts/gui/lobby/players_display.gd" id="2_ekxnf"] +[ext_resource type="Script" uid="uid://2uyxkfmbbims" path="res://scripts/gui/lobby/lobby_buttons.gd" id="3_bqqt6"] + +[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_l6cm7") + +[node name="MainMenu" type="PanelContainer" parent="."] +layout_mode = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="MainMenu"] +layout_mode = 2 + +[node name="HostButton" type="Button" parent="MainMenu/VBoxContainer"] +layout_mode = 2 +text = "HOST" + +[node name="ConnectButton" type="Button" parent="MainMenu/VBoxContainer"] +layout_mode = 2 +text = "CONNECT" + +[node name="Lobby" type="PanelContainer" parent="."] +visible = false +layout_mode = 0 +offset_right = 305.0 +offset_bottom = 242.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="Lobby"] +layout_mode = 2 + +[node name="Players" type="HBoxContainer" parent="Lobby/VBoxContainer"] +layout_mode = 2 +script = ExtResource("2_ekxnf") + +[node name="Buttons" type="HBoxContainer" parent="Lobby/VBoxContainer"] +layout_mode = 2 +script = ExtResource("3_bqqt6") + +[node name="LeaveButton" type="Button" parent="Lobby/VBoxContainer/Buttons"] +layout_mode = 2 +text = "Leave" + +[node name="StartButton" type="Button" parent="Lobby/VBoxContainer/Buttons"] +layout_mode = 2 +text = "Start" + +[connection signal="pressed" from="MainMenu/VBoxContainer/HostButton" to="." method="_on_host_button_pressed"] +[connection signal="pressed" from="MainMenu/VBoxContainer/ConnectButton" to="." method="_on_connect_button_pressed"] +[connection signal="pressed" from="Lobby/VBoxContainer/Buttons/LeaveButton" to="Lobby/VBoxContainer/Buttons" method="_on_leave_button_pressed"] +[connection signal="pressed" from="Lobby/VBoxContainer/Buttons/StartButton" to="Lobby/VBoxContainer/Buttons" method="_on_start_button_pressed"] diff --git a/scenes/player.tscn b/scenes/player.tscn index 13607e1..fd35da4 100644 --- a/scenes/player.tscn +++ b/scenes/player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=10 format=3 uid="uid://dpsr6ug3pkb40"] +[gd_scene load_steps=11 format=3 uid="uid://dpsr6ug3pkb40"] [ext_resource type="Script" uid="uid://3dphlay25fih" path="res://scripts/player/player.gd" id="1_g2els"] [ext_resource type="Script" uid="uid://dalwlndejfdhm" path="res://scripts/player/crosshair.gd" id="3_dqkch"] @@ -95,6 +95,21 @@ _data = { } [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_g2els"] +height = 1.2958984 + +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_qhqgy"] +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("Camera3D:rotation") +properties/2/spawn = true +properties/2/replication_mode = 1 +properties/3/path = NodePath(".:crouched") +properties/3/spawn = true +properties/3/replication_mode = 1 [node name="Player" type="CharacterBody3D" node_paths=PackedStringArray("animation_player", "stand_up_area")] collision_layer = 2 @@ -122,7 +137,7 @@ libraries = { collision_layer = 0 [node name="CollisionShape3D" type="CollisionShape3D" parent="StandArea"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.3520508, 0) shape = SubResource("CapsuleShape3D_g2els") debug_color = Color(0.9878064, 0, 0.31407458, 0.41960785) debug_fill = false @@ -152,3 +167,6 @@ script = ExtResource("3_dqkch") crosses_width = 2.0 crosses_length = 6.0 crosses_offset = 3.0 + +[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."] +replication_config = SubResource("SceneReplicationConfig_qhqgy") diff --git a/scenes/smoke.tscn b/scenes/smoke.tscn index 49e4712..31b1b8f 100644 --- a/scenes/smoke.tscn +++ b/scenes/smoke.tscn @@ -1,3 +1,49 @@ -[gd_scene format=3 uid="uid://cheu6vds21er7"] +[gd_scene load_steps=8 format=3 uid="uid://cheu6vds21er7"] -[node name="Smoke" type="RigidBody3D"] +[ext_resource type="Script" uid="uid://t5jjqwnkxgvo" path="res://scripts/smoke_grenade.gd" id="1_acmqr"] + +[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_acmqr"] +bounce = 0.5 + +[sub_resource type="CapsuleMesh" id="CapsuleMesh_vb5ru"] +radius = 0.1 +height = 0.5 + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_acmqr"] +frequency = 0.1141 + +[sub_resource type="NoiseTexture3D" id="NoiseTexture3D_rx0m8"] +width = 128 +height = 128 +depth = 128 +noise = SubResource("FastNoiseLite_acmqr") + +[sub_resource type="FogMaterial" id="FogMaterial_rx0m8"] +resource_local_to_scene = true +density = 8.0 +density_texture = SubResource("NoiseTexture3D_rx0m8") + +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_acmqr"] +radius = 0.1 +height = 0.5 + +[node name="Smoke" type="RigidBody3D" node_paths=PackedStringArray("fog")] +physics_material_override = SubResource("PhysicsMaterial_acmqr") +contact_monitor = true +max_contacts_reported = 1 +script = ExtResource("1_acmqr") +radius = 10.0 +fog = NodePath("FogVolume") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("CapsuleMesh_vb5ru") + +[node name="FogVolume" type="FogVolume" parent="."] +size = Vector3(0, 0, 0) +shape = 0 +material = SubResource("FogMaterial_rx0m8") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("CapsuleShape3D_acmqr") + +[connection signal="body_entered" from="." to="." method="_on_body_entered"] diff --git a/scripts/gui/lobby/lobby_buttons.gd b/scripts/gui/lobby/lobby_buttons.gd new file mode 100644 index 0000000..88e291f --- /dev/null +++ b/scripts/gui/lobby/lobby_buttons.gd @@ -0,0 +1,13 @@ +extends Node + +func _ready() -> void: + Lobby.lobby_joined.connect(on_lobby_joined) + +func _on_leave_button_pressed() -> void: + Lobby.leave() + +func _on_start_button_pressed() -> void: + Lobby.start_game.rpc() + +func on_lobby_joined() -> void: + $StartButton.hide() diff --git a/scripts/gui/lobby/lobby_buttons.gd.uid b/scripts/gui/lobby/lobby_buttons.gd.uid new file mode 100644 index 0000000..6157091 --- /dev/null +++ b/scripts/gui/lobby/lobby_buttons.gd.uid @@ -0,0 +1 @@ +uid://2uyxkfmbbims diff --git a/scripts/gui/lobby/players_display.gd b/scripts/gui/lobby/players_display.gd new file mode 100644 index 0000000..39709d7 --- /dev/null +++ b/scripts/gui/lobby/players_display.gd @@ -0,0 +1,28 @@ +extends Node + + +func _ready() -> void: + multiplayer.peer_connected.connect(on_peer_connected) + multiplayer.peer_disconnected.connect(on_peer_disconnected) + Lobby.lobby_created.emit(add_self) + Lobby.lobby_joined.emit(add_self) + Lobby.lobby_closed.emit(clear) + +func on_peer_connected(id: int) -> void: + var label = Label.new() + label.text = str(id) + label.name = str(id) + add_child(label,true) + +func on_peer_disconnected(id: int) -> void: + get_node(str(id)).queue_free() + +func add_self() -> void: + var label = Label.new() + label.text = str(multiplayer.get_unique_id()) + label.name = str(multiplayer.get_unique_id()) + add_child(label,true) + +func clear() -> void: + for child in get_children(): + child.queue_free() diff --git a/scripts/gui/lobby/players_display.gd.uid b/scripts/gui/lobby/players_display.gd.uid new file mode 100644 index 0000000..a08b12b --- /dev/null +++ b/scripts/gui/lobby/players_display.gd.uid @@ -0,0 +1 @@ +uid://cl3hhmw5666sj diff --git a/scripts/gui/main_menu_gui.gd b/scripts/gui/main_menu_gui.gd new file mode 100644 index 0000000..17d7caf --- /dev/null +++ b/scripts/gui/main_menu_gui.gd @@ -0,0 +1,14 @@ +extends Node + + + +func _on_host_button_pressed() -> void: + Lobby.host() + $MainMenu.hide() + $Lobby.show() + + +func _on_connect_button_pressed() -> void: + Lobby.join("localhost") + $MainMenu.hide() + $Lobby.show() diff --git a/scripts/gui/main_menu_gui.gd.uid b/scripts/gui/main_menu_gui.gd.uid new file mode 100644 index 0000000..87d7b87 --- /dev/null +++ b/scripts/gui/main_menu_gui.gd.uid @@ -0,0 +1 @@ +uid://bsyuos803g7qf diff --git a/scripts/multiplayer/loaded_notifier.gd b/scripts/multiplayer/loaded_notifier.gd new file mode 100644 index 0000000..7bccb84 --- /dev/null +++ b/scripts/multiplayer/loaded_notifier.gd @@ -0,0 +1,7 @@ +extends Node + + +func _ready() -> void: + if not multiplayer.is_server(): + return + Lobby.add_loaded_player(multiplayer.get_unique_id()) diff --git a/scripts/multiplayer/loaded_notifier.gd.uid b/scripts/multiplayer/loaded_notifier.gd.uid new file mode 100644 index 0000000..5d0fbec --- /dev/null +++ b/scripts/multiplayer/loaded_notifier.gd.uid @@ -0,0 +1 @@ +uid://bdxy621fthtrv diff --git a/scripts/multiplayer/lobby.gd b/scripts/multiplayer/lobby.gd new file mode 100644 index 0000000..849d1d8 --- /dev/null +++ b/scripts/multiplayer/lobby.gd @@ -0,0 +1,29 @@ +extends Node + + +const MAX_PLAYERS: int = 10 +const PORT: int = 7777 + +signal lobby_created +signal lobby_joined +signal lobby_closed + +func host() -> void: + var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new() + peer.create_server(PORT,MAX_PLAYERS) + multiplayer.multiplayer_peer = peer + lobby_created.emit() + +func join(ip: String) -> void: + var peer: ENetMultiplayerPeer = ENetMultiplayerPeer.new() + peer.create_client(ip,PORT) + multiplayer.multiplayer_peer = peer + lobby_joined.emit() + +func leave() -> void: + multiplayer.multiplayer_peer = OfflineMultiplayerPeer.new() + lobby_closed.emit() + +@rpc("authority","call_local","reliable") +func start_game() -> void: + get_tree().change_scene_to_file("res://levels/prototype_scene.tscn") diff --git a/scripts/multiplayer/lobby.gd.uid b/scripts/multiplayer/lobby.gd.uid new file mode 100644 index 0000000..dfe0901 --- /dev/null +++ b/scripts/multiplayer/lobby.gd.uid @@ -0,0 +1 @@ +uid://c5sfxrl7hky6p diff --git a/scripts/multiplayer/player_spawner.gd b/scripts/multiplayer/player_spawner.gd new file mode 100644 index 0000000..881cf19 --- /dev/null +++ b/scripts/multiplayer/player_spawner.gd @@ -0,0 +1,15 @@ +extends Node3D + + +func _ready() -> void: + if not multiplayer.is_server(): + return + spawn_player(multiplayer.get_unique_id()) + for i in multiplayer.get_peers(): + spawn_player(i) + +func spawn_player(id) -> void: + var player: PackedScene = load("res://scenes/player.tscn") + var inst = player.instantiate() + inst.name = str(id) + get_tree().current_scene.add_child.call_deferred(inst,true) diff --git a/scripts/multiplayer/player_spawner.gd.uid b/scripts/multiplayer/player_spawner.gd.uid new file mode 100644 index 0000000..45ddd14 --- /dev/null +++ b/scripts/multiplayer/player_spawner.gd.uid @@ -0,0 +1 @@ +uid://ypgm3aplt78m diff --git a/scripts/player/crosshair.gd b/scripts/player/crosshair.gd index e601de4..26c3133 100644 --- a/scripts/player/crosshair.gd +++ b/scripts/player/crosshair.gd @@ -14,7 +14,7 @@ extends Control @export var crosses_color: Color = Color.WHITE func _draw() -> void: - draw_circle(Vector2(0,0),dot_radius,dot_color,true,outline_width) + draw_circle(Vector2(0,0),dot_radius,dot_color,false,outline_width) for i in range(0,4): var direction: Vector2 = Vector2.RIGHT.rotated(i*PI/2) var offset_position: Vector2 = direction * crosses_offset diff --git a/scripts/player/player.gd b/scripts/player/player.gd index be40fc1..73c549d 100644 --- a/scripts/player/player.gd +++ b/scripts/player/player.gd @@ -10,7 +10,7 @@ extends CharacterBody3D @export var TOGGLE_CROUCH: bool = true @export var WALK_MODIFIER: float = 0.5 -var crouched: bool = false: +@export var crouched: bool = false: set(value): if value != crouched and stand_up_area.has_overlapping_bodies() == false: crouched = value @@ -22,11 +22,18 @@ var crouched: bool = false: var potential_crouched: bool = crouched +func _enter_tree() -> void: + set_multiplayer_authority(str(name).to_int()) + func _process(_delta: float) -> void: + if not is_multiplayer_authority(): + return if potential_crouched != crouched: crouched = potential_crouched func _physics_process(delta: float) -> void: + if not is_multiplayer_authority(): + return # Add the gravity. if not is_on_floor(): velocity += get_gravity() * delta @@ -55,6 +62,8 @@ func update_crouch(): animation_player.play("Crouch",-1,-1/CROUCH_TIME,true) func _input(event: InputEvent) -> void: + if not is_multiplayer_authority(): + return if event.is_action_pressed("plr_crouch"): if TOGGLE_CROUCH: crouched = not crouched @@ -62,3 +71,7 @@ func _input(event: InputEvent) -> void: crouched = true elif event.is_action_released("plr_crouch") and TOGGLE_CROUCH == false: crouched = false + if event.is_action_pressed("plr_drop"): + var grenade = preload("res://scenes/smoke.tscn").instantiate() + get_tree().current_scene.add_child(grenade) + grenade.global_position = global_position + Vector3.UP * 0.5 diff --git a/scripts/player/player_camera.gd b/scripts/player/player_camera.gd index 916d1c8..986fa81 100644 --- a/scripts/player/player_camera.gd +++ b/scripts/player/player_camera.gd @@ -4,11 +4,19 @@ const COLLINEAR = 1.5707963267948966 @export var SENSITIVITY = 0.02 +func _enter_tree() -> void: + set_multiplayer_authority(get_parent().name.to_int()) + func _ready() -> void: # Move to level controller when possible - Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + #Input.mouse_mode = Input.MOUSE_MODE_CAPTURED + if not is_multiplayer_authority(): + return + current = true func _input(event: InputEvent) -> void: + if not is_multiplayer_authority(): + return if event is InputEventMouseMotion: get_parent().rotate_y(-event.relative.x * SENSITIVITY) rotate_x(-event.relative.y * SENSITIVITY) diff --git a/scripts/smoke_grenade.gd b/scripts/smoke_grenade.gd new file mode 100644 index 0000000..9ebacc3 --- /dev/null +++ b/scripts/smoke_grenade.gd @@ -0,0 +1,22 @@ +extends RigidBody3D + + +@export var radius: float +@export var fog: FogVolume + +var bounce_count: int = 0 + + +func _on_body_entered(_body: Node) -> void: + if bounce_count > 2: + return + bounce_count += 1 + if bounce_count == 2: + smoke() + +func smoke(): + var tween = create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_EXPO) + tween.tween_property(fog,"size",Vector3(radius,radius,radius),1.0) + tween.tween_interval(10) + tween.tween_property(fog.material,"density",0,1.0) + tween.tween_callback(queue_free) diff --git a/scripts/smoke_grenade.gd.uid b/scripts/smoke_grenade.gd.uid new file mode 100644 index 0000000..f0fb0a5 --- /dev/null +++ b/scripts/smoke_grenade.gd.uid @@ -0,0 +1 @@ +uid://t5jjqwnkxgvo