mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-18 05:59:03 +03:00
## Mirror of PR #25192: [Criminal record hud icons](https://github.com/space-wizards/space-station-14/pull/25192) from <img src="https://avatars.githubusercontent.com/u/10567778?v=4" alt="space-wizards" width="22"/> [space-wizards](https://github.com/space-wizards)/[space-station-14](https://github.com/space-wizards/space-station-14) ###### `60b9d89e4dbdd8aaad4992a105628297d9480617` PR opened by <img src="https://avatars.githubusercontent.com/u/137322659?v=4" width="16"/><a href="https://github.com/Arendian"> Arendian</a> at 2024-02-13 19:39:59 UTC PR merged by <img src="https://avatars.githubusercontent.com/u/19864447?v=4" width="16"/><a href="https://github.com/web-flow"> web-flow</a> at 2024-03-11 03:12:52 UTC --- PR changed 19 files with 230 additions and 40 deletions. The PR had the following labels: - Changes: UI - Changes: Sprites - Status: Needs Review --- <details open="true"><summary><h1>Original Body</h1></summary> > <!-- Please read these guidelines before opening your PR: https://docs.spacestation14.io/en/getting-started/pr-guideline --> > <!-- The text between the arrows are comments - they will not be visible on your PR. --> > > ## About the PR > <!-- What did you change in this PR? --> > Added three more statuses to the criminal record console: Paroled, Suspect, Discharged. > Entities now have a hud icon based on the criminal record linked to the name they are currently showing. > If "Urist McHands" is wanted, anyone that currently shows as being "Urist McHands", even when their real name is not Urist McHands, will show the relevant hud icon to entities wearing a security hud. If Urist McHands is disguised as someone without a criminal record or has no name(middle-aged captain man etc.), they won't show an icon. > > ## Why / Balance > <!-- Why was it changed? Link any discussions or issues here. Please discuss how this would affect game balance. --> > Icons showing on sechuds will make sechuds more usefull, it will (hopefully) improve security coordination by allowing officers to more easily see who they need to keep track of. > It also gives warden something extra to do. > > ## Technical details > <!-- If this is a code change, summarize at high level how your new code works. This makes it easier to review. --> > > When a criminal record is changed, every entity with an IdentityComponent will have their name checked to see if it equals the name of the changed criminal record, if it's equal the person will get the CriminalRecordComponent which holds the icon that needs to be shown on the entity. > If a criminal record is removed, the CriminalRecordComponent will be removed from all entities that currently have the visible name associated with the criminal record. > > If an entity changes identity, all criminal records will be searched to see if the name has a criminal record attached to it. If the name has a record attached to it, the entity will get the CriminalRecordComponent and the corresponding criminal record hud icon until they change their identity again or the criminal record gets removed. > > ## Media > <!-- > PRs which make ingame changes (adding clothing, items, new features, etc) are required to have media attached that showcase the changes. > Small fixes/refactors are exempt. > Any media may be used in SS14 progress reports, with clear credit given. > > If you're unsure whether your PR will require media, ask a maintainer. > > Check the box below to confirm that you have in fact seen this (put an X in the brackets, like [X]): > --> > > https://github.com/space-wizards/space-station-14/assets/137322659/3c9422e6-897f-456f-b54b-8fdc053cb4e1 > > > - [X] I have added screenshots/videos to this PR showcasing its changes ingame, **or** this PR does not require an ingame showcase > > **Changelog** > <!-- > Make players aware of new features and changes that could affect how they play the game by adding a Changelog entry. Please read the Changelog guidelines located at: https://docs.spacestation14.io/en/getting-started/pr-guideline#changelog > --> > > 🆑 Dygon > - add: The following criminal record statuses have been added: Suspect, Discharged, Paroled. > - add: Security huds now show an icon on entities that have a visible name linked to a criminal record. > </details> Co-authored-by: Arendian <137322659+Arendian@users.noreply.github.com>
388 lines
15 KiB
C#
388 lines
15 KiB
C#
using System.Linq;
|
|
using Content.Server.Administration.Managers;
|
|
using Content.Server.Chat.Managers;
|
|
using Content.Server.Forensics;
|
|
using Content.Server.GameTicking;
|
|
using Content.Server.Hands.Systems;
|
|
using Content.Server.IdentityManagement;
|
|
using Content.Server.Mind;
|
|
using Content.Server.Players.PlayTimeTracking;
|
|
using Content.Server.Popups;
|
|
using Content.Server.StationRecords.Systems;
|
|
using Content.Shared.Administration;
|
|
using Content.Shared.Administration.Events;
|
|
using Content.Shared.CCVar;
|
|
using Content.Shared.GameTicking;
|
|
using Content.Shared.Hands.Components;
|
|
using Content.Shared.IdentityManagement;
|
|
using Content.Shared.Inventory;
|
|
using Content.Shared.PDA;
|
|
using Content.Shared.Players.PlayTimeTracking;
|
|
using Content.Shared.Popups;
|
|
using Content.Shared.Roles;
|
|
using Content.Shared.Roles.Jobs;
|
|
using Content.Shared.StationRecords;
|
|
using Content.Shared.Throwing;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Server.Player;
|
|
using Robust.Shared.Audio;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Enums;
|
|
using Robust.Shared.Network;
|
|
using Robust.Shared.Player;
|
|
|
|
namespace Content.Server.Administration.Systems
|
|
{
|
|
public sealed class AdminSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IAdminManager _adminManager = default!;
|
|
[Dependency] private readonly IChatManager _chat = default!;
|
|
[Dependency] private readonly IConfigurationManager _config = default!;
|
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
|
[Dependency] private readonly HandsSystem _hands = default!;
|
|
[Dependency] private readonly SharedJobSystem _jobs = default!;
|
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
|
[Dependency] private readonly MindSystem _minds = default!;
|
|
[Dependency] private readonly PopupSystem _popup = default!;
|
|
[Dependency] private readonly PhysicsSystem _physics = default!;
|
|
[Dependency] private readonly PlayTimeTrackingManager _playTime = default!;
|
|
[Dependency] private readonly SharedRoleSystem _role = default!;
|
|
[Dependency] private readonly GameTicker _gameTicker = default!;
|
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
[Dependency] private readonly StationRecordsSystem _stationRecords = default!;
|
|
[Dependency] private readonly TransformSystem _transform = default!;
|
|
|
|
private readonly Dictionary<NetUserId, PlayerInfo> _playerList = new();
|
|
|
|
/// <summary>
|
|
/// Set of players that have participated in this round.
|
|
/// </summary>
|
|
public IReadOnlySet<NetUserId> RoundActivePlayers => _roundActivePlayers;
|
|
|
|
private readonly HashSet<NetUserId> _roundActivePlayers = new();
|
|
private readonly PanicBunkerStatus _panicBunker = new();
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
|
|
_adminManager.OnPermsChanged += OnAdminPermsChanged;
|
|
|
|
Subs.CVar(_config, CCVars.PanicBunkerEnabled, OnPanicBunkerChanged, true);
|
|
Subs.CVar(_config, CCVars.PanicBunkerDisableWithAdmins, OnPanicBunkerDisableWithAdminsChanged, true);
|
|
Subs.CVar(_config, CCVars.PanicBunkerEnableWithoutAdmins, OnPanicBunkerEnableWithoutAdminsChanged, true);
|
|
Subs.CVar(_config, CCVars.PanicBunkerCountDeadminnedAdmins, OnPanicBunkerCountDeadminnedAdminsChanged, true);
|
|
Subs.CVar(_config, CCVars.PanicBunkerShowReason, OnShowReasonChanged, true);
|
|
Subs.CVar(_config, CCVars.PanicBunkerMinAccountAge, OnPanicBunkerMinAccountAgeChanged, true);
|
|
Subs.CVar(_config, CCVars.PanicBunkerMinOverallHours, OnPanicBunkerMinOverallHoursChanged, true);
|
|
|
|
SubscribeLocalEvent<IdentityChangedEvent>(OnIdentityChanged);
|
|
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
|
|
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
|
|
SubscribeLocalEvent<RoleAddedEvent>(OnRoleEvent);
|
|
SubscribeLocalEvent<RoleRemovedEvent>(OnRoleEvent);
|
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestartCleanup);
|
|
}
|
|
|
|
private void OnRoundRestartCleanup(RoundRestartCleanupEvent ev)
|
|
{
|
|
_roundActivePlayers.Clear();
|
|
|
|
foreach (var (id, data) in _playerList)
|
|
{
|
|
if (!data.ActiveThisRound)
|
|
continue;
|
|
|
|
if (!_playerManager.TryGetPlayerData(id, out var playerData))
|
|
return;
|
|
|
|
_playerManager.TryGetSessionById(id, out var session);
|
|
_playerList[id] = GetPlayerInfo(playerData, session);
|
|
}
|
|
|
|
var updateEv = new FullPlayerListEvent() { PlayersInfo = _playerList.Values.ToList() };
|
|
|
|
foreach (var admin in _adminManager.ActiveAdmins)
|
|
{
|
|
RaiseNetworkEvent(updateEv, admin.Channel);
|
|
}
|
|
}
|
|
|
|
public void UpdatePlayerList(ICommonSession player)
|
|
{
|
|
_playerList[player.UserId] = GetPlayerInfo(player.Data, player);
|
|
|
|
var playerInfoChangedEvent = new PlayerInfoChangedEvent
|
|
{
|
|
PlayerInfo = _playerList[player.UserId]
|
|
};
|
|
|
|
foreach (var admin in _adminManager.ActiveAdmins)
|
|
{
|
|
RaiseNetworkEvent(playerInfoChangedEvent, admin.Channel);
|
|
}
|
|
}
|
|
|
|
public PlayerInfo? GetCachedPlayerInfo(NetUserId? netUserId)
|
|
{
|
|
if (netUserId == null)
|
|
return null;
|
|
|
|
_playerList.TryGetValue(netUserId.Value, out var value);
|
|
return value ?? null;
|
|
}
|
|
|
|
private void OnIdentityChanged(ref IdentityChangedEvent ev)
|
|
{
|
|
if (!TryComp<ActorComponent>(ev.CharacterEntity, out var actor))
|
|
return;
|
|
|
|
UpdatePlayerList(actor.PlayerSession);
|
|
}
|
|
|
|
private void OnRoleEvent(RoleEvent ev)
|
|
{
|
|
var session = _minds.GetSession(ev.Mind);
|
|
if (!ev.Antagonist || session == null)
|
|
return;
|
|
|
|
UpdatePlayerList(session);
|
|
}
|
|
|
|
private void OnAdminPermsChanged(AdminPermsChangedEventArgs obj)
|
|
{
|
|
UpdatePanicBunker();
|
|
|
|
if (!obj.IsAdmin)
|
|
{
|
|
RaiseNetworkEvent(new FullPlayerListEvent(), obj.Player.Channel);
|
|
return;
|
|
}
|
|
|
|
SendFullPlayerList(obj.Player);
|
|
}
|
|
|
|
private void OnPlayerDetached(PlayerDetachedEvent ev)
|
|
{
|
|
// If disconnected then the player won't have a connected entity to get character name from.
|
|
// The disconnected state gets sent by OnPlayerStatusChanged.
|
|
if (ev.Player.Status == SessionStatus.Disconnected)
|
|
return;
|
|
|
|
UpdatePlayerList(ev.Player);
|
|
}
|
|
|
|
private void OnPlayerAttached(PlayerAttachedEvent ev)
|
|
{
|
|
if (ev.Player.Status == SessionStatus.Disconnected)
|
|
return;
|
|
|
|
_roundActivePlayers.Add(ev.Player.UserId);
|
|
UpdatePlayerList(ev.Player);
|
|
}
|
|
|
|
public override void Shutdown()
|
|
{
|
|
base.Shutdown();
|
|
_playerManager.PlayerStatusChanged -= OnPlayerStatusChanged;
|
|
_adminManager.OnPermsChanged -= OnAdminPermsChanged;
|
|
}
|
|
|
|
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
|
{
|
|
UpdatePlayerList(e.Session);
|
|
UpdatePanicBunker();
|
|
}
|
|
|
|
private void SendFullPlayerList(ICommonSession playerSession)
|
|
{
|
|
var ev = new FullPlayerListEvent();
|
|
|
|
ev.PlayersInfo = _playerList.Values.ToList();
|
|
|
|
RaiseNetworkEvent(ev, playerSession.Channel);
|
|
}
|
|
|
|
private PlayerInfo GetPlayerInfo(SessionData data, ICommonSession? session)
|
|
{
|
|
var name = data.UserName;
|
|
var entityName = string.Empty;
|
|
var identityName = string.Empty;
|
|
|
|
if (session?.AttachedEntity != null)
|
|
{
|
|
entityName = EntityManager.GetComponent<MetaDataComponent>(session.AttachedEntity.Value).EntityName;
|
|
identityName = Identity.Name(session.AttachedEntity.Value, EntityManager);
|
|
}
|
|
|
|
var antag = false;
|
|
var startingRole = string.Empty;
|
|
if (_minds.TryGetMind(session, out var mindId, out _))
|
|
{
|
|
antag = _role.MindIsAntagonist(mindId);
|
|
startingRole = _jobs.MindTryGetJobName(mindId);
|
|
}
|
|
|
|
var connected = session != null && session.Status is SessionStatus.Connected or SessionStatus.InGame;
|
|
TimeSpan? overallPlaytime = null;
|
|
if (session != null &&
|
|
_playTime.TryGetTrackerTimes(session, out var playTimes) &&
|
|
playTimes.TryGetValue(PlayTimeTrackingShared.TrackerOverall, out var playTime))
|
|
{
|
|
overallPlaytime = playTime;
|
|
}
|
|
|
|
return new PlayerInfo(name, entityName, identityName, startingRole, antag, GetNetEntity(session?.AttachedEntity), data.UserId,
|
|
connected, _roundActivePlayers.Contains(data.UserId), overallPlaytime);
|
|
}
|
|
|
|
private void OnPanicBunkerChanged(bool enabled)
|
|
{
|
|
_panicBunker.Enabled = enabled;
|
|
_chat.SendAdminAlert(Loc.GetString(enabled
|
|
? "admin-ui-panic-bunker-enabled-admin-alert"
|
|
: "admin-ui-panic-bunker-disabled-admin-alert"
|
|
));
|
|
|
|
SendPanicBunkerStatusAll();
|
|
}
|
|
|
|
private void OnPanicBunkerDisableWithAdminsChanged(bool enabled)
|
|
{
|
|
_panicBunker.DisableWithAdmins = enabled;
|
|
UpdatePanicBunker();
|
|
}
|
|
|
|
private void OnPanicBunkerEnableWithoutAdminsChanged(bool enabled)
|
|
{
|
|
_panicBunker.EnableWithoutAdmins = enabled;
|
|
UpdatePanicBunker();
|
|
}
|
|
|
|
private void OnPanicBunkerCountDeadminnedAdminsChanged(bool enabled)
|
|
{
|
|
_panicBunker.CountDeadminnedAdmins = enabled;
|
|
UpdatePanicBunker();
|
|
}
|
|
|
|
private void OnShowReasonChanged(bool enabled)
|
|
{
|
|
_panicBunker.ShowReason = enabled;
|
|
SendPanicBunkerStatusAll();
|
|
}
|
|
|
|
private void OnPanicBunkerMinAccountAgeChanged(int minutes)
|
|
{
|
|
_panicBunker.MinAccountAgeHours = minutes / 60;
|
|
SendPanicBunkerStatusAll();
|
|
}
|
|
|
|
private void OnPanicBunkerMinOverallHoursChanged(int hours)
|
|
{
|
|
_panicBunker.MinOverallHours = hours;
|
|
SendPanicBunkerStatusAll();
|
|
}
|
|
|
|
private void UpdatePanicBunker()
|
|
{
|
|
var admins = _panicBunker.CountDeadminnedAdmins
|
|
? _adminManager.AllAdmins
|
|
: _adminManager.ActiveAdmins;
|
|
var hasAdmins = admins.Any();
|
|
|
|
if (hasAdmins && _panicBunker.DisableWithAdmins)
|
|
{
|
|
_config.SetCVar(CCVars.PanicBunkerEnabled, false);
|
|
}
|
|
else if (!hasAdmins && _panicBunker.EnableWithoutAdmins)
|
|
{
|
|
_config.SetCVar(CCVars.PanicBunkerEnabled, true);
|
|
}
|
|
|
|
SendPanicBunkerStatusAll();
|
|
}
|
|
|
|
private void SendPanicBunkerStatusAll()
|
|
{
|
|
var ev = new PanicBunkerChangedEvent(_panicBunker);
|
|
foreach (var admin in _adminManager.AllAdmins)
|
|
{
|
|
RaiseNetworkEvent(ev, admin);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erases a player from the round.
|
|
/// This removes them and any trace of them from the round, deleting their
|
|
/// chat messages and showing a popup to other players.
|
|
/// Their items are dropped on the ground.
|
|
/// </summary>
|
|
public void Erase(ICommonSession player)
|
|
{
|
|
var entity = player.AttachedEntity;
|
|
_chat.DeleteMessagesBy(player);
|
|
|
|
if (entity != null && !TerminatingOrDeleted(entity.Value))
|
|
{
|
|
if (TryComp(entity.Value, out TransformComponent? transform))
|
|
{
|
|
var coordinates = _transform.GetMoverCoordinates(entity.Value, transform);
|
|
var name = Identity.Entity(entity.Value, EntityManager);
|
|
_popup.PopupCoordinates(Loc.GetString("admin-erase-popup", ("user", name)), coordinates, PopupType.LargeCaution);
|
|
var filter = Filter.Pvs(coordinates, 1, EntityManager, _playerManager);
|
|
var audioParams = new AudioParams().WithVolume(3);
|
|
_audio.PlayStatic("/Audio/DeltaV/Misc/reducedtoatmos.ogg", filter, coordinates, true, audioParams);
|
|
}
|
|
|
|
foreach (var item in _inventory.GetHandOrInventoryEntities(entity.Value))
|
|
{
|
|
if (TryComp(item, out PdaComponent? pda) &&
|
|
TryComp(pda.ContainedId, out StationRecordKeyStorageComponent? keyStorage) &&
|
|
keyStorage.Key is { } key &&
|
|
_stationRecords.TryGetRecord(key, out GeneralStationRecord? record))
|
|
{
|
|
if (TryComp(entity, out DnaComponent? dna) &&
|
|
dna.DNA != record.DNA)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (TryComp(entity, out FingerprintComponent? fingerPrint) &&
|
|
fingerPrint.Fingerprint != record.Fingerprint)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_stationRecords.RemoveRecord(key);
|
|
Del(item);
|
|
}
|
|
}
|
|
|
|
if (_inventory.TryGetContainerSlotEnumerator(entity.Value, out var enumerator))
|
|
{
|
|
while (enumerator.NextItem(out var item, out var slot))
|
|
{
|
|
if (_inventory.TryUnequip(entity.Value, entity.Value, slot.Name, true, true))
|
|
_physics.ApplyAngularImpulse(item, ThrowingSystem.ThrowAngularImpulse);
|
|
}
|
|
}
|
|
|
|
if (TryComp(entity.Value, out HandsComponent? hands))
|
|
{
|
|
foreach (var hand in _hands.EnumerateHands(entity.Value, hands))
|
|
{
|
|
_hands.TryDrop(entity.Value, hand, checkActionBlocker: false, doDropInteraction: false, handsComp: hands);
|
|
}
|
|
}
|
|
}
|
|
|
|
_minds.WipeMind(player);
|
|
QueueDel(entity);
|
|
|
|
_gameTicker.SpawnObserver(player);
|
|
}
|
|
}
|
|
}
|