diff --git a/src/Composer/Composer.gd b/src/Composer/Composer.gd index 2d0a12d..f611d39 100644 --- a/src/Composer/Composer.gd +++ b/src/Composer/Composer.gd @@ -6,7 +6,7 @@ extends Node ## the option to have a loading screen and the transfer of data between scenes. ## Emitted when Composer has been fully initialised, alongside with its timer. -signal finished_initialising() +signal finished_initialising ## Emitted when provided scene has been invalid (may not exist or path is invalid). signal invalid_scene(path: String) @@ -22,7 +22,7 @@ signal finished_loading(scene: Node) ## Use with loading screens, for scene activation (i.e making scene visible or activating certain game logic). @warning_ignore("unused_signal") -signal loading_activated() +signal loading_activated ## Tracks if composer finished initializing; i.e when its ready to use. var has_initialized: bool = false: @@ -42,7 +42,8 @@ var cache_mode: ResourceLoader.CacheMode = ResourceLoader.CACHE_MODE_REUSE ## This should not be changed unless absolutely necessary. var loading_timer_delay: float = 0.1: set(val): - if !has_initialized: await finished_initialising + if !has_initialized: + await finished_initialising loading_timer_delay = val _loading_timer.wait_time = loading_timer_delay @@ -59,6 +60,7 @@ var _current_load_screen: Node = null var _current_data: Dictionary = {} + func _enter_tree() -> void: invalid_scene.connect(_on_invalid_scene) failed_loading.connect(_on_failed_loading) @@ -67,22 +69,28 @@ func _enter_tree() -> void: root = get_tree().root _setup_timer() + ## Replaces the current scene with a new scene using a path, ## can also be used for transferring data between scenes with the optional data_to_transfer ## parameter. Data will be stored as new scene metadata, named "transferred_data". func load_scene(path_to_scene: String, data_to_transfer: Dictionary = {}) -> void: - if _is_loading: return + if _is_loading: + return - if !has_initialized: await finished_initialising + if !has_initialized: + await finished_initialising - var loader: Error = ResourceLoader.load_threaded_request(path_to_scene, "", is_using_subthreads, cache_mode) + var loader: Error = ResourceLoader.load_threaded_request( + path_to_scene, "", is_using_subthreads, cache_mode + ) if not ResourceLoader.exists(path_to_scene) or loader == null: invalid_scene.emit(path_to_scene) return _is_loading = true - if _loading_timer == null: _setup_timer() + if _loading_timer == null: + _setup_timer() get_tree().current_scene.queue_free() @@ -90,28 +98,34 @@ func load_scene(path_to_scene: String, data_to_transfer: Dictionary = {}) -> voi _current_data = data_to_transfer _loading_timer.start() + ## Creates a loading screen using a path and adds it to the SceneTree. ## Returns an instance of it for usage with signals. func setup_load_screen(path_to_load_screen: String) -> Node: - if _has_loading_screen: return null + if _has_loading_screen: + return null _has_loading_screen = true _current_load_screen = load(path_to_load_screen).instantiate() - root.call_deferred("add_child",_current_load_screen) - root.call_deferred("move_child",_current_load_screen, get_child_count()-1) + root.call_deferred("add_child", _current_load_screen) + root.call_deferred("move_child", _current_load_screen, get_child_count() - 1) return _current_load_screen + ## Gets rid of the loading screen. func clear_load_screen() -> void: _current_load_screen.queue_free() _current_load_screen = null _has_loading_screen = false + func _check_loading_status() -> void: var load_progress: Array = [] - var load_status: ResourceLoader.ThreadLoadStatus = ResourceLoader.load_threaded_get_status(_current_loading_path, load_progress) + var load_status: ResourceLoader.ThreadLoadStatus = ResourceLoader.load_threaded_get_status( + _current_loading_path, load_progress + ) match load_status: ResourceLoader.THREAD_LOAD_INVALID_RESOURCE: @@ -126,19 +140,23 @@ func _check_loading_status() -> void: updated_loading.emit(_current_loading_path, int(load_progress[0] * 100)) ResourceLoader.THREAD_LOAD_LOADED: _loading_timer.stop() - finished_loading.emit(ResourceLoader.load_threaded_get(_current_loading_path).instantiate()) + finished_loading.emit( + ResourceLoader.load_threaded_get(_current_loading_path).instantiate() + ) + func _setup_timer() -> void: _loading_timer = Timer.new() _loading_timer.name = "LoadingTimer" _loading_timer.wait_time = 0.1 _loading_timer.timeout.connect(_check_loading_status) - root.call_deferred("add_child",_loading_timer) + root.call_deferred("add_child", _loading_timer) await _loading_timer.ready has_initialized = true + func _on_finished_loading(scene: Node) -> void: scene.set_meta("transferred_data", _current_data) @@ -149,8 +167,10 @@ func _on_finished_loading(scene: Node) -> void: _is_loading = false _current_data = {} + func _on_invalid_scene(path: String) -> void: printerr("Error: Invalid resource: " + path) + func _on_failed_loading(path: String) -> void: printerr("Error: Failed to load resource: " + path) diff --git a/src/Game/Bullet/bullet.gd b/src/Game/Bullet/bullet.gd index 04a96ef..e4b9d44 100644 --- a/src/Game/Bullet/bullet.gd +++ b/src/Game/Bullet/bullet.gd @@ -6,12 +6,15 @@ const SPEED: float = 1000.0 var direction: Vector2 = Vector2.RIGHT + func _physics_process(delta: float) -> void: global_position += direction.rotated(rotation) * SPEED * delta + func _on_kill_timer_timeout() -> void: queue_free() + func _on_area_entered(area: Area2D) -> void: if area.is_in_group("Players"): return diff --git a/src/Game/Game.gd b/src/Game/Game.gd index 14e3bc0..88f5ba0 100644 --- a/src/Game/Game.gd +++ b/src/Game/Game.gd @@ -29,16 +29,13 @@ var player_sprites: Array = [ preload("res://assets/images/player4.png") ] -var player_scores: Dictionary = { +var player_scores: Dictionary = {} -} - -var is_player_dead: Dictionary = { - -} +var is_player_dead: Dictionary = {} var is_dead: bool = false + func _ready() -> void: multiplayer.peer_disconnected.connect(_on_player_disconnected) multiplayer.server_disconnected.connect(_on_server_disconnected) @@ -60,10 +57,12 @@ func _ready() -> void: start_game() + func _process(delta: float) -> void: if Input.is_action_just_pressed("exit"): pause_panel.visible = not pause_panel.visible + func spawn_players() -> void: # Spawn players for each client and assign authorities for idx in range(0, Lobby.connected_peers.keys().size()): @@ -87,11 +86,12 @@ func spawn_players() -> void: player_scores[p_id] = {"score": 0, "kills": 0, "wave": 0} if multiplayer.get_unique_id() != 1: - game_manager.rpc_id(1,"add_players_spawned",multiplayer.get_unique_id()) + game_manager.rpc_id(1, "add_players_spawned", multiplayer.get_unique_id()) else: game_manager.add_players_spawned(1) -@rpc("call_remote","authority","reliable",1) + +@rpc("call_remote", "authority", "reliable", 1) func spawn_zombie(z_pos: Vector2, health: float, speed: float) -> void: # Spawn zombie for each client var zombie: Zombie = ZOMBIE.instantiate() @@ -116,6 +116,7 @@ func spawn_zombie(z_pos: Vector2, health: float, speed: float) -> void: zombie.score_updated.connect(player._on_zombie_score_updated) zombie.zombie_killed.connect(player._on_zombie_killed) + func start_game() -> void: # When the game starts, synchronize all players. for plr: Player in players.get_children(): @@ -128,17 +129,23 @@ func start_game() -> void: wave_system.start() -@rpc("authority","call_local","reliable",1) + +@rpc("authority", "call_local", "reliable", 1) func end_game(scores: Dictionary) -> void: # Show the game over panel and the scores sent by the host for each client - $UI/GameHUD/GameEndPanel/VBoxContainer/Title.text = "Game Over!\nYou survived for " + str(scores[multiplayer.get_unique_id()]["wave"]) + " waves.\nPlayers' stats:" + $UI/GameHUD/GameEndPanel/VBoxContainer/Title.text = ( + "Game Over!\nYou survived for " + + str(scores[multiplayer.get_unique_id()]["wave"]) + + " waves.\nPlayers' stats:" + ) wave_system.stop() $GameOver.play() for idx in range(0, player_score_container.get_child_count()): var child: HBoxContainer = player_score_container.get_child(idx) - if child.name == "HBoxContainer": continue + if child.name == "HBoxContainer": + continue var actual_index: int = idx - 1 if Lobby.connected_peers.keys().size() <= actual_index: @@ -161,7 +168,8 @@ func end_game(scores: Dictionary) -> void: set_process(false) -@rpc("any_peer","call_local","reliable",1) + +@rpc("any_peer", "call_local", "reliable", 1) func kill_player(id: int) -> void: # Kill the player for every client for player in players.get_children(): @@ -181,6 +189,7 @@ func kill_player(id: int) -> void: if not is_player_dead.values().has(false): rpc("end_game", player_scores) + func _on_zombie_spawned(health: float, speed: float) -> void: # Randomize the zombie spawn location zombie_spawn_point.progress_ratio = randf() @@ -188,18 +197,22 @@ func _on_zombie_spawned(health: float, speed: float) -> void: if multiplayer.get_unique_id() == 1: spawn_zombie(zombie_spawn_point.global_position, health, speed) + func _on_zombie_score_updated(id: int, value: int) -> void: if multiplayer.get_unique_id() == 1: player_scores[id]["score"] += value + func _on_zombie_killed(id: int) -> void: if multiplayer.get_unique_id() == 1: player_scores[id]["kills"] += 1 + func _on_player_disconnected(id: int) -> void: kill_player(id) game_manager.clear_peer(id) + func _on_server_disconnected() -> void: # Kick everyone once the host has left or disconnected process_mode = PROCESS_MODE_DISABLED @@ -212,23 +225,28 @@ func _on_server_disconnected() -> void: ) ui.add_child(popup) + func _on_menu_button_pressed() -> void: $ButtonClick.play() process_mode = PROCESS_MODE_DISABLED Composer.load_scene("res://src/MainMenu/MainMenu.tscn") Lobby.clear_peer() + func _on_resume_button_pressed() -> void: $ButtonClick.play() pause_panel.hide() + func _on_wave_system_update_info_text(text: String) -> void: if not is_dead: info_text.text = text + func _on_wave_system_wave_started() -> void: %InfoAnimation.play("WaveStart") + func _on_wave_system_wave_ended(wave: int) -> void: %InfoAnimation.play("RESET") diff --git a/src/Game/GameManager/GameManager.gd b/src/Game/GameManager/GameManager.gd index e4eecf6..e78a319 100644 --- a/src/Game/GameManager/GameManager.gd +++ b/src/Game/GameManager/GameManager.gd @@ -1,17 +1,13 @@ class_name GameManager extends Node -signal game_ready() -signal players_ready() +signal game_ready +signal players_ready signal zombie_ready(zombie_name: String) signal zombie_dead(zombie_name: String) -var has_launched_game: Dictionary = { +var has_launched_game: Dictionary = {} -} - -var has_players_spawned: Dictionary = { - -} +var has_players_spawned: Dictionary = {} var is_game_ready: bool = false var are_players_ready: bool = false @@ -21,6 +17,7 @@ var is_zombie_dead: Dictionary = {} var game: Game + func _ready() -> void: game = get_parent() @@ -36,7 +33,8 @@ func _ready() -> void: if not Lobby.is_host_game_ready: await Lobby.host_game_ready - rpc_id(1,"add_launched_game",multiplayer.get_unique_id()) + rpc_id(1, "add_launched_game", multiplayer.get_unique_id()) + func add_zombie(zombie_name: String, z_pos: Vector2, health: float, speed: float) -> void: is_zombie_spawned[zombie_name] = {} @@ -51,7 +49,8 @@ func add_zombie(zombie_name: String, z_pos: Vector2, health: float, speed: float # Spawn the zombie for everyone game.rpc("spawn_zombie", z_pos, health, speed) -@rpc("any_peer","call_remote","reliable",1) + +@rpc("any_peer", "call_remote", "reliable", 1) func add_spawned_zombie(zombie_name: String, idx: int = 0) -> void: # Check if a zombie has spawned for every client if idx > 0: @@ -61,7 +60,8 @@ func add_spawned_zombie(zombie_name: String, idx: int = 0) -> void: if not is_zombie_spawned[zombie_name].values().has(false): rpc("set_zombie_ready", zombie_name) -@rpc("authority","call_local","reliable",1) + +@rpc("authority", "call_local", "reliable", 1) func set_zombie_ready(zombie_name: String) -> void: # Activate the zombie if multiplayer.get_unique_id() == 1: @@ -69,7 +69,8 @@ func set_zombie_ready(zombie_name: String) -> void: zombie_ready.emit(zombie_name) -@rpc("any_peer","call_remote","reliable",1) + +@rpc("any_peer", "call_remote", "reliable", 1) func add_dead_zombie(zombie_name: String, idx: int = 0) -> void: # Check if a zombie has despawned for every client if idx > 0: @@ -79,7 +80,8 @@ func add_dead_zombie(zombie_name: String, idx: int = 0) -> void: if not is_zombie_dead[zombie_name].values().has(false): rpc("set_zombie_dead", zombie_name) -@rpc("authority","call_local","reliable",1) + +@rpc("authority", "call_local", "reliable", 1) func set_zombie_dead(zombie_name: String) -> void: # Delete the zombie if multiplayer.get_unique_id() == 1: @@ -87,7 +89,8 @@ func set_zombie_dead(zombie_name: String) -> void: zombie_dead.emit(zombie_name) -@rpc("any_peer","call_remote","reliable",1) + +@rpc("any_peer", "call_remote", "reliable", 1) func add_launched_game(idx: int = 0) -> void: # Check if the game has launched for every client if idx > 0: @@ -101,12 +104,14 @@ func add_launched_game(idx: int = 0) -> void: if not has_launched_game.values().has(false): rpc("set_game_ready") -@rpc("authority","call_local","reliable",1) + +@rpc("authority", "call_local", "reliable", 1) func set_game_ready() -> void: is_game_ready = true game_ready.emit() -@rpc("any_peer","call_remote","reliable",1) + +@rpc("any_peer", "call_remote", "reliable", 1) func add_players_spawned(idx: int = 0) -> void: # Check if the players have spawned for every client if idx > 0: @@ -116,11 +121,13 @@ func add_players_spawned(idx: int = 0) -> void: if not has_players_spawned.values().has(false): rpc("set_player_ready") -@rpc("authority","call_local","reliable",1) + +@rpc("authority", "call_local", "reliable", 1) func set_player_ready() -> void: are_players_ready = true players_ready.emit() + func clear_peer(id: int) -> void: # Remove all references to a peer who leaves during the game. has_launched_game.erase(id) diff --git a/src/Game/Player/player.gd b/src/Game/Player/player.gd index 63b3a50..a0b17b7 100644 --- a/src/Game/Player/player.gd +++ b/src/Game/Player/player.gd @@ -49,6 +49,7 @@ var is_dead: bool: get(): return health <= 0.0 + func _ready() -> void: game = get_parent().get_parent() @@ -58,6 +59,7 @@ func _ready() -> void: username_text.text = Lobby.connected_peers[multiplayer.get_unique_id()] ui.show() + func _physics_process(delta: float) -> void: # Apply lag compensation for other clients. if get_multiplayer_authority() != multiplayer.get_unique_id(): @@ -77,7 +79,7 @@ func _physics_process(delta: float) -> void: sync_pos = global_position sync_rot = rotation_degrees - var movement_vector: Vector2 = Input.get_vector("left","right","up","down") + var movement_vector: Vector2 = Input.get_vector("left", "right", "up", "down") if movement_vector: global_position = Vector2( clampf(global_position.x + movement_vector.x * speed * delta, 0, 1280), @@ -92,6 +94,7 @@ func shoot() -> void: # Create the same bullet for every client present rpc("create_bullet", bullet_pos.global_position, self.rotation, multiplayer.get_unique_id()) + func reload() -> void: # Reload the weapon, happens locally if reload_timer.time_left <= 0 and magazine < 30: @@ -99,7 +102,8 @@ func reload() -> void: ammo_text.text = "Reloading..." reload_timer.start() -@rpc("any_peer", "call_local", "reliable",1) + +@rpc("any_peer", "call_local", "reliable", 1) func create_bullet(p_pos: Vector2, p_rot: float, id: int) -> void: $ShootSound.play() @@ -110,23 +114,28 @@ func create_bullet(p_pos: Vector2, p_rot: float, id: int) -> void: bullet.rotation = p_rot bullet.set_multiplayer_authority(1) + func _on_area_entered(area: Area2D) -> void: if area.is_in_group("Zombies") && is_multiplayer_authority(): attack_timer.start() + func _on_area_exited(area: Area2D) -> void: if area.is_in_group("Zombies") && is_multiplayer_authority(): attack_timer.stop() + func _on_attack_timer_timeout() -> void: # Only take damage on the controlling client if is_multiplayer_authority(): health -= 10.0 + func _on_reload_timer_timeout() -> void: if is_multiplayer_authority(): magazine = 30 + func _on_zombie_score_updated(id: int, value: int) -> void: # Update the score when a zombie is hit if id == multiplayer.get_unique_id(): @@ -135,6 +144,7 @@ func _on_zombie_score_updated(id: int, value: int) -> void: if not score_animation.is_playing(): score_animation.play("ScoreUpdate") + func _on_zombie_killed(id: int) -> void: # Update the kill count if id == multiplayer.get_unique_id(): diff --git a/src/Game/WaveSystem/WaveSystem.gd b/src/Game/WaveSystem/WaveSystem.gd index 0cfbb44..bfb6179 100644 --- a/src/Game/WaveSystem/WaveSystem.gd +++ b/src/Game/WaveSystem/WaveSystem.gd @@ -4,7 +4,7 @@ signal update_info_text(text: String) signal zombie_spawned(health: float, speed: float) -signal wave_started() +signal wave_started signal wave_ended(wave: int) @onready var zombie_spawn_timer: Timer = $ZombieSpawnTimer @@ -32,9 +32,11 @@ var this_wave_wait_time: float = 2.0 var anticipation_time: int = 10 var this_anticipation_time: int = 0 + func _ready() -> void: game = get_parent() + func start() -> void: current_wave = 0 this_wave_zombies = 20 @@ -44,11 +46,13 @@ func start() -> void: _start_anticipation() + func stop() -> void: anticipation_timer.stop() zombie_spawn_timer.stop() game_theme.stop() + func _start_anticipation() -> void: # Start the countdown before the next wave this_anticipation_time = 0 @@ -58,6 +62,7 @@ func _start_anticipation() -> void: anticipation_timer.start() update_info_text.emit("Wave " + str(current_wave) + " in 10 seconds...") + func _start_wave() -> void: update_info_text.emit("Wave " + str(current_wave) + " in progress...") wave_started.emit() @@ -68,6 +73,7 @@ func _start_wave() -> void: game_theme.stream = game_themes.pick_random() game_theme.play() + func _end_wave() -> void: # End the wave and update the difficulty variables await get_tree().create_timer(2).timeout @@ -88,16 +94,26 @@ func _end_wave() -> void: this_wave_health += 5.0 this_wave_speed = clampf(this_wave_speed + 10.0, 100.0, 250.0) + func _on_anticipation_timer_timeout() -> void: this_anticipation_time += 1 - update_info_text.emit("Wave " + str(current_wave) + " in " + str(anticipation_time - this_anticipation_time) + " seconds...") + update_info_text.emit( + ( + "Wave " + + str(current_wave) + + " in " + + str(anticipation_time - this_anticipation_time) + + " seconds..." + ) + ) # End the anticipation phase and start the wave if this_anticipation_time == anticipation_time: anticipation_timer.stop() _start_wave() + func _on_zombie_spawn_timer_timeout() -> void: this_wave_spawned += 1 this_wave_remaining += 1 @@ -108,9 +124,11 @@ func _on_zombie_spawn_timer_timeout() -> void: zombie_spawned.emit(this_wave_health, this_wave_speed) + func _on_zombie_killed(_id: int) -> void: rpc("_update_zombie_remaining") + @rpc("any_peer", "call_local", "reliable", 2) func _update_zombie_remaining() -> void: this_wave_remaining -= 1 @@ -119,6 +137,7 @@ func _update_zombie_remaining() -> void: if this_wave_spawned == this_wave_zombies and this_wave_remaining <= 0: _end_wave() + func _on_wave_survived_finished() -> void: $Ambience.play() diff --git a/src/Game/Zombie/SoftCollision/soft_collision.gd b/src/Game/Zombie/SoftCollision/soft_collision.gd index 36d21ab..143243c 100644 --- a/src/Game/Zombie/SoftCollision/soft_collision.gd +++ b/src/Game/Zombie/SoftCollision/soft_collision.gd @@ -5,13 +5,21 @@ var overlapping_areas: Array[Area2D] = [] # This script prevents zombies from overlapping with each other # Detailed explanation of this script and how it works can be found here: https://www.youtube.com/watch?v=2LBuO7lbeAE + func check_for_overlap() -> bool: overlapping_areas = get_overlapping_areas() return overlapping_areas.size() > 0 + func get_push_vector() -> Vector2: if overlapping_areas.size() > 0: var direction: int = -1 if randf() < 0.5 else 1 - return overlapping_areas[0].global_position.direction_to(global_position).rotated(direction * PI/4).normalized() + return ( + overlapping_areas[0] + . global_position + . direction_to(global_position) + . rotated(direction * PI / 4) + . normalized() + ) return Vector2.ZERO diff --git a/src/Game/Zombie/zombie.gd b/src/Game/Zombie/zombie.gd index 83ce5bf..1d6f6a6 100644 --- a/src/Game/Zombie/zombie.gd +++ b/src/Game/Zombie/zombie.gd @@ -34,6 +34,7 @@ var target: Player = null var game_manager: GameManager var last_player_hit: int + func _ready() -> void: clean() @@ -49,6 +50,7 @@ func _ready() -> void: else: game_manager.rpc_id(1, "add_spawned_zombie", self.name, multiplayer.get_unique_id()) + func _physics_process(delta: float) -> void: # The host selects the target player, then syncs it with other clients. if is_multiplayer_authority(): @@ -86,6 +88,7 @@ func _physics_process(delta: float) -> void: if not global_position.distance_squared_to(target.global_position) < 350: global_position += direction.rotated(rotation) * speed * delta + func get_closest_target_id() -> int: # Find the nearest player alive and target them. var players: Array[Node] = get_tree().get_nodes_in_group("Players") @@ -108,13 +111,17 @@ func get_closest_target_id() -> int: continue if closest_target: - if global_position.distance_squared_to(player.global_position) < global_position.distance_squared_to(closest_target.global_position): + if ( + global_position.distance_squared_to(player.global_position) + < global_position.distance_squared_to(closest_target.global_position) + ): closest_target = player else: closest_target = player return closest_target.name.to_int() + func get_target(id: int) -> Player: var players: Array[Node] = get_tree().get_nodes_in_group("Players") @@ -124,6 +131,7 @@ func get_target(id: int) -> Player: return null + @rpc("authority", "call_local", "reliable", 2) func update_score_on_hit(id: int, score: int) -> void: if not $ZombieHit.playing: @@ -131,11 +139,13 @@ func update_score_on_hit(id: int, score: int) -> void: score_updated.emit(id, score) + @rpc("authority", "call_local", "reliable", 2) func update_kill(id: int) -> void: # Notify that the zombie is dead on this client zombie_killed.emit(id) + func _on_area_entered(area: Area2D) -> void: if area.is_in_group("Bullets"): if not $AnimationPlayer.is_playing(): @@ -154,14 +164,17 @@ func _on_area_entered(area: Area2D) -> void: score_updated.emit(last_player_hit, hit_score) + func _on_zombie_ready(zombie_name: String) -> void: if self.name == zombie_name: activate() + func _on_zombie_dead(zombie_name: String) -> void: if self.name == zombie_name: kill() + func activate() -> void: # Start synchronizing the zombie once its spawned for everyone synchronizer.public_visibility = true @@ -169,7 +182,8 @@ func activate() -> void: show() set_physics_process(true) - collision_shape_2d.set_deferred("disabled",false) + collision_shape_2d.set_deferred("disabled", false) + func clean() -> void: # Hide the zombie before removing it to avoid bugs and desync. @@ -190,10 +204,11 @@ func clean() -> void: $ZombieDead.play() synchronizer.public_visibility = false - collision_shape_2d.set_deferred("disabled",true) + collision_shape_2d.set_deferred("disabled", true) hide() set_physics_process(false) + func kill() -> void: health_synchronizer.public_visibility = false diff --git a/src/MainMenu/JoinMenu/join_menu.gd b/src/MainMenu/JoinMenu/join_menu.gd index b0b79a3..f1288ee 100644 --- a/src/MainMenu/JoinMenu/join_menu.gd +++ b/src/MainMenu/JoinMenu/join_menu.gd @@ -8,39 +8,49 @@ extends Control var menu: MainMenu + func _ready() -> void: menu = get_parent() Lobby.join_failed.connect(_on_join_failed) Lobby.join_success.connect(_on_join_success) + func _on_join_button_pressed() -> void: menu.button_click.play() # Check for valid IP and port number if ip_edit.text == "": - var popup: MessagePopup = Messenger.create_popup("Invalid IP", "The IP address cannot be empty.") + var popup: MessagePopup = Messenger.create_popup( + "Invalid IP", "The IP address cannot be empty." + ) add_child(popup) return if port_edit.text == "": - var popup: MessagePopup = Messenger.create_popup("Invalid Port", "The port number cannot be empty.") + var popup: MessagePopup = Messenger.create_popup( + "Invalid Port", "The port number cannot be empty." + ) add_child(popup) return # Attempt to join a server var error: Error = Lobby.create_client(ip_edit.text, int(port_edit.text)) if error != OK: - var popup: MessagePopup = Messenger.create_popup("Unknown error", "Unknown error has been returned with code: " + str(error)) + var popup: MessagePopup = Messenger.create_popup( + "Unknown error", "Unknown error has been returned with code: " + str(error) + ) add_child(popup) return join_button.disabled = true back_button.disabled = true + func _on_join_success() -> void: join_button.disabled = false back_button.disabled = false + func _on_join_failed(quiet: bool) -> void: join_button.disabled = false back_button.disabled = false @@ -48,5 +58,7 @@ func _on_join_failed(quiet: bool) -> void: Lobby.clear_peer() if not quiet: - var popup: MessagePopup = Messenger.create_popup("Join Timeout", "Connection was terminated, because the game didn't respond.") + var popup: MessagePopup = Messenger.create_popup( + "Join Timeout", "Connection was terminated, because the game didn't respond." + ) add_child(popup) diff --git a/src/MainMenu/LobbyCreate/lobby_create.gd b/src/MainMenu/LobbyCreate/lobby_create.gd index 2e5abbf..a3e650a 100644 --- a/src/MainMenu/LobbyCreate/lobby_create.gd +++ b/src/MainMenu/LobbyCreate/lobby_create.gd @@ -1,21 +1,25 @@ extends Control -signal game_created() +signal game_created @onready var port_edit: LineEdit = %PortEdit @onready var player_amount_field: SpinBox = %PlayerAmountField var menu: MainMenu + func _ready() -> void: menu = get_parent() + func _on_host_button_pressed() -> void: menu.button_click.play() # Check if the port is valid if port_edit.text == "" and int(port_edit.text) != 0: - var popup: MessagePopup = Messenger.create_popup("Invalid Port", "The port number cannot be empty.") + var popup: MessagePopup = Messenger.create_popup( + "Invalid Port", "The port number cannot be empty." + ) add_child(popup) return @@ -25,5 +29,7 @@ func _on_host_button_pressed() -> void: if error == OK: game_created.emit() else: - var popup: MessagePopup = Messenger.create_popup(Lobby.get_error_title(error), Lobby.get_error_text(error)) + var popup: MessagePopup = Messenger.create_popup( + Lobby.get_error_title(error), Lobby.get_error_text(error) + ) add_child(popup) diff --git a/src/MainMenu/MainMenu.gd b/src/MainMenu/MainMenu.gd index 74c7c54..b94127b 100644 --- a/src/MainMenu/MainMenu.gd +++ b/src/MainMenu/MainMenu.gd @@ -13,6 +13,7 @@ class_name MainMenu extends CanvasLayer @onready var username_text: Label = $VBoxContainer/UsernameText @onready var version_text: Label = $VBoxContainer/VersionText + func _ready() -> void: print(int("ab1")) $MenuTheme.play() @@ -23,6 +24,7 @@ func _ready() -> void: version_text.text = "v" + ProjectSettings.get_setting("application/config/version") + func _on_multi_button_pressed() -> void: button_click.play() main_menu.hide() @@ -36,10 +38,12 @@ func _on_multi_button_pressed() -> void: else: game_select.show() + func _on_nick_edit_text_submitted(new_text: String) -> void: # Allow submitting the username with 'Enter' _on_confirm_button_pressed() + func _on_confirm_button_pressed() -> void: button_click.play() username_enter.reset() @@ -50,20 +54,23 @@ func _on_confirm_button_pressed() -> void: username_text.text = "Username: " + str(Lobby.player_username) username_enter.hide() - game_select.show() # Allow to join the game after entering the username + game_select.show() # Allow to join the game after entering the username else: username_enter.nick_edit.grab_focus() + func _on_host_game_button_pressed() -> void: button_click.play() game_select.hide() lobby_create.show() + func _on_menu_button_pressed() -> void: button_click.play() game_select.hide() main_menu.show() + func _on_back_button_pressed() -> void: button_click.play() Lobby.clear_peer() @@ -72,11 +79,13 @@ func _on_back_button_pressed() -> void: join_menu.hide() game_select.show() + func _on_join_game_button_pressed() -> void: button_click.play() game_select.hide() join_menu.show() + func _on_leave_button_pressed() -> void: button_click.play() Lobby.clear_peer() @@ -84,6 +93,7 @@ func _on_leave_button_pressed() -> void: main_menu.show() lobby_menu.hide() + func _on_player_kicked(error_title: String, error_content: String) -> void: var popup: MessagePopup = Messenger.create_popup(error_title, error_content) add_child(popup) @@ -93,6 +103,7 @@ func _on_player_kicked(error_title: String, error_content: String) -> void: lobby_menu.hide() main_menu.show() + func _on_game_created() -> void: button_click.play() lobby_create.hide() @@ -101,16 +112,19 @@ func _on_game_created() -> void: lobby_menu.show() lobby_menu.draw_lobby(Lobby.connected_peers, Lobby.lobby_max) + func _on_settings_pressed() -> void: button_click.play() main_menu.hide() settings.show() + func _on_credits_pressed() -> void: button_click.play() main_menu.hide() credits.show() + func _on_settings_menu_button_pressed() -> void: button_click.play() username_text.text = "Username: " + Lobby.player_username diff --git a/src/MainMenu/Settings/settings.gd b/src/MainMenu/Settings/settings.gd index f9a10fe..be27c3f 100644 --- a/src/MainMenu/Settings/settings.gd +++ b/src/MainMenu/Settings/settings.gd @@ -8,9 +8,11 @@ extends Control @onready var sfx_slider: HSlider = %SfxSlider @onready var nick_edit: LineEdit = %NickEdit + func _ready() -> void: update() + func update() -> void: nick_edit.text = Lobby.player_username @@ -18,32 +20,45 @@ func update() -> void: update_sliders() update_buses() + func update_text() -> void: master_text.text = "Master Volume: " + str(Lobby.master_volume) music_text.text = "Music Volume: " + str(Lobby.music_volume) sfx_text.text = "SFX Volume: " + str(Lobby.sfx_volume) + func update_sliders() -> void: master_slider.value = Lobby.master_volume music_slider.value = Lobby.music_volume sfx_slider.value = Lobby.sfx_volume + func update_buses() -> void: - AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Master"), linear_to_db(Lobby.master_volume/100.0)) - AudioServer.set_bus_volume_db(AudioServer.get_bus_index("Music"), linear_to_db(Lobby.music_volume/100.0)) - AudioServer.set_bus_volume_db(AudioServer.get_bus_index("SFX"), linear_to_db(Lobby.sfx_volume/100.0)) + AudioServer.set_bus_volume_db( + AudioServer.get_bus_index("Master"), linear_to_db(Lobby.master_volume / 100.0) + ) + AudioServer.set_bus_volume_db( + AudioServer.get_bus_index("Music"), linear_to_db(Lobby.music_volume / 100.0) + ) + AudioServer.set_bus_volume_db( + AudioServer.get_bus_index("SFX"), linear_to_db(Lobby.sfx_volume / 100.0) + ) + func _on_master_slider_value_changed(value: float) -> void: Lobby.master_volume = value update() + func _on_music_slider_value_changed(value: float) -> void: Lobby.music_volume = value update() + func _on_sfx_slider_value_changed(value: float) -> void: Lobby.sfx_volume = value update() + func _on_nick_edit_text_changed(new_text: String) -> void: Lobby.player_username = new_text diff --git a/src/MainMenu/UsernameEnter/username_enter.gd b/src/MainMenu/UsernameEnter/username_enter.gd index f08b24f..99bf091 100644 --- a/src/MainMenu/UsernameEnter/username_enter.gd +++ b/src/MainMenu/UsernameEnter/username_enter.gd @@ -3,9 +3,11 @@ class_name UsernameEnter extends Control @onready var nick_edit: LineEdit = %NickEdit @onready var desc: Label = $Panel/VBoxContainer/VBoxContainer2/Desc + func reset() -> void: desc.text = "To play multiplayer you need a username. enter it in the field below. The username cannot have more than 20 characters." + func is_username_correct() -> bool: # Check if the username isn't empty var username: String = nick_edit.text.strip_edges() diff --git a/src/Network/Lobby.gd b/src/Network/Lobby.gd index 0609fe1..8a819d9 100644 --- a/src/Network/Lobby.gd +++ b/src/Network/Lobby.gd @@ -2,11 +2,11 @@ extends Node signal player_kicked(error_title: String, error_content: String) -signal join_success() +signal join_success signal join_failed(quiet: bool) -signal host_game_ready() -signal host_player_ready() +signal host_game_ready +signal host_player_ready var master_volume: float = 100 var music_volume: float = 100 @@ -23,15 +23,14 @@ var lobby_menu: LobbyMenu var player_username: String = "" var game_version: String = ProjectSettings.get_setting("application/config/version") as String -var connected_peers: Dictionary = { - -} +var connected_peers: Dictionary = {} var has_game_started: bool = false var has_game_ended: bool = false var is_host_game_ready: bool = false + func _ready() -> void: multiplayer.peer_connected.connect(_on_peer_connected) multiplayer.peer_disconnected.connect(_on_peer_disconnected) @@ -46,6 +45,7 @@ func _ready() -> void: join_timer.timeout.connect(func() -> void: join_failed.emit(false)) add_child(join_timer) + func create_server(port: int, max_players: int) -> Error: # Creates a server as a host and a multiplayer peer (host) lobby_port = port @@ -55,7 +55,7 @@ func create_server(port: int, max_players: int) -> Error: var error: Error = peer.create_server(lobby_port, lobby_max) if error != OK: - printerr("Cannot host due to error ",error) + printerr("Cannot host due to error ", error) return error peer.get_host().compress(ENetConnection.COMPRESS_RANGE_CODER) @@ -63,6 +63,7 @@ func create_server(port: int, max_players: int) -> Error: connected_peers[1] = player_username return error + func create_client(ip: String, port: int) -> Error: # Creates a client and tries to join the given server (lobby) lobby_ip = ip @@ -72,7 +73,7 @@ func create_client(ip: String, port: int) -> Error: var error: Error = peer.create_client(lobby_ip, lobby_port) if error != OK: - printerr("Cannot host due to error ",error) + printerr("Cannot host due to error ", error) return error peer.get_host().compress(ENetConnection.COMPRESS_RANGE_CODER) @@ -82,6 +83,7 @@ func create_client(ip: String, port: int) -> Error: return error + func clear_peer() -> void: # Clear everything from the current session has_game_ended = false @@ -96,31 +98,36 @@ func clear_peer() -> void: peer = null + func game_started() -> void: if multiplayer.is_server(): has_game_started = true rpc("start_game") -@rpc("authority","call_local","reliable") + +@rpc("authority", "call_local", "reliable") func start_game() -> void: # Starts the game for every player in the lobby has_game_started = true Composer.load_scene("res://src/Game/Game.tscn") -@rpc("authority","call_remote","reliable") + +@rpc("authority", "call_remote", "reliable") func kick_peer(title: String, content: String) -> void: # Kicks a player from the server, only the host can trigger this on other clients. player_kicked.emit(title, content) join_failed.emit(true) -@rpc("authority","call_local","reliable") + +@rpc("authority", "call_local", "reliable") func set_host_game_ready() -> void: # Notify all clients that the host has joined and can receive RPCs. is_host_game_ready = true host_game_ready.emit() -@rpc("authority","call_remote","reliable") + +@rpc("authority", "call_remote", "reliable") func greet_peer(peers: Dictionary, max_players: int) -> void: # This function is sent from the host to a client upon joining. # Send server info for the UI and request client info. @@ -131,7 +138,8 @@ func greet_peer(peers: Dictionary, max_players: int) -> void: rpc("peer_send_info", multiplayer.get_unique_id(), player_username, game_version) -@rpc("any_peer","call_remote","reliable") + +@rpc("any_peer", "call_remote", "reliable") func peer_send_info(id: int, username: String, version: String) -> void: # This function is sent from a client to the host after greet_peer # It sends the info about the client to everyone else in the lobby @@ -139,7 +147,12 @@ func peer_send_info(id: int, username: String, version: String) -> void: # Check if the client is running the same game version if multiplayer.is_server() and game_version != version: printerr("Invalid game version for peer ", str(id)) - rpc_id(id,"kick_peer","Invalid Version","To join the lobby, both the host and the client must have the same game version.") + rpc_id( + id, + "kick_peer", + "Invalid Version", + "To join the lobby, both the host and the client must have the same game version." + ) return if connected_peers.keys().has(id): @@ -150,17 +163,28 @@ func peer_send_info(id: int, username: String, version: String) -> void: lobby_menu.check_if_game_can_start() lobby_menu.rpc("draw_lobby", connected_peers, lobby_max) + func _on_peer_connected(id: int) -> void: # Do not allow players to join if the game is started if has_game_started: if multiplayer.is_server(): - rpc_id(id,"kick_peer","Game has started","You cannot join this game, because the game has already started.") + rpc_id( + id, + "kick_peer", + "Game has started", + "You cannot join this game, because the game has already started." + ) return # Do not allow players to join if we're exceeding the lobby size if connected_peers.size() == lobby_max: if multiplayer.is_server(): - rpc_id(id,"kick_peer","Lobby is full","You cannot join this game, because the lobby is full.") + rpc_id( + id, + "kick_peer", + "Lobby is full", + "You cannot join this game, because the lobby is full." + ) return # Wait for the data of the peer to be sent @@ -173,6 +197,7 @@ func _on_peer_connected(id: int) -> void: Messenger.message(str(id) + " Has connected") + func _on_peer_disconnected(id: int) -> void: # Remove the peer from the lobby or game @@ -187,8 +212,10 @@ func _on_peer_disconnected(id: int) -> void: lobby_menu.rpc("draw_lobby", connected_peers, lobby_max) + ### CLIENT SIGNALS + func _on_connected_to_server() -> void: if join_timer: join_timer.stop() @@ -199,14 +226,17 @@ func _on_connected_to_server() -> void: Messenger.message("Connected to server") join_success.emit() + func _on_connection_failed() -> void: Messenger.message("Couldn't connect") + func _on_server_disconnected() -> void: if has_game_ended: return - player_kicked.emit("Host Left","The game has ended because host has left.") + player_kicked.emit("Host Left", "The game has ended because host has left.") + func get_error_title(error: Error) -> String: match error: @@ -217,6 +247,7 @@ func get_error_title(error: Error) -> String: _: return "Unknown Error" + func get_error_text(error: Error) -> String: match error: ERR_ALREADY_IN_USE: diff --git a/src/Network/LobbyMenu/LobbyPlayer/lobby_player.gd b/src/Network/LobbyMenu/LobbyPlayer/lobby_player.gd index eeacb96..768614d 100644 --- a/src/Network/LobbyMenu/LobbyPlayer/lobby_player.gd +++ b/src/Network/LobbyMenu/LobbyPlayer/lobby_player.gd @@ -17,9 +17,11 @@ signal player_kicked(player_name: String) return player_name_text.text + func remove_kick() -> void: kick_button.get_parent().hide() + func _on_kick_button_pressed() -> void: $ButtonClick.play() player_kicked.emit(player_name) diff --git a/src/Network/LobbyMenu/lobby_menu.gd b/src/Network/LobbyMenu/lobby_menu.gd index 309926d..7a9dd5a 100644 --- a/src/Network/LobbyMenu/lobby_menu.gd +++ b/src/Network/LobbyMenu/lobby_menu.gd @@ -7,15 +7,15 @@ class_name LobbyMenu extends Control var menu: MainMenu -var has_player_joined: Dictionary = { +var has_player_joined: Dictionary = {} -} func _ready() -> void: menu = get_parent() Lobby.lobby_menu = self lobby_title.text = "Please wait, joining..." + @rpc("authority", "call_local", "reliable") func draw_lobby(players: Dictionary, max_players: int) -> void: # Only allow the game to be started by the host @@ -23,7 +23,9 @@ func draw_lobby(players: Dictionary, max_players: int) -> void: start_button_control.show() if players.size() > 0: - lobby_title.text = "Players in Lobby (" + str(players.size()) + "/" + str(max_players) + "):" + lobby_title.text = ( + "Players in Lobby (" + str(players.size()) + "/" + str(max_players) + "):" + ) # Display all players and their usernames in the lobby. for idx in range(0, player_list.get_child_count()): @@ -47,19 +49,25 @@ func draw_lobby(players: Dictionary, max_players: int) -> void: lobby_player.show() + func check_if_game_can_start() -> void: # Start the game only when all players have joined and no connections are pending. if multiplayer.is_server(): start_button.disabled = has_player_joined.values().has(false) + func _on_start_button_pressed() -> void: menu.button_click.play() if multiplayer.is_server(): Lobby.game_started() + func _on_lobby_player_player_kicked(player_name: String) -> void: if multiplayer.is_server(): - Lobby.rpc_id(player_name.to_int(),"kick_peer", "Kicked", "You have been kicked from the lobby.") + Lobby.rpc_id( + player_name.to_int(), "kick_peer", "Kicked", "You have been kicked from the lobby." + ) + func _exit_tree() -> void: Lobby.lobby_menu = null diff --git a/src/Network/MessagePopup/message_popup.gd b/src/Network/MessagePopup/message_popup.gd index 8460cfb..2528741 100644 --- a/src/Network/MessagePopup/message_popup.gd +++ b/src/Network/MessagePopup/message_popup.gd @@ -1,6 +1,6 @@ class_name MessagePopup extends Control -signal ok_pressed() +signal ok_pressed @onready var title: Label = %Title @onready var content: Label = %Content @@ -20,11 +20,13 @@ signal ok_pressed() content.text = val + func _ready() -> void: $Error.play() process_mode = Node.PROCESS_MODE_ALWAYS ok_button.pressed.connect(default_ok) + func default_ok() -> void: $ButtonClick.play() ok_pressed.emit() diff --git a/src/Network/Messenger.gd b/src/Network/Messenger.gd index 9167040..2b20012 100644 --- a/src/Network/Messenger.gd +++ b/src/Network/Messenger.gd @@ -2,9 +2,11 @@ extends Node const MESSAGE_POPUP = preload("res://src/Network/MessagePopup/MessagePopup.tscn") + func message(content: String) -> void: prints("(", multiplayer.get_unique_id(), "): ", content) + func create_popup(title: String, content: String) -> MessagePopup: var popup: MessagePopup = MESSAGE_POPUP.instantiate()