Implement respawning

This commit is contained in:
Sofia 2026-07-20 22:43:10 +03:00
parent 41c50a764e
commit c8d11f8a54
4 changed files with 105 additions and 52 deletions

View File

@ -3,4 +3,5 @@
[ext_resource type="MapResource" uid="uid://c7n1tacc7a73t" path="res://maps/default_map.tres" id="1_glms7"] [ext_resource type="MapResource" uid="uid://c7n1tacc7a73t" path="res://maps/default_map.tres" id="1_glms7"]
[node name="game_manager" type="GameManager" unique_id=1442044769] [node name="game_manager" type="GameManager" unique_id=1442044769]
respawn_timer = 5.0
maps = Array[MapResource]([ExtResource("1_glms7")]) maps = Array[MapResource]([ExtResource("1_glms7")])

View File

@ -9,7 +9,7 @@ use crate::{
net::{ net::{
network_manager::{ network_manager::{
NetworkManager, NetworkManager,
Package::{self, Kill, SelfSync, TakeDamage}, Package::{self, Kill, SelfSync, SpawnPlayer, TakeDamage},
PeerKind, SyncPackage, PeerKind, SyncPackage,
}, },
util::NetVector3, util::NetVector3,
@ -22,6 +22,9 @@ pub const GAME_MANAGER_GLOBAL: &str = "GameManagerGlobal";
#[derive(GodotClass)] #[derive(GodotClass)]
#[class(base=Node, init)] #[class(base=Node, init)]
pub struct GameManager { pub struct GameManager {
#[export]
pub respawn_timer: f64,
pub game: Option<Gd<Game>>, pub game: Option<Gd<Game>>,
base: Base<Node>, base: Base<Node>,
@ -51,6 +54,7 @@ impl GameManager {
let mut game = Game::new_alloc(); let mut game = Game::new_alloc();
game.bind_mut().is_host = is_host; game.bind_mut().is_host = is_host;
game.bind_mut().maps = self.maps.clone(); game.bind_mut().maps = self.maps.clone();
game.bind_mut().respawn_timer = self.respawn_timer;
self.run_deferred(|s| { self.run_deferred(|s| {
if let Some(game) = &s.game { if let Some(game) = &s.game {
@ -78,6 +82,9 @@ impl GameManager {
#[derive(GodotClass)] #[derive(GodotClass)]
#[class(base=Node, init)] #[class(base=Node, init)]
pub struct Game { pub struct Game {
#[export]
pub respawn_timer: f64,
pub selected_map_idx: u8, pub selected_map_idx: u8,
pub selected_map: Option<Gd<MapResource>>, pub selected_map: Option<Gd<MapResource>>,
pub maps: Array<Gd<MapResource>>, pub maps: Array<Gd<MapResource>>,
@ -143,6 +150,25 @@ impl INode for Game {
} }
} }
} }
if let Some(peer) = &mut NetworkManager::singleton().bind_mut().peer {
if let PeerKind::Server(peer, _) = peer {
for player in self.players.clone() {
if let Some(character) = player.character {
if character.dyn_bind().dead_for() > self.respawn_timer {
let character = self.spawn_player_server(player.id);
if let Some(character) = character {
peer.broadcast_reliable(SpawnPlayer(
player.id,
character.dyn_bind().get_transform(),
));
}
}
}
}
}
}
} }
} }
@ -272,64 +298,72 @@ impl Game {
} else { } else {
self.run_deferred(|s| { self.run_deferred(|s| {
for player in s.players.clone() { for player in s.players.clone() {
let player_id = player.id; s.spawn_player_server(player.id);
if let Some(mut map) = s.current_map.clone() {
let mut character = map.bind_mut().spawn_player(
&player,
player.id == s.self_id.unwrap_or(0),
None,
);
if let Some(character) = &mut character {
if let Ok(mut local_player) =
character.clone().try_cast::<LocalPlayer>()
{
local_player
.bind_mut()
.signals()
.on_die()
.connect_other(s, move |s| s.on_death(player_id));
local_player
.bind_mut()
.signals()
.on_take_damage()
.connect_other(s, move |s, damage| {
s.on_take_damage(player_id, damage)
});
} else if let Ok(mut remote_player) =
character.clone().try_cast::<RemotePlayer>()
{
remote_player
.bind_mut()
.signals()
.on_die()
.connect_other(s, move |s| s.on_death(player_id));
remote_player
.bind_mut()
.signals()
.on_take_damage()
.connect_other(s, move |s, damage| {
s.on_take_damage(player_id, damage)
});
}
}
if let Some(player) = s.find_player_mut(player_id) {
player.character = character;
}
}
} }
}); });
} }
} }
} }
pub fn spawn_player_server(&mut self, id: u16) -> Option<DynGd<CharacterBody3D, dyn IPlayer>> {
if let Some(player) = self.find_player_mut(id)
&& let Some(character) = &mut player.character
{
// This is a respawn, remove old character
character.queue_free();
}
if let Some(mut map) = self.current_map.clone() {
let mut character =
map.bind_mut()
.spawn_player(id, id == self.self_id.unwrap_or(0), None);
if let Some(character) = &mut character {
if let Ok(mut local_player) = character.clone().try_cast::<LocalPlayer>() {
local_player
.bind_mut()
.signals()
.on_die()
.connect_other(self, move |s| s.on_death(id));
local_player
.bind_mut()
.signals()
.on_take_damage()
.connect_other(self, move |s, damage| s.on_take_damage(id, damage));
} else if let Ok(mut remote_player) = character.clone().try_cast::<RemotePlayer>() {
remote_player
.bind_mut()
.signals()
.on_die()
.connect_other(self, move |s| s.on_death(id));
remote_player
.bind_mut()
.signals()
.on_take_damage()
.connect_other(self, move |s, damage| s.on_take_damage(id, damage));
}
}
if let Some(player) = self.find_player_mut(id) {
player.character = character.clone();
}
character
} else {
None
}
}
// Method called on the client-side whenever a player has spawned // Method called on the client-side whenever a player has spawned
pub fn spawn_player_client(&mut self, id: u16, transform: NetPlayerTransform) { pub fn spawn_player_client(&mut self, id: u16, transform: NetPlayerTransform) {
if let Some(map) = &mut self.current_map { if let Some(map) = &mut self.current_map {
if let Some(player) = self.players.iter_mut().find(|p| p.id == id) { if let Some(player) = self.players.iter_mut().find(|p| p.id == id) {
if let Some(character) = &mut player.character {
// This is a respawn, delete old character
character.queue_free();
}
let character = map.bind_mut().spawn_player( let character = map.bind_mut().spawn_player(
player, player.id,
player.id == self.self_id.unwrap_or(0), player.id == self.self_id.unwrap_or(0),
Some(transform), Some(transform),
); );
@ -410,7 +444,6 @@ impl Game {
// Handle dying, from server to client // Handle dying, from server to client
pub fn handle_kill(&mut self, player_id: u16) { pub fn handle_kill(&mut self, player_id: u16) {
godot_print!("Got kill!");
if let Some(player) = self.find_player_mut(player_id) { if let Some(player) = self.find_player_mut(player_id) {
if let Some(character) = &mut player.character { if let Some(character) = &mut player.character {
character.dyn_bind_mut().kill(); character.dyn_bind_mut().kill();

View File

@ -34,14 +34,14 @@ impl Map {
pub fn spawn_player( pub fn spawn_player(
&mut self, &mut self,
player: &Player, player_id: u16,
is_local: bool, is_local: bool,
transform: Option<NetPlayerTransform>, transform: Option<NetPlayerTransform>,
) -> Option<DynGd<CharacterBody3D, dyn IPlayer + 'static>> { ) -> Option<DynGd<CharacterBody3D, dyn IPlayer + 'static>> {
if is_local { if is_local {
if let Some(local_player) = &self.local_player { if let Some(local_player) = &self.local_player {
let mut node = local_player.instantiate_as::<LocalPlayer>(); let mut node = local_player.instantiate_as::<LocalPlayer>();
node.bind_mut().player_id = Some(player.id); node.bind_mut().player_id = Some(player_id);
self.base_mut().add_child(&node); self.base_mut().add_child(&node);
if let Some(transform) = transform { if let Some(transform) = transform {
node.bind_mut() node.bind_mut()
@ -63,7 +63,7 @@ impl Map {
} }
} else if let Some(remote_player) = &self.remote_player { } else if let Some(remote_player) = &self.remote_player {
let mut node = remote_player.instantiate_as::<RemotePlayer>(); let mut node = remote_player.instantiate_as::<RemotePlayer>();
node.bind_mut().player_id = Some(player.id); node.bind_mut().player_id = Some(player_id);
self.base_mut().add_child(&node); self.base_mut().add_child(&node);
if let Some(transform) = transform { if let Some(transform) = transform {
node.bind_mut() node.bind_mut()

View File

@ -48,6 +48,7 @@ pub trait IPlayer {
fn take_damage(&mut self, damage: i32, kill: bool); fn take_damage(&mut self, damage: i32, kill: bool);
fn is_dead(&self) -> bool; fn is_dead(&self) -> bool;
fn kill(&mut self); fn kill(&mut self);
fn dead_for(&self) -> f64;
} }
pub trait PlayerMovement { pub trait PlayerMovement {
@ -108,6 +109,7 @@ pub struct LocalPlayer {
shoot_cd: f64, shoot_cd: f64,
is_dead: bool, is_dead: bool,
dead_for: f64,
locked: bool, locked: bool,
@ -201,6 +203,10 @@ impl IPlayer for LocalPlayer {
self.is_dead self.is_dead
} }
fn dead_for(&self) -> f64 {
self.dead_for
}
fn kill(&mut self) { fn kill(&mut self) {
self.run_deferred(move |s| s.signals().on_die().emit()); self.run_deferred(move |s| s.signals().on_die().emit());
if let Some(mut mesh) = self.soldier_mesh.take() { if let Some(mut mesh) = self.soldier_mesh.take() {
@ -243,6 +249,10 @@ impl ICharacterBody3D for LocalPlayer {
} }
fn process(&mut self, delta: f64) { fn process(&mut self, delta: f64) {
if self.is_dead {
self.dead_for += delta;
}
self.shoot_cd -= delta; self.shoot_cd -= delta;
let input_direction = if self.locked { let input_direction = if self.locked {
@ -381,6 +391,7 @@ pub struct RemotePlayer {
look_up: f32, look_up: f32,
is_dead: bool, is_dead: bool,
dead_for: f64,
shoot_cd: f64, shoot_cd: f64,
@ -472,6 +483,10 @@ impl IPlayer for RemotePlayer {
self.is_dead self.is_dead
} }
fn dead_for(&self) -> f64 {
self.dead_for
}
fn kill(&mut self) { fn kill(&mut self) {
self.run_deferred(move |s| s.signals().on_die().emit()); self.run_deferred(move |s| s.signals().on_die().emit());
if let Some(mut mesh) = self.soldier_mesh.take() { if let Some(mut mesh) = self.soldier_mesh.take() {
@ -499,6 +514,10 @@ impl ICharacterBody3D for RemotePlayer {
} }
fn process(&mut self, delta: f64) { fn process(&mut self, delta: f64) {
if self.is_dead {
self.dead_for += delta;
}
self.player_update(delta); self.player_update(delta);
self.shoot_cd -= delta; self.shoot_cd -= delta;