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;
using Content.Server.Chat.Systems;
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!;
[Dependency] private readonly ChatSystem _chat = default!;
private bool _announced = false;
///
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnSuicide);
SubscribeLocalEvent(OnMobStateChanged);
foreach (var tracker in EntityQuery())
{
tracker.GlobalTimer = _timing.CurTime + tracker.RespawnDelay;
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
if (_station.GetStations().FirstOrNull() is not { } station)
return;
foreach (var tracker in EntityQuery())
{
if (!tracker.Fixed)
{
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);
}
}
else
{
if (_timing.CurTime > tracker.GlobalTimer && _announced == false)
{
_announced = true;
var announcementMessage = "Reinforcements are arriving!";
RespawnFixed(tracker);
_chat.DispatchGlobalAnnouncement(announcementMessage, "Round", false, null, Color.Yellow);
tracker.GlobalTimer = _timing.CurTime + tracker.RespawnDelay;
_announced = false;
}
}
}
}
private void RespawnFixed(RespawnTrackerComponent tracker)
{
foreach (var (player, time) in tracker.RespawnQueue)
{
tracker.RespawnQueue.Remove(player);
}
}
private void OnSuicide(SuicideEvent ev)
{
if (!TryComp(ev.Victim, 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));
}
}
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);
}
}