Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Port] Respawn Button / Кнопка Респавна #56

Merged
merged 8 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Content.Client/Ghost/GhostSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,11 @@ public void ToggleGhostVisibility()
{
GhostVisibility = !GhostVisibility;
}

public void ReturnToRound() // WD EDIT
{
var msg = new GhostReturnToRoundRequest();
RaiseNetworkEvent(msg);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ public void LoadGui()
Gui.ReturnToBodyPressed += ReturnToBody;
Gui.GhostRolesPressed += GhostRolesPressed;
Gui.TargetWindow.WarpClicked += OnWarpClicked;
Gui.ReturnToRoundPressed += ReturnToRound; // WD EDIT

UpdateGui();
}
Expand All @@ -133,6 +134,7 @@ public void UnloadGui()
Gui.ReturnToBodyPressed -= ReturnToBody;
Gui.GhostRolesPressed -= GhostRolesPressed;
Gui.TargetWindow.WarpClicked -= OnWarpClicked;
Gui.ReturnToRoundPressed -= ReturnToRound; // WD EDIT

Gui.Hide();
}
Expand All @@ -142,6 +144,11 @@ private void ReturnToBody()
_system?.ReturnToBody();
}

private void ReturnToRound() // WD EDIT
{
_system?.ReturnToRound();
}

private void RequestWarps()
{
_system?.RequestWarps();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
<Button Name="ReturnToBodyButton" Text="{Loc ghost-gui-return-to-body-button}" />
<Button Name="GhostWarpButton" Text="{Loc ghost-gui-ghost-warp-button}" />
<Button Name="GhostRolesButton" />
<Button Name="ReturnToRound" Text="{Loc ghost-gui-return-to-round-button}" /> <!-- WD EDIT -->
</BoxContainer>
</widgets:GhostGui>
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public sealed partial class GhostGui : UIWidget
public event Action? RequestWarpsPressed;
public event Action? ReturnToBodyPressed;
public event Action? GhostRolesPressed;
public event Action? ReturnToRoundPressed; // WD EDIT

public GhostGui()
{
Expand All @@ -26,6 +27,7 @@ public GhostGui()
GhostWarpButton.OnPressed += _ => RequestWarpsPressed?.Invoke();
ReturnToBodyButton.OnPressed += _ => ReturnToBodyPressed?.Invoke();
GhostRolesButton.OnPressed += _ => GhostRolesPressed?.Invoke();
ReturnToRound.OnPressed += _ => ReturnToRoundPressed?.Invoke(); // WD EDIT
}

public void Hide()
Expand Down
8 changes: 8 additions & 0 deletions Content.Server/GameTicking/GameTicker.GamePreset.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Content.Server._White.Ghost;
using Content.Server.GameTicking.Presets;
using Content.Server.Maps;
using Content.Shared.CCVar;
Expand All @@ -21,6 +22,7 @@ namespace Content.Server.GameTicking
public sealed partial class GameTicker
{
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
[Dependency] private readonly GhostReturnToRoundSystem _ghostReturnToRound = default!; // WD EDIT

public const float PresetFailedCooldownIncrease = 30f;

Expand Down Expand Up @@ -303,6 +305,12 @@ public bool OnGhostAttempt(EntityUid mindId, bool canReturnGlobal, bool viaComma
_mind.Visit(mindId, ghost, mind);
else
_mind.TransferTo(mindId, ghost, mind: mind);

// WD EDIT START
var userId = mind.Session!.UserId;
if (!_ghostReturnToRound.DeathTime.TryGetValue(userId, out _))
_ghostReturnToRound.DeathTime[userId] = _gameTiming.CurTime;
// WD EDIT END
Spatison marked this conversation as resolved.
Show resolved Hide resolved
return true;
}

Expand Down
81 changes: 81 additions & 0 deletions Content.Server/GameTicking/GameTicker.Spawning.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
using Content.Server.Speech.Components;
using Content.Server.Station.Components;
using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Database;
using Content.Shared.Mind;
using Content.Shared.Players;
using Content.Shared.Preferences;
using Content.Shared.Roles;
Expand Down Expand Up @@ -154,6 +156,66 @@ private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile charact
return;
}

//WD EDIT START
//Ghost system return to round, check for whether the character isn't the same.
if (lateJoin && !_adminManager.IsAdmin(player))
{
var sameChar = false;
var checkAvoid = false;

var allPlayerMinds = EntityQuery<MindComponent>()
.Where(mind => mind.OriginalOwnerUserId == player.UserId);

foreach (var mind in allPlayerMinds)
{
if (mind.CharacterName == character.Name)
{
sameChar = true;
break;
}

if (mind.CharacterName != null)
{
var similarity = CalculateStringSimilarity(mind.CharacterName, character.Name);

switch (similarity)
{
case >= 85f:
{
_chatManager.SendAdminAlert(Loc.GetString("ghost-respawn-log-character-almost-same",
("player", player.Name), ("try", false), ("oldName", mind.CharacterName), ("newName", character.Name)));

checkAvoid = true;
sameChar = true;

break;
}
case >= 50f:
{
_chatManager.SendAdminAlert(Loc.GetString("ghost-respawn-log-character-almost-same",
("player", player.Name), ("try", true), ("oldName", mind.CharacterName), ("newName", character.Name)));

break;
}
}
}
}

if (sameChar)
{
var message = checkAvoid
? Loc.GetString("ghost-respawn-same-character-slightly-changed-name")
: Loc.GetString("ghost-respawn-same-character");
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));

_chatManager.ChatMessageToOne(ChatChannel.Server, message, wrappedMessage,
default, false, player.Channel, Color.Red);

return;
}
}
Spatison marked this conversation as resolved.
Show resolved Hide resolved
//WD EDIT END

// We raise this event to allow other systems to handle spawning this player themselves. (e.g. late-join wizard, etc)
var bev = new PlayerBeforeSpawnEvent(player, character, jobId, lateJoin, station);
RaiseLocalEvent(bev);
Expand Down Expand Up @@ -346,6 +408,25 @@ public void SpawnObserver(ICommonSession player)
_adminLogger.Add(LogType.LateJoin, LogImpact.Low, $"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}.");
}

//WD EDIT START
private float CalculateStringSimilarity(string str1, string str2)
{
var minLength = Math.Min(str1.Length, str2.Length);
var matchingCharacters = 0;

for (var i = 0; i < minLength; i++)
{
if (str1[i] == str2[i])
matchingCharacters++;
}

float maxLength = Math.Max(str1.Length, str2.Length);
var similarityPercentage = (matchingCharacters / maxLength) * 100;

return similarityPercentage;
}
//WD EDIT END

#region Mob Spawning Helpers
private EntityUid SpawnObserverMob()
{
Expand Down
108 changes: 108 additions & 0 deletions Content.Server/_White/Ghost/GhostReturnToRoundSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using Content.Server.Administration.Logs;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
using Content.Shared._White;
using Content.Shared.Database;
using Content.Shared.GameTicking;
using Content.Shared.Ghost;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Timing;

namespace Content.Server._White.Ghost;

public sealed class GhostReturnToRoundSystem : EntitySystem
{
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;

public readonly Dictionary<NetUserId, TimeSpan> DeathTime = new();

Spatison marked this conversation as resolved.
Show resolved Hide resolved
public override void Initialize()
{
SubscribeNetworkEvent<GhostReturnToRoundRequest>(OnGhostReturnToRoundRequest);
SubscribeLocalEvent<RoundRestartCleanupEvent>(ResetDeathTimes);
}

private void OnGhostReturnToRoundRequest(GhostReturnToRoundRequest msg, EntitySessionEventArgs args)
{
var cfg = IoCManager.Resolve<IConfigurationManager>();
Spatison marked this conversation as resolved.
Show resolved Hide resolved
var maxPlayers = cfg.GetCVar(WhiteCVars.GhostRespawnMaxPlayers);
var connectedClient = args.SenderSession.ConnectedClient;
if (_playerManager.PlayerCount >= maxPlayers)
{
var message = Loc.GetString("ghost-respawn-max-players", ("players", maxPlayers));
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chatManager.ChatMessageToOne(Shared.Chat.ChatChannel.Server,
message,
wrappedMessage,
default,
false,
connectedClient,
Color.Red);
return;
}

var userId = args.SenderSession.UserId;
if (!DeathTime.TryGetValue(userId, out var deathTime))
{
var message = Loc.GetString("ghost-respawn-bug");
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chatManager.ChatMessageToOne(Shared.Chat.ChatChannel.Server,
message,
wrappedMessage,
default,
false,
connectedClient,
Color.Red);
DeathTime[userId] = _gameTiming.CurTime;
return;
}

var timeUntilRespawn = (double) cfg.GetCVar(WhiteCVars.GhostRespawnTime);
Spatison marked this conversation as resolved.
Show resolved Hide resolved
var timePast = (_gameTiming.CurTime - deathTime).TotalMinutes;
if (timePast >= timeUntilRespawn)
{
var ticker = Get<GameTicker>();
var playerMgr = IoCManager.Resolve<IPlayerManager>();
Spatison marked this conversation as resolved.
Show resolved Hide resolved
playerMgr.TryGetSessionById(userId, out var targetPlayer);

if (targetPlayer != null)
ticker.Respawn(targetPlayer);
DeathTime.Remove(userId);

_adminLogger.Add(LogType.Mind, LogImpact.Medium, $"{Loc.GetString("ghost-respawn-log-return-to-lobby", ("userName", connectedClient.UserName))}");

var message = Loc.GetString("ghost-respawn-window-rules-footer");
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chatManager.ChatMessageToOne(Shared.Chat.ChatChannel.Server,
message,
wrappedMessage,
default,
false,
connectedClient,
Color.Red);

}
else
{
Spatison marked this conversation as resolved.
Show resolved Hide resolved
var message = Loc.GetString("ghost-respawn-time-left", ("time", (int) (timeUntilRespawn - timePast)));
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chatManager.ChatMessageToOne(Shared.Chat.ChatChannel.Server,
message,
wrappedMessage,
default,
false,
connectedClient,
Color.Red);
}
}

private void ResetDeathTimes(RoundRestartCleanupEvent ev)
{
DeathTime.Clear();
}
}
5 changes: 5 additions & 0 deletions Content.Shared/Ghost/SharedGhostSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,9 @@ public GhostUpdateGhostRoleCountEvent(int availableGhostRoleCount)
AvailableGhostRoles = availableGhostRoleCount;
}
}

// WD EDIT START
[Serializable, NetSerializable]
public sealed class GhostReturnToRoundRequest : EntityEventArgs;
// WD EDIT END
}
10 changes: 9 additions & 1 deletion Content.Shared/_White/CVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public sealed class WhiteCVars

public static readonly CVarDef<double> AspectChance =
CVarDef.Create("aspects.chance", 0.1d, CVar.SERVERONLY);

#endregion

#region Keybind
Expand All @@ -31,4 +31,12 @@ public static readonly CVarDef<string>
ServerCulture = CVarDef.Create("white.culture", "ru-RU", CVar.REPLICATED | CVar.SERVER);

#endregion

#region GhostRespawn
public static readonly CVarDef<float> GhostRespawnTime =
CVarDef.Create("ghost.respawn_time", 15f, CVar.SERVERONLY);

public static readonly CVarDef<int> GhostRespawnMaxPlayers =
CVarDef.Create("ghost.respawn_max_players", 40, CVar.SERVERONLY);
#endregion
}
1 change: 1 addition & 0 deletions Resources/Locale/en-US/_white/ghost/ghost-gui.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ghost-gui-return-to-round-button = Return to round
14 changes: 14 additions & 0 deletions Resources/Locale/en-US/_white/ghost/ghost-respawn.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ghost-respawn-time-left = Before the opportunity to return to the round { $time ->
[one] { $time } minute
*[other] { $time } minutes}.
ghost-respawn-max-players = The function is not available, there should be fewer players on the server { $players }.
ghost-respawn-window-title = Rules for returning to the round
ghost-respawn-window-rules-footer = By using this feature, you [color=#ff7700]agree[/color] [color=#ff0000]not to transfer[/color] the knowledge of your past character to a new one. For violation of the clause specified here, [color=#ff0000]a ban in the amount of 3 days or more follows[/color].
ghost-respawn-bug = There is no time of death. The standard value is set.
ghost-respawn-same-character = You cannot enter the round for the same character. Change it in the character settings.

ghost-respawn-log-character-almost-same = Player { $player } { $try ->
[true] join
*[false] tried to join
} in the round after the respawn with a similar name. Past name: { $oldName }, current: { $newName }.
ghost-respawn-log-return-to-lobby = { $userName } returned to the lobby.
1 change: 1 addition & 0 deletions Resources/Locale/ru-RU/_white/ghost/ghost-gui.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ghost-gui-return-to-round-button = Вернуться в раунд
15 changes: 15 additions & 0 deletions Resources/Locale/ru-RU/_white/ghost/ghost-respawn.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ghost-respawn-time-left = До возможности вернуться в раунд { $time ->
[one] { $time } минута
[few] { $time } минуты
*[other] { $time } минут}.
ghost-respawn-max-players = Функция недоступна, игроков на сервере должно быть меньше { $players }.
ghost-respawn-window-title = Правила возвращения в раунд
ghost-respawn-window-rules-footer = Пользуясь это функцией, вы [color=#ff7700]обязуетесь[/color] [color=#ff0000]не переносить[/color] знания своего прошлого персонажа в нового. За нарушение пункта, указанного здесь, следует [color=#ff0000]бан в размере от 3-ех дней[/color].
ghost-respawn-bug = Нет времени смерти. Установлено стандартное значение.
ghost-respawn-same-character = Нельзя заходить в раунд за того же персонажа. Поменяйте его в настройках персонажей.

ghost-respawn-log-character-almost-same = Игрок { $player } { $try ->
[true] зашёл
*[false] попытался зайти
} в раунд после возвращения в лобби с похожим именем. Прошлое имя: { $oldName }, текущее: { $newName }.
ghost-respawn-log-return-to-lobby = { $userName } вернулся в лобби.
Loading