TeamDeathMatchRuleSystem.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. using System.Linq;
  2. using Content.Server.Administration.Commands;
  3. using Content.Server.GameTicking.Rules.Components;
  4. using Content.Server.KillTracking;
  5. using Content.Server.Mind;
  6. using Content.Server.Points;
  7. using Content.Server.RoundEnd;
  8. using Content.Server.Station.Systems;
  9. using Content.Server.NPC.Systems;
  10. using Content.Shared.GameTicking;
  11. using Content.Shared.GameTicking.Components;
  12. using Content.Shared.NPC.Components;
  13. using Content.Shared.Points;
  14. using Content.Shared.Storage;
  15. using Robust.Server.GameObjects;
  16. using Robust.Server.Player;
  17. using Robust.Shared.Utility;
  18. using Content.Shared.NPC.Systems;
  19. namespace Content.Server.GameTicking.Rules;
  20. /// <summary>
  21. /// Manages <see cref="TeamDeathMatchRuleComponent"/>
  22. /// </summary>
  23. public sealed class TeamDeathMatchRuleSystem : GameRuleSystem<TeamDeathMatchRuleComponent>
  24. {
  25. [Dependency] private readonly IPlayerManager _player = default!;
  26. [Dependency] private readonly MindSystem _mind = default!;
  27. [Dependency] private readonly PointSystem _point = default!;
  28. [Dependency] private readonly RespawnRuleSystem _respawn = default!;
  29. [Dependency] private readonly RoundEndSystem _roundEnd = default!;
  30. [Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
  31. [Dependency] private readonly NpcFactionSystem _factionSystem = default!; // Added dependency
  32. [Dependency] private readonly TransformSystem _transform = default!;
  33. public override void Initialize()
  34. {
  35. base.Initialize();
  36. SubscribeLocalEvent<PlayerBeforeSpawnEvent>(OnBeforeSpawn);
  37. SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnSpawnComplete);
  38. SubscribeLocalEvent<KillReportedEvent>(OnKillReported);
  39. }
  40. private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev)
  41. {
  42. var query = EntityQueryEnumerator<TeamDeathMatchRuleComponent, RespawnTrackerComponent, GameRuleComponent>();
  43. while (query.MoveNext(out var uid, out var dm, out var tracker, out var rule))
  44. {
  45. if (!GameTicker.IsGameRuleActive(uid, rule))
  46. continue;
  47. var newMind = _mind.CreateMind(ev.Player.UserId, ev.Profile.Name);
  48. _mind.SetUserId(newMind, ev.Player.UserId);
  49. var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(ev.Station, null, ev.Profile);
  50. DebugTools.AssertNotNull(mobMaybe);
  51. var mob = mobMaybe!.Value;
  52. _mind.TransferTo(newMind, mob);
  53. EnsureComp<KillTrackerComponent>(mob);
  54. ev.Handled = true;
  55. break;
  56. }
  57. }
  58. private void OnSpawnComplete(PlayerSpawnCompleteEvent ev)
  59. {
  60. EnsureComp<KillTrackerComponent>(ev.Mob);
  61. var query = EntityQueryEnumerator<TeamDeathMatchRuleComponent, GameRuleComponent>();
  62. while (query.MoveNext(out var uid, out var component, out var rule))
  63. {
  64. if (!GameTicker.IsGameRuleActive(uid, rule))
  65. continue;
  66. if (component.Team1 == "")
  67. {
  68. if (TryComp<NpcFactionMemberComponent>(ev.Mob, out var npc))
  69. {
  70. if (npc.Factions.Count > 0 && npc.Factions.First() != component.Team2)
  71. {
  72. component.Team1 = npc.Factions.First();
  73. }
  74. }
  75. }
  76. if (component.Team2 == "")
  77. {
  78. if (TryComp<NpcFactionMemberComponent>(ev.Mob, out var npc))
  79. {
  80. if (npc.Factions.Count > 0 && npc.Factions.First() != component.Team1)
  81. {
  82. component.Team2 = npc.Factions.First();
  83. }
  84. }
  85. }
  86. }
  87. }
  88. private void OnKillReported(ref KillReportedEvent ev)
  89. {
  90. var query = EntityQueryEnumerator<TeamDeathMatchRuleComponent, GameRuleComponent>();
  91. while (query.MoveNext(out var uid, out var dm, out var rule))
  92. {
  93. if (!GameTicker.IsGameRuleActive(uid, rule))
  94. continue;
  95. // Check if the killed entity is part of either team using FactionSystem
  96. // This avoids potential direct access permission issues with NpcFactionMemberComponent.Factions
  97. if (HasComp<NpcFactionMemberComponent>(ev.Entity)) // Ensure the component exists before checking factions
  98. {
  99. if (_factionSystem.IsMember(ev.Entity, dm.Team1))
  100. {
  101. dm.Team1Deaths += 1;
  102. dm.Team2Kills += 1;
  103. }
  104. else if (_factionSystem.IsMember(ev.Entity, dm.Team2))
  105. {
  106. dm.Team2Deaths += 1;
  107. dm.Team1Kills += 1;
  108. }
  109. }
  110. }
  111. }
  112. protected override void AppendRoundEndText(EntityUid uid, TeamDeathMatchRuleComponent component, GameRuleComponent gameRule, ref RoundEndTextAppendEvent args)
  113. {
  114. // If we are using points, use them to display winner
  115. if (component.Team1Points > 0 && component.Team2Points > 0)
  116. {
  117. if (component.Team1Points > component.Team2Points)
  118. {
  119. args.AddLine($"[color=lime]{component.Team1}[/color] has won!");
  120. }
  121. else if (component.Team1Points < component.Team2Points)
  122. {
  123. args.AddLine($"[color=lime]{component.Team2}[/color] has won!");
  124. }
  125. else
  126. {
  127. args.AddLine("The round ended in a [color=yellow]draw[/color]!");
  128. }
  129. }
  130. args.AddLine("");
  131. args.AddLine($"[color=cyan]{component.Team1}[/color]: {component.Team1Kills} Kills, {component.Team1Deaths} Deaths");
  132. args.AddLine("");
  133. args.AddLine($"[color=cyan]{component.Team2}[/color]: {component.Team2Kills} Kills, {component.Team2Deaths} Deaths");
  134. }
  135. }