Skip to content

Commit ac474a2

Browse files
authored
Merge pull request #12 from PuroSlavKing/Respawn
[Port] Respawn
2 parents 2a38654 + a2c66c9 commit ac474a2

13 files changed

Lines changed: 233 additions & 0 deletions

File tree

Content.Client/Ghost/GhostSystem.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,5 +185,11 @@ public void ToggleGhostVisibility(bool? visibility = null)
185185
{
186186
GhostVisibility = visibility ?? !GhostVisibility;
187187
}
188+
189+
public void ReturnToRound()
190+
{
191+
var msg = new GhostReturnToRoundRequest();
192+
RaiseNetworkEvent(msg);
193+
}
188194
}
189195
}

Content.Client/UserInterface/Systems/Ghost/GhostUIController.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ public void LoadGui()
128128
Gui.GhostBarWindow.SpawnButtonPressed += GhostBarSpawnPressed; // Goobstation - Ghost Bar
129129
Gui.TargetWindow.WarpClicked += OnWarpClicked;
130130
Gui.TargetWindow.OnGhostnadoClicked += OnGhostnadoClicked;
131+
Gui.ReturnToRoundPressed += ReturnToRound;
131132

132133
UpdateGui();
133134
}
@@ -143,6 +144,7 @@ public void UnloadGui()
143144
Gui.GhostBarPressed -= GhostBarPressed; // Goobstation - Ghost Bar
144145
Gui.GhostBarWindow.SpawnButtonPressed -= GhostBarSpawnPressed; // Goobstation - Ghost Bar
145146
Gui.TargetWindow.WarpClicked -= OnWarpClicked;
147+
Gui.ReturnToRoundPressed -= ReturnToRound;
146148

147149
Gui.Hide();
148150
}
@@ -152,6 +154,11 @@ private void ReturnToBody()
152154
_system?.ReturnToBody();
153155
}
154156

157+
private void ReturnToRound()
158+
{
159+
_system?.ReturnToRound();
160+
}
161+
155162
private void RequestWarps()
156163
{
157164
_system?.RequestWarps();

Content.Client/UserInterface/Systems/Ghost/Widgets/GhostGui.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
<Button Name="GhostWarpButton" Text="{Loc ghost-gui-ghost-warp-button}" />
77
<Button Name="GhostRolesButton" />
88
<Button Name="GhostBarButton" Text="{Loc 'ghost-target-window-ghostbar'}" /> <!-- Goobstation - Ghost Bar -->
9+
<Button Name="ReturnToRound" Text="{Loc ghost-gui-return-to-round-button}" />
910
</BoxContainer>
1011
</widgets:GhostGui>

Content.Client/UserInterface/Systems/Ghost/Widgets/GhostGui.xaml.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public sealed partial class GhostGui : UIWidget
1717
public event Action? ReturnToBodyPressed;
1818
public event Action? GhostRolesPressed;
1919
public event Action? GhostBarPressed; // Goobstation - Ghost Bar
20+
public event Action? ReturnToRoundPressed;
2021

2122
public GhostGui()
2223
{
@@ -32,6 +33,7 @@ public GhostGui()
3233
ReturnToBodyButton.OnPressed += _ => ReturnToBodyPressed?.Invoke();
3334
GhostRolesButton.OnPressed += _ => GhostRolesPressed?.Invoke();
3435
GhostBarButton.OnPressed += _ => GhostBarPressed?.Invoke(); // Goobstation - Ghost Bar
36+
ReturnToRound.OnPressed += _ => ReturnToRoundPressed?.Invoke();
3537
}
3638

3739
public void Hide()

Content.Server/GameTicking/GameTicker.GamePreset.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using Content.Server.Ghost;
15
using Content.Server.GameTicking.Presets;
26
using Content.Server.Maps;
37
using Content.Shared.CCVar;
@@ -6,11 +10,14 @@
610
using System.Diagnostics.CodeAnalysis;
711
using System.Linq;
812
using System.Threading.Tasks;
13+
using Content.Shared.Mobs.Systems;
914

1015
namespace Content.Server.GameTicking
1116
{
1217
public sealed partial class GameTicker
1318
{
19+
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
20+
[Dependency] private readonly GhostReturnToRoundSystem _ghostReturnToRound = default!;
1421
public const float PresetFailedCooldownIncrease = 30f;
1522

1623
/// <summary>

Content.Server/GameTicking/GameTicker.Spawning.cs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
using Content.Server.Station.Components;
99
using Content.Shared.Database;
1010
using Content.Shared.GameTicking;
11+
using Content.Shared.CCVar;
12+
using Content.Shared.Chat;
13+
using Content.Shared.Database;
1114
using Content.Shared.Mind;
1215
using Content.Shared.Players;
1316
using Content.Shared.Preferences;
@@ -174,6 +177,20 @@ private void SpawnPlayer(ICommonSession player,
174177
return;
175178
}
176179

180+
//Ghost system return to round, check for whether the character isn't the same.
181+
if (!_cfg.GetCVar(CCVars.GhostAllowSameCharacter) && lateJoin && !_adminManager.IsAdmin(player) && !CheckGhostReturnToRound(player, character, out var checkAvoid))
182+
{
183+
var message = checkAvoid
184+
? Loc.GetString("ghost-respawn-same-character-slightly-changed-name")
185+
: Loc.GetString("ghost-respawn-same-character");
186+
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
187+
188+
_chatManager.ChatMessageToOne(ChatChannel.Server, message, wrappedMessage,
189+
default, false, player.Channel, Color.Red);
190+
191+
return;
192+
}
193+
177194
// We raise this event to allow other systems to handle spawning this player themselves. (e.g. late-join wizard, etc)
178195
var bev = new PlayerBeforeSpawnEvent(player, character, jobId, lateJoin, station);
179196
RaiseLocalEvent(bev);
@@ -371,6 +388,68 @@ public void SpawnObserver(ICommonSession player)
371388
$"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}.");
372389
}
373390

391+
private bool CheckGhostReturnToRound(ICommonSession player, HumanoidCharacterProfile character, out bool checkAvoid)
392+
{
393+
checkAvoid = false;
394+
395+
var allPlayerMinds = EntityQuery<MindComponent>()
396+
.Where(mind => mind.OriginalOwnerUserId == player.UserId);
397+
398+
foreach (var mind in allPlayerMinds)
399+
{
400+
if (mind.CharacterName == character.Name)
401+
return false;
402+
403+
if (mind.CharacterName == null)
404+
continue;
405+
406+
var similarity = CalculateStringSimilarity(mind.CharacterName, character.Name);
407+
switch (similarity)
408+
{
409+
case >= 85f:
410+
_chatManager.SendAdminAlert(Loc.GetString("ghost-respawn-log-character-almost-same",
411+
("player", player.Name), ("try", false), ("oldName", mind.CharacterName),
412+
("newName", character.Name)));
413+
checkAvoid = true;
414+
415+
return false;
416+
case >= 50f:
417+
_chatManager.SendAdminAlert(Loc.GetString("ghost-respawn-log-character-almost-same",
418+
("player", player.Name), ("try", true), ("oldName", mind.CharacterName),
419+
("newName", character.Name)));
420+
421+
break;
422+
}
423+
}
424+
425+
return true;
426+
}
427+
428+
private float CalculateStringSimilarity(string str1, string str2)
429+
{
430+
var minLength = Math.Min(str1.Length, str2.Length);
431+
var matchingCharacters = 0;
432+
433+
for (var i = 0; i < minLength; i++)
434+
{
435+
if (str1[i] == str2[i])
436+
matchingCharacters++;
437+
}
438+
439+
float maxLength = Math.Max(str1.Length, str2.Length);
440+
var similarityPercentage = (matchingCharacters / maxLength) * 100;
441+
442+
return similarityPercentage;
443+
}
444+
445+
#region Mob Spawning Helpers
446+
private EntityUid SpawnObserverMob()
447+
{
448+
var coordinates = GetObserverSpawnPoint();
449+
return EntityManager.SpawnEntity(ObserverPrototypeName, coordinates);
450+
}
451+
#endregion
452+
374453
#region Spawn Points
375454

376455
public EntityCoordinates GetObserverSpawnPoint()
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using Content.Server.Administration.Logs;
2+
using Content.Server.Chat.Managers;
3+
using Content.Server.GameTicking;
4+
using Content.Shared.Database;
5+
using Content.Shared.CCVar;
6+
using Content.Shared.Ghost;
7+
using Robust.Server.Player;
8+
using Robust.Shared.Configuration;
9+
using Robust.Shared.Network;
10+
using Robust.Shared.Timing;
11+
12+
namespace Content.Server.Ghost;
13+
14+
public sealed class GhostReturnToRoundSystem : EntitySystem
15+
{
16+
[Dependency] private readonly IChatManager _chatManager = default!;
17+
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
18+
[Dependency] private readonly IPlayerManager _playerManager = default!;
19+
[Dependency] private readonly IGameTiming _gameTiming = default!;
20+
[Dependency] private readonly IConfigurationManager _cfg = default!;
21+
[Dependency] private readonly GameTicker _ticker = default!;
22+
23+
public override void Initialize()
24+
{
25+
SubscribeNetworkEvent<GhostReturnToRoundRequest>(OnGhostReturnToRoundRequest);
26+
}
27+
28+
private void OnGhostReturnToRoundRequest(GhostReturnToRoundRequest msg, EntitySessionEventArgs args)
29+
{
30+
var uid = args.SenderSession.AttachedEntity;
31+
32+
if (uid == null)
33+
return;
34+
35+
var connectedClient = args.SenderSession.Channel;
36+
var userId = args.SenderSession.UserId;
37+
38+
TryGhostReturnToRound(uid.Value, connectedClient, userId, out var message, out var wrappedMessage);
39+
40+
_chatManager.ChatMessageToOne(Shared.Chat.ChatChannel.Server,
41+
message,
42+
wrappedMessage,
43+
default,
44+
false,
45+
connectedClient,
46+
Color.Red);
47+
}
48+
49+
private void TryGhostReturnToRound(EntityUid uid, INetChannel connectedClient, NetUserId userId, out string message, out string wrappedMessage)
50+
{
51+
var maxPlayers = _cfg.GetCVar(CCVars.GhostRespawnMaxPlayers);
52+
if (_playerManager.PlayerCount >= maxPlayers)
53+
{
54+
message = Loc.GetString("ghost-respawn-max-players", ("players", maxPlayers));
55+
wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
56+
return;
57+
}
58+
59+
var deathTime = EnsureComp<GhostComponent>(uid).TimeOfDeath;
60+
var timeUntilRespawn = _cfg.GetCVar(CCVars.GhostRespawnTime);
61+
var timePast = (_gameTiming.CurTime - deathTime).TotalSeconds;
62+
if (timePast >= timeUntilRespawn)
63+
{
64+
_playerManager.TryGetSessionById(userId, out var targetPlayer);
65+
66+
if (targetPlayer != null)
67+
_ticker.Respawn(targetPlayer);
68+
69+
_adminLogger.Add(LogType.Mind, LogImpact.Medium, $"{Loc.GetString("ghost-respawn-log-return-to-lobby", ("userName", connectedClient.UserName))}");
70+
71+
message = Loc.GetString("ghost-respawn-window-rules-footer");
72+
wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
73+
74+
return;
75+
}
76+
77+
message = Loc.GetString("ghost-respawn-time-left", ("time", (int) (timeUntilRespawn - timePast)));
78+
wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
79+
}
80+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Robust.Shared.Configuration;
2+
3+
namespace Content.Shared.CCVar;
4+
5+
public sealed partial class CCVars
6+
{
7+
public static readonly CVarDef<int> GhostRespawnTime =
8+
CVarDef.Create("ghost.respawn_time", 15, CVar.SERVER | CVar.REPLICATED);
9+
10+
public static readonly CVarDef<int> GhostRespawnMaxPlayers =
11+
CVarDef.Create("ghost.respawn_max_players", 40, CVar.SERVER | CVar.REPLICATED);
12+
13+
public static readonly CVarDef<bool> GhostAllowSameCharacter =
14+
CVarDef.Create("ghost.allow_same_character", false, CVar.SERVER | CVar.REPLICATED);
15+
}

Content.Shared/Ghost/SharedGhostSystem.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,7 @@ public GhostUpdateGhostRoleCountEvent(int availableGhostRoleCount)
166166
AvailableGhostRoles = availableGhostRoleCount;
167167
}
168168
}
169+
170+
[Serializable, NetSerializable]
171+
public sealed class GhostReturnToRoundRequest : EntityEventArgs;
169172
}

Resources/Locale/en-US/ghost/ghost-gui.ftl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ ghost-roles-window-rules-footer = The button will enable after {$time} seconds (
3434
3535
ghost-return-to-body-title = Return to Body
3636
ghost-return-to-body-text = Med is so competent that you are being revived! Return to your body?
37+
ghost-gui-return-to-round-button = Return to round

0 commit comments

Comments
 (0)