using Content.Server.Chat.Managers; using Content.Server.Database.Migrations.Postgres; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; using Content.Shared.Chat; using Content.Shared.GameTicking.Components; using Content.Shared.Interaction.Events; using Content.Shared.Mind; using Content.Shared.Mobs; using Content.Shared.Players; using Robust.Server.Player; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.GameTicking.Rules; /// /// This handles logic and interactions related to /// public sealed class RespawnRuleSystem : GameRuleSystem { [Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly StationSystem _station = default!; /// public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnSuicide); SubscribeLocalEvent(OnMobStateChanged); } public override void Update(float frameTime) { base.Update(frameTime); if (_station.GetStations().FirstOrNull() is not { } station) return; foreach (var tracker in EntityQuery()) { foreach (var (player, time) in tracker.RespawnQueue) { if (_timing.CurTime < time) continue; if (!_playerManager.TryGetSessionById(player, out var session)) continue; //This autorespawner is disabled since people can manually return to the lobby. //We just check on the spawn event if the player is not in the queue. //if (session.GetMind() is { } mind && TryComp(mind, out var mindComp) && mindComp.OwnedEntity.HasValue) // QueueDel(mindComp.OwnedEntity.Value); //GameTicker.MakeJoinGame(session, station, silent: true); tracker.RespawnQueue.Remove(player); } } } private void OnSuicide(SuicideEvent ev) { if (!TryComp(ev.Victim, out var actor)) return; var query = EntityQueryEnumerator(); while (query.MoveNext(out _, out var respawn)) { if (respawn.Players.Remove(actor.PlayerSession.UserId)) QueueDel(ev.Victim); } } private void OnMobStateChanged(MobStateChangedEvent args) { if (args.NewMobState != MobState.Dead) return; if (!TryComp(args.Target, out var actor)) return; var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var respawnRule, out var tracker, out var rule)) { if (!GameTicker.IsGameRuleActive(uid, rule)) continue; if (respawnRule.AlwaysRespawnDead) AddToTracker(actor.PlayerSession.UserId, (uid, tracker)); if (RespawnPlayer((args.Target, actor), (uid, tracker))) break; } } /// /// Attempts to directly respawn a player, skipping the lobby screen. /// public bool RespawnPlayer(Entity player, Entity respawnTracker) { if (!respawnTracker.Comp.Players.Contains(player.Comp.PlayerSession.UserId) || respawnTracker.Comp.RespawnQueue.ContainsKey(player.Comp.PlayerSession.UserId)) return false; if (respawnTracker.Comp.RespawnDelay == TimeSpan.Zero) { if (_station.GetStations().FirstOrNull() is not { } station) return false; if (respawnTracker.Comp.DeleteBody) QueueDel(player); GameTicker.MakeJoinGame(player.Comp.PlayerSession, station, silent: true); return false; } var msg = Loc.GetString("rule-respawn-in-seconds", ("second", respawnTracker.Comp.RespawnDelay.TotalSeconds)); var wrappedMsg = Loc.GetString("chat-manager-server-wrap-message", ("message", msg)); _chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMsg, respawnTracker, false, player.Comp.PlayerSession.Channel, Color.LimeGreen); respawnTracker.Comp.RespawnQueue[player.Comp.PlayerSession.UserId] = _timing.CurTime + respawnTracker.Comp.RespawnDelay; return true; } /// /// Adds a given player to the respawn tracker, ensuring that they are respawned if they die. /// public void AddToTracker(Entity player, Entity respawnTracker) { if (!Resolve(respawnTracker, ref respawnTracker.Comp) || !Resolve(player, ref player.Comp, false)) return; AddToTracker(player.Comp.PlayerSession.UserId, (respawnTracker, respawnTracker.Comp)); } /// /// Adds a given player to the respawn tracker, ensuring that they are respawned if they die. /// public void AddToTracker(NetUserId id, Entity tracker) { tracker.Comp.Players.Add(id); } }