Id[entity] 2.0 (real) (#9612)

* starter API

* network ID cards

* Port more stuff from old identity

* Re-implement identity representation + name updating

* move

* proper name returning for `IdentityName`

* move everything important to server, give in to  temptation

* shared / server / client split sadly. move ensure to shared and spawn to server

* identity update queueing + identityblocker

* fixes

* and just like that it's usable for admins

* huge identity pass

* pass dos

* jesus christ

* figs :D

* fuck u

* fix bad merge.

Co-authored-by: Moony <moonheart08@users.noreply.github.com>
This commit is contained in:
Kara
2022-07-10 18:36:53 -07:00
committed by GitHub
parent fb6586cdc6
commit 2d5ec7f85c
68 changed files with 668 additions and 188 deletions

View File

@@ -0,0 +1,7 @@
using Content.Shared.Access.Systems;
namespace Content.Client.Access;
public sealed class IdCardSystem : SharedIdCardSystem
{
}

View File

@@ -170,9 +170,12 @@ namespace Content.Client.Administration.UI
if (pl.Antag)
sb.Append(new Rune(0x1F5E1)); // 🗡
sb.AppendFormat("\"{0}\"", pl.CharacterName)
.Append(' ')
.Append(pl.Username);
sb.AppendFormat("\"{0}\"", pl.CharacterName);
if (pl.IdentityName != pl.CharacterName && pl.IdentityName != string.Empty)
sb.Append(' ').AppendFormat("[{0}]", pl.IdentityName);
sb.Append(' ').Append(pl.Username);
return sb.ToString();
}

View File

@@ -71,6 +71,8 @@ namespace Content.Client.Administration.UI.CustomControls
foreach (var info in _adminSystem.PlayerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text) &&
!displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
{

View File

@@ -100,6 +100,7 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab
{
var entry = new PlayerTabEntry(player.Username,
player.CharacterName,
player.IdentityName,
player.StartingJob,
player.Antag ? "YES" : "NO",
new StyleBoxFlat(useAltColor ? _altColor : _defaultColor),

View File

@@ -10,7 +10,7 @@ public sealed partial class PlayerTabEntry : ContainerButton
{
public EntityUid? PlayerUid;
public PlayerTabEntry(string username, string character, string job, string antagonist, StyleBox styleBox, bool connected)
public PlayerTabEntry(string username, string character, string identity, string job, string antagonist, StyleBox styleBox, bool connected)
{
RobustXamlLoader.Load(this);
@@ -19,6 +19,8 @@ public sealed partial class PlayerTabEntry : ContainerButton
UsernameLabel.StyleClasses.Add("Disabled");
JobLabel.Text = job;
CharacterLabel.Text = character;
if (identity != character)
CharacterLabel.Text += $" [{identity}]";
AntagonistLabel.Text = antagonist;
BackgroundColorPanel.PanelOverride = styleBox;
}

View File

@@ -3,6 +3,7 @@ using Content.Client.Cargo.UI;
using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Events;
using Content.Shared.Cargo.Prototypes;
using Content.Shared.IdentityManagement;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Prototypes;
@@ -55,7 +56,7 @@ namespace Content.Client.Cargo.BUI
string orderRequester;
if (entityManager.TryGetComponent<MetaDataComponent>(localPlayer, out var metadata))
orderRequester = metadata.EntityName;
orderRequester = Identity.Name(localPlayer.Value, entityManager);
else
orderRequester = string.Empty;

View File

@@ -1,4 +1,8 @@
using Content.Client.Administration.Managers;
using Content.Shared.Administration;
using Content.Shared.IdentityManagement;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -11,6 +15,7 @@ namespace Content.Client.ContextMenu.UI
public const string StyleClassEntityMenuCountText = "contextMenuCount";
[Dependency] private IEntityManager _entityManager = default!;
[Dependency] private IPlayerManager _playerManager = default!;
/// <summary>
/// The entity that can be accessed by interacting with this element.
@@ -74,10 +79,12 @@ namespace Content.Client.ContextMenu.UI
EntityIcon.Sprite = _entityManager.GetComponentOrNull<ISpriteComponent>(entity);
if (UserInterfaceManager.DebugMonitors.Visible)
var admin = IoCManager.Resolve<IClientAdminManager>();
if (admin.HasFlag(AdminFlags.Admin | AdminFlags.Debug))
Text = _entityManager.ToPrettyString(entity.Value);
else
Text = _entityManager.GetComponent<MetaDataComponent>(entity.Value).EntityName;
Text = Identity.Name(entity.Value, _entityManager, _playerManager.LocalPlayer!.ControlledEntity!);
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Shared.IdentityManagement;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -21,7 +22,7 @@ namespace Content.Client.ContextMenu.UI
{
if (GroupingContextMenuType == 0)
{
var newEntities = entities.GroupBy(e => _entityManager.GetComponent<MetaDataComponent>(e).EntityName + (_entityManager.GetComponent<MetaDataComponent>(e).EntityPrototype?.ID ?? string.Empty)).ToList();
var newEntities = entities.GroupBy(e => Identity.Name(e, _entityManager) + (_entityManager.GetComponent<MetaDataComponent>(e).EntityPrototype?.ID ?? string.Empty)).ToList();
return newEntities.Select(grp => grp.ToList()).ToList();
}
else

View File

@@ -2,6 +2,7 @@ using System.Linq;
using System.Threading;
using Content.Client.Verbs;
using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
using Content.Shared.Input;
using Content.Shared.Verbs;
using JetBrains.Annotations;
@@ -192,7 +193,7 @@ namespace Content.Client.Examine
hBox.AddChild(new Label
{
Text = EntityManager.GetComponent<MetaDataComponent>(target).EntityName,
Text = Identity.Name(target, EntityManager, player),
HorizontalExpand = true,
});

View File

@@ -7,7 +7,7 @@ using Content.Shared.Disease.Components;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
using Content.Shared.Damage;
using Content.Shared.IdentityManagement;
using static Content.Shared.MedicalScanner.SharedHealthAnalyzerComponent;
namespace Content.Client.HealthAnalyzer.UI
@@ -28,8 +28,9 @@ namespace Content.Client.HealthAnalyzer.UI
if (msg.TargetEntity != null && entities.TryGetComponent<DamageableComponent>(msg.TargetEntity, out var damageable))
{
string entityName = "Unknown";
if (msg.TargetEntity != null && entities.TryGetComponent<MetaDataComponent>(msg.TargetEntity.Value, out var metaData))
entityName = metaData.EntityName;
if (msg.TargetEntity != null &&
entities.TryGetComponent<MetaDataComponent>(msg.TargetEntity.Value, out var metaData))
entityName = Identity.Name(msg.TargetEntity.Value, entities);
IReadOnlyDictionary<string, FixedPoint2> DamagePerGroup = damageable.DamagePerGroup;
IReadOnlyDictionary<string, FixedPoint2> DamagePerType = damageable.Damage.DamageDict;

View File

@@ -0,0 +1,7 @@
using Content.Shared.IdentityManagement;
namespace Content.Client.IdentityManagement;
public class IdentitySystem : SharedIdentitySystem
{
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using Content.Client.Strip;
using Content.Shared.IdentityManagement;
using Content.Shared.Strip.Components;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
@@ -28,7 +29,8 @@ namespace Content.Client.Inventory
{
base.Open();
_strippingMenu = new StrippingMenu($"{Loc.GetString("strippable-bound-user-interface-stripping-menu-title",("ownerName", IoCManager.Resolve<IEntityManager>().GetComponent<MetaDataComponent>(Owner.Owner).EntityName))}");
var entMan = IoCManager.Resolve<IEntityManager>();
_strippingMenu = new StrippingMenu($"{Loc.GetString("strippable-bound-user-interface-stripping-menu-title", ("ownerName", Identity.Name(Owner.Owner, entMan)))}");
_strippingMenu.OnClose += Close;
_strippingMenu.OpenCentered();

View File

@@ -4,6 +4,7 @@ using Content.Client.Items.Components;
using Content.Client.Resources;
using Content.Client.Stylesheets;
using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
@@ -159,11 +160,12 @@ namespace Content.Client.Items.UI
if (_entityManager.TryGetComponent(_entity, out HandVirtualItemComponent? virtualItem)
&& _entityManager.EntityExists(virtualItem.BlockingEntity))
{
_itemNameLabel.Text = _entityManager.GetComponent<MetaDataComponent>(virtualItem.BlockingEntity).EntityName;
// Uses identity because we can be blocked by pulling someone
_itemNameLabel.Text = Identity.Name(virtualItem.BlockingEntity, _entityManager);
}
else
{
_itemNameLabel.Text = _entityManager.GetComponent<MetaDataComponent>(_entity.Value).EntityName;
_itemNameLabel.Text = Identity.Name(_entity.Value, _entityManager);
}
}

View File

@@ -83,6 +83,7 @@ namespace Content.Server.Access.Systems
jobTitle = jobTitle[..SharedIdCardConsoleComponent.MaxJobTitleLength];
id.JobTitle = jobTitle;
Dirty(id);
UpdateEntityName(uid, id);
return true;
}
@@ -96,6 +97,7 @@ namespace Content.Server.Access.Systems
fullName = fullName[..SharedIdCardConsoleComponent.MaxFullNameLength];
id.FullName = fullName;
Dirty(id);
UpdateEntityName(uid, id);
return true;
}
@@ -129,50 +131,5 @@ namespace Content.Server.Access.Systems
("jobSuffix", jobSuffix));
EntityManager.GetComponent<MetaDataComponent>(id.Owner).EntityName = val;
}
/// <summary>
/// Attempt to find an ID card on an entity. This will look in the entity itself, in the entity's hands, and
/// in the entity's inventory.
/// </summary>
public bool TryFindIdCard(EntityUid uid, [NotNullWhen(true)] out IdCardComponent? idCard)
{
// check held item?
if (EntityManager.TryGetComponent(uid, out SharedHandsComponent? hands) &&
hands.ActiveHandEntity is EntityUid heldItem &&
TryGetIdCard(heldItem, out idCard))
{
return true;
}
// check entity itself
if (TryGetIdCard(uid, out idCard))
return true;
// check inventory slot?
if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid) && TryGetIdCard(idUid.Value, out idCard))
{
return true;
}
return false;
}
/// <summary>
/// Attempt to get an id card component from an entity, either by getting it directly from the entity, or by
/// getting the contained id from a <see cref="PDAComponent"/>.
/// </summary>
public bool TryGetIdCard(EntityUid uid, [NotNullWhen(true)] out IdCardComponent? idCard)
{
if (EntityManager.TryGetComponent(uid, out idCard))
return true;
if (EntityManager.TryGetComponent(uid, out PDAComponent? pda) && pda.ContainedID != null)
{
idCard = pda.ContainedID;
return true;
}
return false;
}
}
}

View File

@@ -1,10 +1,12 @@
using System.Globalization;
using System.Linq;
using Content.Server.Administration.Managers;
using Content.Server.IdentityManagement;
using Content.Server.Players;
using Content.Server.Roles;
using Content.Shared.Administration;
using Content.Shared.Administration.Events;
using Content.Shared.IdentityManagement;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Enums;
@@ -25,6 +27,7 @@ namespace Content.Server.Administration.Systems
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
_adminManager.OnPermsChanged += OnAdminPermsChanged;
SubscribeLocalEvent<IdentityChangedEvent>(OnIdentityChanged);
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<RoleAddedEvent>(OnRoleEvent);
@@ -46,6 +49,14 @@ namespace Content.Server.Administration.Systems
}
}
private void OnIdentityChanged(IdentityChangedEvent ev)
{
if (!TryComp<ActorComponent>(ev.CharacterEntity, out var actor))
return;
UpdatePlayerList(actor.PlayerSession);
}
private void OnRoleEvent(RoleEvent ev)
{
if (!ev.Role.Antagonist || ev.Role.Mind.Session == null)
@@ -106,9 +117,13 @@ namespace Content.Server.Administration.Systems
{
var name = session.Name;
var username = string.Empty;
var identityName = string.Empty;
if (session.AttachedEntity != null)
{
username = EntityManager.GetComponent<MetaDataComponent>(session.AttachedEntity.Value).EntityName;
identityName = Identity.Name(session.AttachedEntity.Value, EntityManager);
}
var mind = session.ContentData()?.Mind;
@@ -119,7 +134,7 @@ namespace Content.Server.Administration.Systems
var connected = session.Status is SessionStatus.Connected or SessionStatus.InGame;
return new PlayerInfo(name, username, startingRole, antag, session.AttachedEntity.GetValueOrDefault(), session.UserId,
return new PlayerInfo(name, username, identityName, startingRole, antag, session.AttachedEntity.GetValueOrDefault(), session.UserId,
connected);
}
}

View File

@@ -4,6 +4,7 @@ using Content.Server.Chemistry.EntitySystems;
using Content.Server.DoAfter;
using Content.Server.Nutrition.Components;
using Content.Server.Popups;
using Content.Shared.IdentityManagement;
using Content.Shared.Nutrition.Components;
using Content.Shared.Popups;
using Content.Shared.Verbs;
@@ -110,7 +111,7 @@ namespace Content.Server.Animals.Systems
var split = _solutionContainerSystem.SplitSolution(uid, solution, quantity);
_solutionContainerSystem.TryAddSolution(ev.ContainerUid, targetSolution, split);
_popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", ev.ContainerUid)), uid,
_popupSystem.PopupEntity(Loc.GetString("udder-system-success", ("amount", quantity), ("target", Identity.Entity(ev.ContainerUid, EntityManager))), uid,
Filter.Entities(ev.UserUid), PopupType.Medium);
}

View File

@@ -10,6 +10,7 @@ using Content.Server.Cooldown;
using Content.Server.Bible.Components;
using Content.Server.MobState;
using Content.Server.Popups;
using Content.Shared.IdentityManagement;
using Content.Shared.Popups;
using Robust.Shared.Random;
using Robust.Shared.Audio;
@@ -121,10 +122,10 @@ namespace Content.Server.Bible
{
if (_random.Prob(component.FailChance))
{
var othersFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-others", ("user", args.User),("target", args.Target),("bible", uid));
var othersFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-others", ("user", Identity.Entity(args.User, EntityManager)),("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
_popupSystem.PopupEntity(othersFailMessage, args.User, Filter.PvsExcept(args.User), PopupType.SmallCaution);
var selfFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-self", ("target", args.Target),("bible", uid));
var selfFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-self", ("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
_popupSystem.PopupEntity(selfFailMessage, args.User, Filter.Entities(args.User), PopupType.MediumCaution);
SoundSystem.Play("/Audio/Effects/hit_kick.ogg", Filter.Pvs(args.Target.Value), args.User);
@@ -133,10 +134,10 @@ namespace Content.Server.Bible
}
}
var othersMessage = Loc.GetString(component.LocPrefix + "-heal-success-others", ("user", args.User),("target", args.Target),("bible", uid));
var othersMessage = Loc.GetString(component.LocPrefix + "-heal-success-others", ("user", Identity.Entity(args.User, EntityManager)),("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
_popupSystem.PopupEntity(othersMessage, args.User, Filter.PvsExcept(args.User), PopupType.Medium);
var selfMessage = Loc.GetString(component.LocPrefix + "-heal-success-self", ("target", args.Target),("bible", uid));
var selfMessage = Loc.GetString(component.LocPrefix + "-heal-success-self", ("target", Identity.Entity(args.Target.Value, EntityManager)),("bible", uid));
_popupSystem.PopupEntity(selfMessage, args.User, Filter.Entities(args.User), PopupType.Large);
SoundSystem.Play(component.HealSoundPath.GetSound(), Filter.Pvs(args.Target.Value), args.User);

View File

@@ -9,6 +9,7 @@ using Content.Shared.Chemistry.Reaction;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
using Content.Shared.MobState.Components;
using Content.Shared.Popups;
using Robust.Shared.Audio;
@@ -174,18 +175,18 @@ public sealed class BloodstreamSystem : EntitySystem
if (component.BleedAmount > 10)
{
args.Message.PushNewline();
args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", uid)));
args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", Identity.Entity(uid, EntityManager))));
}
else if (component.BleedAmount > 0)
{
args.Message.PushNewline();
args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", uid)));
args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", Identity.Entity(uid, EntityManager))));
}
if (GetBloodLevelPercentage(uid, component) < component.BloodlossThreshold)
{
args.Message.PushNewline();
args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", uid)));
args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", Identity.Entity(uid, EntityManager))));
}
}

View File

@@ -7,6 +7,7 @@ using Content.Server.Popups;
using Content.Server.Preferences.Managers;
using Content.Shared.Actions;
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.IdentityManagement;
using Content.Shared.Preferences;
using Content.Shared.Species;
using Content.Shared.Verbs;
@@ -176,7 +177,7 @@ namespace Content.Server.Body.Systems
EntityManager.DeleteEntity(entity);
}
_popupSystem.PopupEntity(Loc.GetString("reassemble-success", ("user", mob)), mob, Filter.Entities(mob));
_popupSystem.PopupEntity(Loc.GetString("reassemble-success", ("user", Identity.Entity(mob, EntityManager))), mob, Filter.Entities(mob));
}
/// <summary>

View File

@@ -8,6 +8,7 @@ using Content.Shared.Examine;
using Content.Shared.Tag;
using Content.Shared.FixedPoint;
using Content.Shared.Audio;
using Content.Shared.IdentityManagement;
using Content.Shared.Popups;
using Content.Shared.Random.Helpers;
using Robust.Shared.Player;
@@ -241,7 +242,7 @@ namespace Content.Server.Botany.Systems
("owner", uid),
("usingItem", args.Used)), Filter.Entities(args.User), PopupType.Medium);
_popupSystem.PopupEntity(Loc.GetString("plant-holder-component-compost-others-message",
("user", args.User),
("user", Identity.Entity(args.User, EntityManager)),
("usingItem", args.Used),
("owner", uid)), uid, Filter.PvsExcept(args.User));

View File

@@ -6,6 +6,7 @@ using Content.Server.Weapon.Melee;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
using Content.Shared.MobState.Components;
using Content.Shared.Popups;
using Content.Shared.Sound;
@@ -66,7 +67,7 @@ namespace Content.Server.Chemistry.Components
if (!solutionsSys.TryGetInjectableSolution(target.Value, out var targetSolution))
{
user.PopupMessage(user,
Loc.GetString("hypospray-cant-inject", ("target", target)));
Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target.Value, _entMan))));
return false;
}

View File

@@ -9,6 +9,7 @@ using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using Content.Shared.FixedPoint;
using Content.Shared.Hands;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.MobState.Components;
@@ -63,7 +64,7 @@ public sealed partial class ChemistrySystem
else
{
_popup.PopupEntity(Loc.GetString("injector-component-cannot-transfer-message",
("target", target)), component.Owner, Filter.Entities(user));
("target", Identity.Entity(target, EntityManager))), component.Owner, Filter.Entities(user));
}
}
else if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Draw)
@@ -82,7 +83,7 @@ public sealed partial class ChemistrySystem
else
{
_popup.PopupEntity(Loc.GetString("injector-component-cannot-draw-message",
("target", target)), component.Owner, Filter.Entities(user));
("target", Identity.Entity(target, EntityManager))), component.Owner, Filter.Entities(user));
}
}
}
@@ -194,7 +195,7 @@ public sealed partial class ChemistrySystem
if (user != target)
{
// Create a pop-up for the target
var userName = MetaData(user).EntityName;
var userName = Identity.Name(user, EntityManager);
_popup.PopupEntity(Loc.GetString("injector-component-injecting-target",
("user", userName)), user, Filter.Entities(target));
@@ -256,7 +257,7 @@ public sealed partial class ChemistrySystem
if (realTransferAmount <= 0)
{
_popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", targetBloodstream.Owner)),
_popup.PopupEntity(Loc.GetString("injector-component-cannot-inject-message", ("target", Identity.Entity(targetBloodstream.Owner, EntityManager))),
component.Owner, Filter.Entities(user));
return;
}
@@ -270,7 +271,7 @@ public sealed partial class ChemistrySystem
_popup.PopupEntity(Loc.GetString("injector-component-inject-success-message",
("amount", removedSolution.TotalVolume),
("target", targetBloodstream.Owner)), component.Owner, Filter.Entities(user));
("target", Identity.Entity(targetBloodstream.Owner, EntityManager))), component.Owner, Filter.Entities(user));
Dirty(component);
AfterInject(component);
@@ -289,7 +290,7 @@ public sealed partial class ChemistrySystem
if (realTransferAmount <= 0)
{
_popup.PopupEntity(Loc.GetString("injector-component-target-already-full-message", ("target", targetEntity)),
_popup.PopupEntity(Loc.GetString("injector-component-target-already-full-message", ("target", Identity.Entity(targetEntity, EntityManager))),
component.Owner, Filter.Entities(user));
return;
}
@@ -310,7 +311,7 @@ public sealed partial class ChemistrySystem
_popup.PopupEntity(Loc.GetString("injector-component-transfer-success-message",
("amount", removedSolution.TotalVolume),
("target", targetEntity)), component.Owner, Filter.Entities(user));
("target", Identity.Entity(targetEntity, EntityManager))), component.Owner, Filter.Entities(user));
Dirty(component);
AfterInject(component);
@@ -349,7 +350,7 @@ public sealed partial class ChemistrySystem
if (realTransferAmount <= 0)
{
_popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", targetEntity)),
_popup.PopupEntity(Loc.GetString("injector-component-target-is-empty-message", ("target", Identity.Entity(targetEntity, EntityManager))),
component.Owner, Filter.Entities(user));
return;
}
@@ -371,7 +372,7 @@ public sealed partial class ChemistrySystem
_popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
("amount", removedSolution.TotalVolume),
("target", targetEntity)), component.Owner, Filter.Entities(user));
("target", Identity.Entity(targetEntity, EntityManager))), component.Owner, Filter.Entities(user));
Dirty(component);
AfterDraw(component);
@@ -395,7 +396,7 @@ public sealed partial class ChemistrySystem
_popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
("amount", drawAmount),
("target", target)), component.Owner, Filter.Entities(user));
("target", Identity.Entity(target, EntityManager))), component.Owner, Filter.Entities(user));
Dirty(component);
AfterDraw(component);

View File

@@ -10,6 +10,7 @@ using Content.Shared.Climbing;
using Content.Shared.Damage;
using Content.Shared.DragDrop;
using Content.Shared.GameTicking;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Physics;
using Content.Shared.Popups;
@@ -138,7 +139,7 @@ public sealed class ClimbSystem : SharedClimbSystem
return;
if (user == uid)
{
var othersMessage = Loc.GetString("comp-climbable-user-climbs-other", ("user", uid),
var othersMessage = Loc.GetString("comp-climbable-user-climbs-other", ("user", Identity.Entity(uid, EntityManager)),
("climbable", climbable));
uid.PopupMessageOtherClients(othersMessage);
@@ -147,11 +148,11 @@ public sealed class ClimbSystem : SharedClimbSystem
}
else
{
var othersMessage = Loc.GetString("comp-climbable-user-climbs-force-other", ("user", user),
("moved-user", uid), ("climbable", climbable));
var othersMessage = Loc.GetString("comp-climbable-user-climbs-force-other", ("user", Identity.Entity(user, EntityManager)),
("moved-user", Identity.Entity(uid, EntityManager)), ("climbable", climbable));
user.PopupMessageOtherClients(othersMessage);
var selfMessage = Loc.GetString("comp-climbable-user-climbs-force", ("moved-user", uid),
var selfMessage = Loc.GetString("comp-climbable-user-climbs-force", ("moved-user", Identity.Entity(uid, EntityManager)),
("climbable", climbable));
user.PopupMessage(selfMessage);
}
@@ -428,4 +429,4 @@ public sealed class StartClimbEvent : EntityEventArgs
{
Climbable = climbable;
}
}
}

View File

@@ -9,6 +9,7 @@ using Content.Shared.Audio;
using Content.Shared.CombatMode;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.IdentityManagement;
using Content.Shared.Stunnable;
using JetBrains.Annotations;
using Robust.Shared.Audio;
@@ -81,14 +82,12 @@ namespace Content.Server.CombatMode
{
SoundSystem.Play(component.DisarmFailSound.GetSound(), Filter.Pvs(args.Performer), args.Performer, AudioHelpers.WithVariation(0.025f));
var targetName = Name(args.Target);
var msgOther = Loc.GetString(
"disarm-action-popup-message-other-clients",
("performerName", Name(args.Performer)),
("targetName", targetName));
("performerName", Identity.Entity(args.Performer, EntityManager)),
("targetName", Identity.Entity(args.Target, EntityManager)));
var msgUser = Loc.GetString("disarm-action-popup-message-cursor", ("targetName", targetName ));
var msgUser = Loc.GetString("disarm-action-popup-message-cursor", ("targetName", Identity.Entity(args.Target, EntityManager)));
_popupSystem.PopupEntity(msgOther, args.Performer, filterOther);
_popupSystem.PopupEntity(msgUser, args.Performer, Filter.Entities(args.Performer));

View File

@@ -17,6 +17,7 @@ using Robust.Shared.Audio;
using Robust.Shared.Utility;
using Content.Shared.Tools.Components;
using Content.Server.Station.Systems;
using Content.Shared.IdentityManagement;
namespace Content.Server.Disease
{
@@ -120,7 +121,7 @@ namespace Content.Server.Disease
EntityManager.TryGetComponent<IngestionBlockerComponent>(maskUid, out var blocker) &&
blocker.Enabled)
{
_popupSystem.PopupEntity(Loc.GetString("swab-mask-blocked", ("target", args.Target), ("mask", maskUid)), args.User, Filter.Entities(args.User));
_popupSystem.PopupEntity(Loc.GetString("swab-mask-blocked", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("mask", maskUid)), args.User, Filter.Entities(args.User));
return;
}
@@ -320,7 +321,7 @@ namespace Content.Server.Disease
return;
args.Swab.Used = true;
_popupSystem.PopupEntity(Loc.GetString("swab-swabbed", ("target", args.Target)), args.Target.Value, Filter.Entities(args.User));
_popupSystem.PopupEntity(Loc.GetString("swab-swabbed", ("target", Identity.Entity(args.Target.Value, EntityManager))), args.Target.Value, Filter.Entities(args.User));
if (args.Swab.Disease != null || args.Carrier.Diseases.Count == 0)
return;

View File

@@ -2,6 +2,7 @@ using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Emag.Components;
using Content.Shared.Emag.Systems;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Tag;
@@ -65,7 +66,7 @@ namespace Content.Server.Emag
RaiseLocalEvent(args.Target.Value, emaggedEvent, false);
if (emaggedEvent.Handled)
{
_popupSystem.PopupEntity(Loc.GetString("emag-success", ("target", args.Target)), args.User,
_popupSystem.PopupEntity(Loc.GetString("emag-success", ("target", Identity.Entity(args.Target.Value, EntityManager))), args.User,
Filter.Entities(args.User), PopupType.Medium);
_adminLogger.Add(LogType.Emag, LogImpact.High, $"{ToPrettyString(args.User):player} emagged {ToPrettyString(args.Target.Value):target}");
component.Charges--;

View File

@@ -3,6 +3,7 @@ using Content.Server.Stunnable;
using Content.Server.Weapon.Melee;
using Content.Shared.Examine;
using Content.Shared.Flash;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
@@ -130,7 +131,7 @@ namespace Content.Server.Flash
if (displayPopup && user != null && target != user && EntityManager.EntityExists(user.Value))
{
user.Value.PopupMessage(target, Loc.GetString("flash-component-user-blinds-you",
("user", user.Value)));
("user", Identity.Entity(user.Value, EntityManager))));
}
}

View File

@@ -4,6 +4,7 @@ using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Server.DoAfter;
using Content.Server.Popups;
using Content.Shared.IdentityManagement;
using Robust.Shared.Player;
namespace Content.Server.Forensics
@@ -59,7 +60,7 @@ namespace Content.Server.Forensics
if (_inventory.TryGetSlotEntity(args.Target.Value, "gloves", out var gloves))
{
_popupSystem.PopupEntity(Loc.GetString("forensic-pad-gloves", ("target", args.Target.Value)), args.Target.Value, Filter.Entities(args.User));
_popupSystem.PopupEntity(Loc.GetString("forensic-pad-gloves", ("target", Identity.Entity(args.Target.Value, EntityManager))), args.Target.Value, Filter.Entities(args.User));
return;
}
@@ -67,8 +68,8 @@ namespace Content.Server.Forensics
{
if (args.User != args.Target)
{
_popupSystem.PopupEntity(Loc.GetString("forensic-pad-start-scan-user", ("target", args.Target.Value)), args.Target.Value, Filter.Entities(args.User));
_popupSystem.PopupEntity(Loc.GetString("forensic-pad-start-scan-target", ("user", args.User)), args.Target.Value, Filter.Entities(args.Target.Value));
_popupSystem.PopupEntity(Loc.GetString("forensic-pad-start-scan-user", ("target", Identity.Entity(args.Target.Value, EntityManager))), args.Target.Value, Filter.Entities(args.User));
_popupSystem.PopupEntity(Loc.GetString("forensic-pad-start-scan-target", ("user", Identity.Entity(args.User, EntityManager))), args.Target.Value, Filter.Entities(args.Target.Value));
}
StartScan(args.User, args.Target.Value, component, fingerprint.Fingerprint);
return;

View File

@@ -13,6 +13,7 @@ using Content.Shared.ActionBlocker;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
using Content.Shared.Input;
using Content.Shared.Inventory;
using Content.Shared.Physics.Pull;
@@ -92,10 +93,10 @@ namespace Content.Server.Hands.Systems
if (!_handsSystem.TryDrop(uid, component.ActiveHand!, null, checkActionBlocker: false))
return;
var targetName = Name(args.Target);
var msgOther = Loc.GetString("hands-component-disarm-success-others-message", ("disarmer", Name(args.Source)), ("disarmed", targetName));
var msgUser = Loc.GetString("hands-component-disarm-success-message", ("disarmed", targetName));
var targEnt = Identity.Entity(args.Target, EntityManager);
var msgOther = Loc.GetString("hands-component-disarm-success-others-message",
("disarmer", Identity.Entity(args.Source, EntityManager)), ("disarmed", targEnt));
var msgUser = Loc.GetString("hands-component-disarm-success-message", ("disarmed", targEnt));
var filter = Filter.Pvs(args.Source).RemoveWhereAttachedEntity(e => e == args.Source);
_popupSystem.PopupEntity(msgOther, args.Source, filter);

View File

@@ -1,6 +1,7 @@
using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
using Content.Shared.Verbs;
using Robust.Shared.Utility;
@@ -60,7 +61,7 @@ public sealed class HealthExaminableSystem : EntitySystem
foreach (var threshold in component.Thresholds)
{
var str = $"health-examinable-{component.LocPrefix}-{type}-{threshold}";
var tempLocStr = Loc.GetString($"health-examinable-{component.LocPrefix}-{type}-{threshold}", ("target", uid));
var tempLocStr = Loc.GetString($"health-examinable-{component.LocPrefix}-{type}-{threshold}", ("target", Identity.Entity(uid, EntityManager)));
// i.e., this string doesn't exist, because theres nothing for that threshold
if (tempLocStr == str)

View File

@@ -0,0 +1,163 @@
using Content.Server.Access.Systems;
using Content.Server.Administration.Logs;
using Content.Shared.CharacterAppearance.Components;
using Content.Shared.Database;
using Content.Shared.Hands;
using Content.Shared.IdentityManagement;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Preferences;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects.Components.Localization;
namespace Content.Server.IdentityManagement;
/// <summary>
/// Responsible for updating the identity of an entity on init or clothing equip/unequip.
/// </summary>
public class IdentitySystem : SharedIdentitySystem
{
[Dependency] private readonly IdCardSystem _idCard = default!;
[Dependency] private readonly IAdminLogManager _adminLog = default!;
private Queue<EntityUid> _queuedIdentityUpdates = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<IdentityComponent, DidEquipEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, DidEquipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, DidUnequipEvent>((uid, _, _) => QueueIdentityUpdate(uid));
SubscribeLocalEvent<IdentityComponent, DidUnequipHandEvent>((uid, _, _) => QueueIdentityUpdate(uid));
}
public override void Update(float frameTime)
{
base.Update(frameTime);
while (_queuedIdentityUpdates.TryDequeue(out var ent))
{
if (!TryComp<IdentityComponent>(ent, out var identity))
continue;
UpdateIdentityInfo(ent, identity);
}
}
// This is where the magic happens
protected override void OnComponentInit(EntityUid uid, IdentityComponent component, ComponentInit args)
{
base.OnComponentInit(uid, component, args);
var ident = Spawn(null, Transform(uid).Coordinates);
QueueIdentityUpdate(uid);
component.IdentityEntitySlot.Insert(ident);
}
/// <summary>
/// Queues an identity update to the start of the next tick.
/// </summary>
public void QueueIdentityUpdate(EntityUid uid)
{
_queuedIdentityUpdates.Enqueue(uid);
}
#region Private API
/// <summary>
/// Updates the metadata name for the id(entity) from the current state of the character.
/// </summary>
private void UpdateIdentityInfo(EntityUid uid, IdentityComponent identity)
{
if (identity.IdentityEntitySlot.ContainedEntity is not { } ident)
return;
var representation = GetIdentityRepresentation(uid);
var name = GetIdentityName(uid, representation);
// Clone the old entity's grammar to the identity entity, for loc purposes.
if (TryComp<GrammarComponent>(uid, out var grammar))
{
var identityGrammar = EnsureComp<GrammarComponent>(ident);
identityGrammar.Attributes.Clear();
foreach (var (k, v) in grammar.Attributes)
{
identityGrammar.Attributes.Add(k, v);
}
// If presumed name is null and we're using that, we set proper noun to be false ("the old woman")
if (name != representation.TrueName && representation.PresumedName == null)
identityGrammar.ProperNoun = false;
}
if (name == Name(ident))
return;
MetaData(ident).EntityName = name;
_adminLog.Add(LogType.Identity, LogImpact.Medium, $"{ToPrettyString(uid)} changed identity to {name}");
RaiseLocalEvent(new IdentityChangedEvent(uid, ident));
}
private string GetIdentityName(EntityUid target, IdentityRepresentation representation)
{
var ev = new SeeIdentityAttemptEvent();
RaiseLocalEvent(target, ev);
return representation.ToStringKnown(!ev.Cancelled);
}
/// <summary>
/// Gets an 'identity representation' of an entity, with their true name being the entity name
/// and their 'presumed name' and 'presumed job' being the name/job on their ID card, if they have one.
/// </summary>
private IdentityRepresentation GetIdentityRepresentation(EntityUid target,
InventoryComponent? inventory=null,
HumanoidAppearanceComponent? appearance=null)
{
int age = HumanoidCharacterProfile.MinimumAge;
Gender gender = Gender.Neuter;
// Always use their actual age and gender, since that can't really be changed by an ID.
if (Resolve(target, ref appearance, false))
{
gender = appearance.Gender;
age = appearance.Age;
}
var trueName = Name(target);
if (!Resolve(target, ref inventory, false))
return new(trueName, age, gender, string.Empty);
string? presumedJob = null;
string? presumedName = null;
// Get their name and job from their ID for their presumed name.
if (_idCard.TryFindIdCard(target, out var id))
{
presumedName = id.FullName;
presumedJob = id.JobTitle?.ToLowerInvariant();
}
// If it didn't find a job, that's fine.
return new(trueName, age, gender, presumedName, presumedJob);
}
#endregion
}
public sealed class IdentityChangedEvent : EntityEventArgs
{
public EntityUid CharacterEntity;
public EntityUid IdentityEntity;
public IdentityChangedEvent(EntityUid characterEntity, EntityUid identityEntity)
{
CharacterEntity = characterEntity;
IdentityEntity = identityEntity;
}
}

View File

@@ -1,5 +1,6 @@
using Content.Server.Popups;
using Content.Server.Interaction.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.MobState.Components;
using Robust.Shared.Audio;
@@ -42,7 +43,7 @@ public sealed class InteractionPopupSystem : EntitySystem
if (_random.Prob(component.SuccessChance))
{
if (component.InteractSuccessString != null)
msg = Loc.GetString(component.InteractSuccessString, ("target", uid)); // Success message (localized).
msg = Loc.GetString(component.InteractSuccessString, ("target", Identity.Entity(uid, EntityManager))); // Success message (localized).
if (component.InteractSuccessSound != null)
sfx = component.InteractSuccessSound.GetSound();
@@ -50,7 +51,7 @@ public sealed class InteractionPopupSystem : EntitySystem
else
{
if (component.InteractFailureString != null)
msg = Loc.GetString(component.InteractFailureString, ("target", uid)); // Failure message (localized).
msg = Loc.GetString(component.InteractFailureString, ("target", Identity.Entity(uid, EntityManager))); // Failure message (localized).
if (component.InteractFailureSound != null)
sfx = component.InteractFailureSound.GetSound();
@@ -58,7 +59,8 @@ public sealed class InteractionPopupSystem : EntitySystem
if (component.MessagePerceivedByOthers != null)
{
string msgOthers = Loc.GetString(component.MessagePerceivedByOthers,("user", args.User), ("target", uid));
string msgOthers = Loc.GetString(component.MessagePerceivedByOthers,
("user", Identity.Entity(args.User, EntityManager)), ("target", Identity.Entity(uid, EntityManager)));
_popupSystem.PopupEntity(msg, uid, Filter.Entities(args.User));
_popupSystem.PopupEntity(msgOthers, uid, Filter.Pvs(uid, 2F, EntityManager).RemoveWhereAttachedEntity(puid => puid == args.User));
}

View File

@@ -6,6 +6,7 @@ using Content.Server.Popups;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.DragDrop;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.MobState.Components;
using Content.Shared.Nutrition.Components;
@@ -123,7 +124,7 @@ namespace Content.Server.Kitchen.EntitySystems
UpdateAppearance(uid, null, component);
_popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-kill", ("user", userUid), ("victim", victimUid)), uid,
_popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-kill", ("user", Identity.Entity(userUid, EntityManager)), ("victim", victimUid)), uid,
Filter.Pvs(userUid), PopupType.LargeCaution);
// THE WHAT?

View File

@@ -4,6 +4,7 @@ using Content.Server.Medical.Components;
using Content.Server.Disease;
using Content.Server.Popups;
using Content.Shared.Damage;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.MobState.Components;
using Robust.Server.GameObjects;
@@ -83,7 +84,7 @@ namespace Content.Server.Medical
args.User, Filter.Entities(args.User));
return;
}
_popupSystem.PopupEntity(Loc.GetString("disease-scanner-gave-other", ("target", args.Target), ("disease", args.Component.Disease)),
_popupSystem.PopupEntity(Loc.GetString("disease-scanner-gave-other", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("disease", args.Component.Disease)),
args.User, Filter.Entities(args.User));
}

View File

@@ -13,6 +13,7 @@ using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.MobState.Components;
@@ -249,8 +250,7 @@ namespace Content.Server.Nutrition.EntitySystems
if (forceDrink)
{
EntityManager.TryGetComponent(user, out MetaDataComponent? meta);
var userName = meta?.EntityName ?? string.Empty;
var userName = Identity.Name(user, EntityManager);
_popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed", ("user", userName)),
user, Filter.Entities(target));
@@ -334,11 +334,8 @@ namespace Content.Server.Nutrition.EntitySystems
if (forceDrink)
{
EntityManager.TryGetComponent(uid, out MetaDataComponent? targetMeta);
var targetName = targetMeta?.EntityName ?? string.Empty;
EntityManager.TryGetComponent(args.User, out MetaDataComponent? userMeta);
var userName = userMeta?.EntityName ?? string.Empty;
var targetName = Identity.Name(uid, EntityManager);
var userName = Identity.Name(args.User, EntityManager);
_popupSystem.PopupEntity(
Loc.GetString("drink-component-force-feed-success", ("user", userName)), uid, Filter.Entities(uid));

View File

@@ -19,6 +19,7 @@ using Robust.Shared.Player;
using Robust.Shared.Utility;
using Content.Shared.Inventory;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
namespace Content.Server.Nutrition.EntitySystems
@@ -114,9 +115,7 @@ namespace Content.Server.Nutrition.EntitySystems
if (forceFeed)
{
EntityManager.TryGetComponent(user, out MetaDataComponent? meta);
var userName = meta?.EntityName ?? string.Empty;
var userName = Identity.Name(user, EntityManager);
_popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)),
user, Filter.Entities(target));
@@ -180,12 +179,8 @@ namespace Content.Server.Nutrition.EntitySystems
if (forceFeed)
{
EntityManager.TryGetComponent(uid, out MetaDataComponent? targetMeta);
var targetName = targetMeta?.EntityName ?? string.Empty;
EntityManager.TryGetComponent(args.User, out MetaDataComponent? userMeta);
var userName = userMeta?.EntityName ?? string.Empty;
var targetName = Identity.Name(uid, EntityManager);
var userName = Identity.Name(args.User, EntityManager);
_popupSystem.PopupEntity(Loc.GetString("food-system-force-feed-success", ("user", userName)),
uid, Filter.Entities(uid));

View File

@@ -5,6 +5,7 @@ using Content.Shared.Interaction;
using Content.Shared.Tag;
using Robust.Server.GameObjects;
using Content.Server.Popups;
using Content.Shared.IdentityManagement;
using Robust.Shared.Player;
using static Content.Shared.Paper.SharedPaperComponent;
@@ -89,9 +90,9 @@ namespace Content.Server.Paper
if (TryComp<StampComponent>(args.Used, out var stampComp) && TryStamp(uid, stampComp.StampedName, stampComp.StampState, paperComp))
{
// successfully stamped, play popup
var stampPaperOtherMessage = Loc.GetString("paper-component-action-stamp-paper-other", ("user", args.User),("target", args.Target),("stamp", args.Used));
var stampPaperOtherMessage = Loc.GetString("paper-component-action-stamp-paper-other", ("user", Identity.Entity(args.User, EntityManager)),("target", Identity.Entity(args.Target, EntityManager)),("stamp", args.Used));
_popupSystem.PopupEntity(stampPaperOtherMessage, args.User, Filter.Pvs(args.User, entityManager: EntityManager).RemoveWhereAttachedEntity(puid => puid == args.User));
var stampPaperSelfMessage = Loc.GetString("paper-component-action-stamp-paper-self", ("target", args.Target),("stamp", args.Used));
var stampPaperSelfMessage = Loc.GetString("paper-component-action-stamp-paper-self", ("target", Identity.Entity(args.Target, EntityManager)),("stamp", args.Used));
_popupSystem.PopupEntity(stampPaperSelfMessage, args.User, Filter.Entities(args.User));
}
}

View File

@@ -3,6 +3,7 @@ using Content.Server.Ghost.Components;
using Content.Server.Players;
using Content.Server.Pointing.Components;
using Content.Server.Visible;
using Content.Shared.IdentityManagement;
using Content.Shared.Input;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
@@ -155,11 +156,11 @@ namespace Content.Server.Pointing.EntitySystems
string selfMessage;
string viewerMessage;
string? viewerPointedAtMessage = null;
var playerName = Name(player);
var playerName = Identity.Entity(player, EntityManager);
if (Exists(pointed))
{
var pointedName = Name(pointed);
var pointedName = Identity.Entity(pointed, EntityManager);
selfMessage = player == pointed
? Loc.GetString("pointing-system-point-at-self")

View File

@@ -3,6 +3,7 @@ using Content.Server.CharacterAppearance.Systems;
using Content.Server.DetailExaminable;
using Content.Server.Hands.Components;
using Content.Server.Hands.Systems;
using Content.Server.IdentityManagement;
using Content.Server.PDA;
using Content.Server.Roles;
using Content.Server.Station.Components;
@@ -38,6 +39,7 @@ public sealed class StationSpawningSystem : EntitySystem
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly PDASystem _pdaSystem = default!;
[Dependency] private readonly AccessSystem _accessSystem = default!;
[Dependency] private readonly IdentitySystem _identity = default!;
/// <inheritdoc/>
public override void Initialize()
@@ -99,6 +101,7 @@ public sealed class StationSpawningSystem : EntitySystem
var jobEntity = EntityManager.SpawnEntity(job.JobEntity, coordinates);
MakeSentientCommand.MakeSentient(jobEntity, EntityManager);
DoJobSpecials(job, jobEntity);
_identity.QueueIdentityUpdate(jobEntity);
return jobEntity;
}
@@ -125,7 +128,7 @@ public sealed class StationSpawningSystem : EntitySystem
}
DoJobSpecials(job, entity);
_identity.QueueIdentityUpdate(entity);
return entity;
}

View File

@@ -36,6 +36,9 @@ public sealed class StorageFillVisualizerSystem : EntitySystem
if (!Resolve(uid, ref storage, ref appearance, ref component, false))
return;
if (component.MaxFillLevels < 1)
return;
var level = ContentHelpers.RoundToEqualLevels(storage.StorageUsed, storage.StorageCapacityMax, component.MaxFillLevels);
appearance.SetData(StorageFillVisuals.FillLevel, level);
}

View File

@@ -6,6 +6,7 @@ using Content.Server.Inventory;
using Content.Server.UserInterface;
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
@@ -306,7 +307,7 @@ namespace Content.Server.Strip
{
if (userHands.ActiveHandEntity != null)
{
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert", ("user", user), ("item", userHands.ActiveHandEntity)), component.Owner,
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert", ("user", Identity.Entity(user, EntityManager)), ("item", userHands.ActiveHandEntity)), component.Owner,
Filter.Entities(component.Owner), PopupType.Large);
}
}
@@ -369,7 +370,7 @@ namespace Content.Server.Strip
{
if (handSlot.HeldEntity != null)
{
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert", ("user", user), ("item", handSlot.HeldEntity)), component.Owner,
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert", ("user", Identity.Entity(user, EntityManager)), ("item", handSlot.HeldEntity)), component.Owner,
Filter.Entities(component.Owner), PopupType.Large);
}
}
@@ -437,7 +438,7 @@ namespace Content.Server.Strip
else
{
if (_inventorySystem.TryGetSlotEntity(component.Owner, slot, out var slotItem))
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", user), ("item", slotItem)), component.Owner,
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", slotItem)), component.Owner,
Filter.Entities(component.Owner), PopupType.Large);
}
}
@@ -501,7 +502,7 @@ namespace Content.Server.Strip
{
if (handSlot.HeldEntity != null)
{
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", user), ("item", handSlot.HeldEntity)), component.Owner, Filter.Entities(component.Owner));
_popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", handSlot.HeldEntity)), component.Owner, Filter.Entities(component.Owner));
}
}

View File

@@ -3,6 +3,7 @@ using Content.Server.CombatMode;
using Content.Server.Popups;
using Content.Shared.Audio;
using Content.Shared.Database;
using Content.Shared.IdentityManagement;
using Content.Shared.Popups;
using Content.Shared.StatusEffect;
using Content.Shared.Stunnable;
@@ -38,9 +39,11 @@ namespace Content.Server.Stunnable
var knock = EntityManager.GetComponent<KnockedDownComponent>(uid);
SoundSystem.Play(knock.StunAttemptSound.GetSound(), Filter.Pvs(source), source, AudioHelpers.WithVariation(0.025f));
var targetEnt = Identity.Entity(target, EntityManager);
var sourceEnt = Identity.Entity(source, EntityManager);
// TODO: Use PopupSystem
source.PopupMessageOtherClients(Loc.GetString("stunned-component-disarm-success-others", ("source", Name(source)), ("target", Name(target))));
source.PopupMessageCursor(Loc.GetString("stunned-component-disarm-success", ("target", Name(target))));
source.PopupMessageOtherClients(Loc.GetString("stunned-component-disarm-success-others", ("source", sourceEnt), ("target", targetEnt)));
source.PopupMessageCursor(Loc.GetString("stunned-component-disarm-success", ("target", targetEnt)));
_adminLogger.Add(LogType.DisarmedKnockdown, LogImpact.Medium, $"{ToPrettyString(args.Source):user} knocked down {ToPrettyString(args.Target):target}");

View File

@@ -73,4 +73,5 @@ public enum LogType
// haha so funny
Emag = 69,
Gib = 70,
Identity = 71
}

View File

@@ -1,11 +1,11 @@
using Content.Shared.Access.Systems;
using Content.Shared.PDA;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Access.Components
{
// TODO BUI NETWORKING if ever clients can open their own BUI's (id card console, pda), then this data should be
// networked.
[RegisterComponent]
[RegisterComponent, NetworkedComponent]
[Access(typeof(SharedIdCardSystem), typeof(SharedPDASystem), typeof(SharedAgentIdCardSystem))]
public sealed class IdCardComponent : Component
{
@@ -20,4 +20,17 @@ namespace Content.Shared.Access.Components
[DataField("jobTitle")]
public string? JobTitle;
}
[Serializable, NetSerializable]
public sealed class IdCardComponentState : ComponentState
{
public string? FullName;
public string? JobTitle;
public IdCardComponentState(string? fullName, string? jobTitle)
{
FullName = fullName;
JobTitle = jobTitle;
}
}
}

View File

@@ -1,6 +1,80 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Access.Components;
using Content.Shared.Hands.Components;
using Content.Shared.Inventory;
using Content.Shared.PDA;
using Robust.Shared.GameStates;
namespace Content.Shared.Access.Systems;
public abstract class SharedIdCardSystem : EntitySystem
{
// this class just exists to make friends. Will you be its friend?
[Dependency] private readonly InventorySystem _inventorySystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<IdCardComponent, ComponentGetState>(OnComponentGetState);
SubscribeLocalEvent<IdCardComponent, ComponentHandleState>(OnComponentHandleState);
}
private void OnComponentGetState(EntityUid uid, IdCardComponent component, ref ComponentGetState args)
{
args.State = new IdCardComponentState(component.FullName, component.JobTitle);
}
private void OnComponentHandleState(EntityUid uid, IdCardComponent component, ref ComponentHandleState args)
{
if (args.Current is IdCardComponentState state)
{
component.FullName = state.FullName;
component.JobTitle = state.JobTitle;
}
}
/// <summary>
/// Attempt to find an ID card on an entity. This will look in the entity itself, in the entity's hands, and
/// in the entity's inventory.
/// </summary>
public bool TryFindIdCard(EntityUid uid, [NotNullWhen(true)] out IdCardComponent? idCard)
{
// check held item?
if (EntityManager.TryGetComponent(uid, out SharedHandsComponent? hands) &&
hands.ActiveHandEntity is EntityUid heldItem &&
TryGetIdCard(heldItem, out idCard))
{
return true;
}
// check entity itself
if (TryGetIdCard(uid, out idCard))
return true;
// check inventory slot?
if (_inventorySystem.TryGetSlotEntity(uid, "id", out var idUid) && TryGetIdCard(idUid.Value, out idCard))
{
return true;
}
return false;
}
/// <summary>
/// Attempt to get an id card component from an entity, either by getting it directly from the entity, or by
/// getting the contained id from a <see cref="PDAComponent"/>.
/// </summary>
public bool TryGetIdCard(EntityUid uid, [NotNullWhen(true)] out IdCardComponent? idCard)
{
if (EntityManager.TryGetComponent(uid, out idCard))
return true;
if (EntityManager.TryGetComponent(uid, out PDAComponent? pda) && pda.ContainedID != null)
{
idCard = pda.ContainedID;
return true;
}
return false;
}
}

View File

@@ -4,5 +4,5 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Administration
{
[Serializable, NetSerializable]
public record PlayerInfo(string Username, string CharacterName, string StartingJob, bool Antag, EntityUid EntityUid, NetUserId SessionId, bool Connected);
public record PlayerInfo(string Username, string CharacterName, string IdentityName, string StartingJob, bool Antag, EntityUid EntityUid, NetUserId SessionId, bool Connected);
}

View File

@@ -2,6 +2,7 @@
using Content.Shared.Actions.ActionTypes;
using Content.Shared.Hands;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
using Content.Shared.Physics;
using Content.Shared.Popups;
using Content.Shared.Toggleable;
@@ -105,8 +106,9 @@ public sealed class BlockingSystem : EntitySystem
var shieldName = Name(item);
var blockerName = Identity.Entity(user, EntityManager);
var msgUser = Loc.GetString("action-popup-blocking-user", ("shield", shieldName));
var msgOther = Loc.GetString("action-popup-blocking-other", ("blockerName", Name(user)), ("shield", shieldName));
var msgOther = Loc.GetString("action-popup-blocking-other", ("blockerName", blockerName), ("shield", shieldName));
if (component.BlockingToggleAction != null)
{
@@ -154,8 +156,9 @@ public sealed class BlockingSystem : EntitySystem
var shieldName = Name(item);
var blockerName = Identity.Entity(user, EntityManager);
var msgUser = Loc.GetString("action-popup-blocking-disabling-user", ("shield", shieldName));
var msgOther = Loc.GetString("action-popup-blocking-disabling-other", ("blockerName", Name(user)), ("shield", shieldName));
var msgOther = Loc.GetString("action-popup-blocking-disabling-other", ("blockerName", blockerName), ("shield", shieldName));
//If the component blocking toggle isn't null, grab the users SharedBlockingUserComponent and PhysicsComponent
//then toggle the action to false, unanchor the user, remove the hard fixture

View File

@@ -1,4 +1,5 @@
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.Preferences;
using Content.Shared.Species;
using Robust.Shared.Enums;
using Robust.Shared.GameStates;
@@ -23,6 +24,9 @@ namespace Content.Shared.CharacterAppearance.Components
[ViewVariables]
public string Species { get; set; } = SpeciesManager.DefaultSpecies;
[ViewVariables(VVAccess.ReadWrite)]
public int Age { get; set; } = HumanoidCharacterProfile.MinimumAge;
[DataField("categoriesHair")]
[ViewVariables]
public SpriteAccessoryCategories CategoriesHair { get; set; } = SpriteAccessoryCategories.HumanHair;
@@ -55,16 +59,19 @@ namespace Content.Shared.CharacterAppearance.Components
public Sex Sex { get; }
public Gender Gender { get; }
public string Species { get; }
public int Age { get; }
public HumanoidAppearanceComponentState(HumanoidCharacterAppearance appearance,
Sex sex,
Gender gender,
string species)
string species,
int age)
{
Appearance = appearance;
Sex = sex;
Gender = gender;
Species = species;
Age = age;
}
}
}

View File

@@ -19,7 +19,7 @@ namespace Content.Shared.CharacterAppearance.Systems
public void UpdateFromProfile(EntityUid uid, ICharacterProfile profile, HumanoidAppearanceComponent? appearance=null)
{
var humanoid = (HumanoidCharacterProfile) profile;
UpdateAppearance(uid, humanoid.Appearance, humanoid.Sex, humanoid.Gender, humanoid.Species, appearance);
UpdateAppearance(uid, humanoid.Appearance, humanoid.Sex, humanoid.Gender, humanoid.Species, humanoid.Age, appearance);
}
// The magic mirror otherwise wouldn't work. (it directly modifies the component server-side)
@@ -29,7 +29,7 @@ namespace Content.Shared.CharacterAppearance.Systems
component.Dirty();
}
private void UpdateAppearance(EntityUid uid, HumanoidCharacterAppearance appearance, Sex sex, Gender gender, string species, HumanoidAppearanceComponent? component = null)
private void UpdateAppearance(EntityUid uid, HumanoidCharacterAppearance appearance, Sex sex, Gender gender, string species, int age, HumanoidAppearanceComponent? component = null)
{
if (!Resolve(uid, ref component)) return;
@@ -37,6 +37,7 @@ namespace Content.Shared.CharacterAppearance.Systems
component.Sex = sex;
component.Gender = gender;
component.Species = species;
component.Age = age;
if (EntityManager.TryGetComponent(uid, out GrammarComponent? g))
g.Gender = gender;
@@ -59,7 +60,7 @@ namespace Content.Shared.CharacterAppearance.Systems
private void OnAppearanceGetState(EntityUid uid, HumanoidAppearanceComponent component, ref ComponentGetState args)
{
args.State = new HumanoidAppearanceComponentState(component.Appearance, component.Sex, component.Gender, component.Species);
args.State = new HumanoidAppearanceComponentState(component.Appearance, component.Sex, component.Gender, component.Species, component.Age);
}
private void OnAppearanceHandleState(EntityUid uid, HumanoidAppearanceComponent component, ref ComponentHandleState args)
@@ -67,7 +68,7 @@ namespace Content.Shared.CharacterAppearance.Systems
if (args.Current is not HumanoidAppearanceComponentState state)
return;
UpdateAppearance(uid, state.Appearance, state.Sex, state.Gender, state.Species);
UpdateAppearance(uid, state.Appearance, state.Sex, state.Gender, state.Species, state.Age);
}
// Scaffolding until Body is moved to ECS.
@@ -106,7 +107,6 @@ namespace Content.Shared.CharacterAppearance.Systems
Uid = uid;
Args = args;
}
}
[Serializable, NetSerializable]

View File

@@ -1,5 +1,6 @@
using Content.Shared.Examine;
using Content.Shared.Hands.Components;
using Content.Shared.IdentityManagement;
using Content.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
@@ -166,7 +167,7 @@ public abstract partial class SharedHandsSystem : EntitySystem
if (HasComp<HandVirtualItemComponent>(inhand))
continue;
args.PushText(Loc.GetString("comp-hands-examine", ("user", handsComp.Owner), ("item", inhand)));
args.PushText(Loc.GetString("comp-hands-examine", ("user", Identity.Entity(handsComp.Owner, EntityManager)), ("item", inhand)));
}
}
}

View File

@@ -0,0 +1,18 @@
using Content.Shared.Inventory;
using Robust.Shared.GameStates;
namespace Content.Shared.IdentityManagement.Components;
[RegisterComponent, NetworkedComponent]
public sealed class IdentityBlockerComponent : Component
{
}
/// <summary>
/// Raised on an entity and relayed to inventory to determine if its identity should be knowable.
/// </summary>
public sealed class SeeIdentityAttemptEvent : CancellableEntityEventArgs, IInventoryRelayEvent
{
// i.e. masks or helmets.
public SlotFlags TargetSlots => SlotFlags.MASK | SlotFlags.HEAD;
}

View File

@@ -0,0 +1,76 @@
using Robust.Shared.Containers;
using Robust.Shared.Enums;
namespace Content.Shared.IdentityManagement.Components;
/// <summary>
/// Stores the identity entity (whose name is the users 'identity', etc)
/// for a given entity, and marks that it can have an identity at all.
/// </summary>
/// <remarks>
/// This is a <see cref="ContainerSlot"/> and not just a datum entity because we do sort of care that it gets deleted and sent with the user.
/// </remarks>
[RegisterComponent]
public sealed class IdentityComponent : Component
{
[ViewVariables]
public ContainerSlot IdentityEntitySlot = default!;
}
/// <summary>
/// A data structure representing the 'identity' of an entity as presented to
/// other players.
/// </summary>
public sealed class IdentityRepresentation
{
public string TrueName;
public int TrueAge;
public Gender TrueGender;
public string? PresumedName;
public string? PresumedJob;
public IdentityRepresentation(string trueName, int trueAge, Gender trueGender, string? presumedName=null, string? presumedJob=null)
{
TrueName = trueName;
TrueAge = trueAge;
TrueGender = trueGender;
PresumedJob = presumedJob;
PresumedName = presumedName;
}
public string ToStringKnown(bool trueName)
{
return trueName
? TrueName
: PresumedName ?? ToStringUnknown();
}
/// <summary>
/// Returns a string representing their identity where it is 'unknown' by a viewer.
/// Used for cases where the viewer is not necessarily able to accurately assess
/// the identity of the person being viewed.
/// </summary>
public string ToStringUnknown()
{
var ageString = TrueAge switch
{
<= 30 => Loc.GetString("identity-age-young"),
> 30 and <= 60 => Loc.GetString("identity-age-middle-aged"),
> 60 => Loc.GetString("identity-age-old")
};
var genderString = TrueGender switch
{
Gender.Female => Loc.GetString("identity-gender-feminine"),
Gender.Male => Loc.GetString("identity-gender-masculine"),
Gender.Epicene or Gender.Neuter or _ => Loc.GetString("identity-gender-person")
};
// i.e. 'young assistant man' or 'old cargo technician person' or 'middle-aged captain'
return PresumedJob is null
? $"{ageString} {genderString}"
: $"{ageString} {PresumedJob} {genderString}";
}
}

View File

@@ -0,0 +1,60 @@
using Content.Shared.Ghost;
using Content.Shared.IdentityManagement.Components;
namespace Content.Shared.IdentityManagement;
/// <summary>
/// Static content API for getting the identity entities/names for a given entity.
/// This should almost always be used in favor of metadata name, if the entity in question is a human player that
/// can have identity.
/// </summary>
public static class Identity
{
/// <summary>
/// Returns the name that should be used for this entity for identity purposes.
/// </summary>
public static string Name(EntityUid uid, IEntityManager ent, EntityUid? viewer=null)
{
var uidName = ent.GetComponent<MetaDataComponent>(uid).EntityName;
if (!ent.TryGetComponent<IdentityComponent>(uid, out var identity))
return uidName;
var ident = identity.IdentityEntitySlot.ContainedEntity;
if (ident is null)
return uidName;
var identName = ent.GetComponent<MetaDataComponent>(ident.Value).EntityName;
if (viewer == null || !CanSeeThroughIdentity(uid, viewer.Value, ent))
{
return identName;
}
if (uidName == identName)
{
return uidName;
}
return uidName + $" ({identName})";
}
/// <summary>
/// Returns the entity that should be used for identity purposes, for example to pass into localization.
/// This is an extension method because of its simplicity, and if it was any harder to call it might not
/// be used enough for loc.
/// </summary>
public static EntityUid Entity(EntityUid uid, IEntityManager ent)
{
if (!ent.TryGetComponent<IdentityComponent>(uid, out var identity))
return uid;
return identity.IdentityEntitySlot.ContainedEntity ?? uid;
}
public static bool CanSeeThroughIdentity(EntityUid uid, EntityUid viewer, IEntityManager ent)
{
// Would check for uid == viewer here but I think it's better for you to see yourself
// how everyone else will see you, otherwise people will probably get confused and think they aren't disguised
return ent.HasComponent<SharedGhostComponent>(viewer);
}
}

View File

@@ -0,0 +1,28 @@
using Content.Shared.IdentityManagement.Components;
using Robust.Shared.Containers;
namespace Content.Shared.IdentityManagement;
public abstract class SharedIdentitySystem : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
private static string SlotName = "identity";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<IdentityComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<IdentityBlockerComponent, SeeIdentityAttemptEvent>(OnSeeIdentity);
}
private void OnSeeIdentity(EntityUid uid, IdentityBlockerComponent component, SeeIdentityAttemptEvent args)
{
args.Cancel();
}
protected virtual void OnComponentInit(EntityUid uid, IdentityComponent component, ComponentInit args)
{
component.IdentityEntitySlot = _container.EnsureContainer<ContainerSlot>(uid, SlotName);
}
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.Damage;
using Content.Shared.Electrocution;
using Content.Shared.Explosion;
using Content.Shared.IdentityManagement.Components;
using Content.Shared.Movement.Systems;
using Content.Shared.Slippery;
using Content.Shared.Strip.Components;
@@ -17,6 +18,7 @@ public partial class InventorySystem
SubscribeLocalEvent<InventoryComponent, RefreshMovementSpeedModifiersEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, GetExplosionResistanceEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, BeforeStripEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, SeeIdentityAttemptEvent>(RelayInventoryEvent);
}
protected void RelayInventoryEvent<T>(EntityUid uid, InventoryComponent component, T args) where T : EntityEventArgs, IInventoryRelayEvent

View File

@@ -4,7 +4,7 @@ action-description-blocking = Raise or lower your shield.
action-popup-blocking-user = You raise your {$shield}!
action-popup-blocking-disabling-user = You lower your {$shield}!
action-popup-blocking-other = {$blockerName} raises their {$shield}!
action-popup-blocking-disabling-other = {$blockerName} lowers their {$shield}!
action-popup-blocking-other = {CAPITALIZE(THE($blockerName))} raises {POSS-ADJ($blockerName)} {$shield}!
action-popup-blocking-disabling-other = {CAPITALIZE(THE($blockerName))} lowers {POSS-ADJ($blockerName)} {$shield}!
action-popup-blocking-user-cant-block = The gravity here prevents you from blocking.

View File

@@ -1,7 +1,7 @@
disarm-action-free-hand = You need to use a free hand to disarm!
disarm-action-popup-message-other-clients = {$performerName} fails to disarm {$targetName}!
disarm-action-popup-message-cursor = You fail to disarm {$targetName}!
disarm-action-popup-message-other-clients = {CAPITALIZE(THE($performerName))} fails to disarm {THE($targetName)}!
disarm-action-popup-message-cursor = You fail to disarm {THE($targetName)}!
action-name-disarm = [color=red]Disarm[/color]
action-description-disarm = Attempt to [color=red]disarm[/color] someone.

View File

@@ -21,4 +21,4 @@ injector-component-target-is-empty-message = {$target} is empty!
## mob-inject doafter messages
injector-component-injecting-user = You start inserting the needle.
injector-component-injecting-target = {$user} is trying to stick a needle into you!
injector-component-injecting-target = {CAPITALIZE(THE($user))} is trying to stick a needle into you!

View File

@@ -2,9 +2,9 @@
pointing-system-try-point-cannot-reach = You can't reach there!
pointing-system-point-at-self = You point at yourself.
pointing-system-point-at-other = You point at {$other}.
pointing-system-point-at-self-others = {$otherName} points at {$other}.
pointing-system-point-at-other-others = {$otherName} points at {$other}.
pointing-system-point-at-other = You point at {THE($other)}.
pointing-system-point-at-self-others = {CAPITALIZE(THE($otherName))} points at {THE($other)}.
pointing-system-point-at-other-others = {CAPITALIZE(THE($otherName))} points at {THE($other)}.
pointing-system-point-at-you-other = {$otherName} points at you.
pointing-system-point-at-tile = You point at the {$tileName}.
pointing-system-other-point-at-tile = {$otherName} points at the {$tileName}.
pointing-system-other-point-at-tile = {CAPITALIZE(THE($otherName))} points at the {$tileName}.

View File

@@ -1,4 +1,4 @@
hands-component-disarm-success-others-message = {$disarmer} disarms {$disarmed}!
hands-component-disarm-success-message = You disarm {$disarmed}!
hands-component-shove-success-others-message = {$shover} shoves {$shoved}!
hands-component-shove-success-message = You shove {$shoved}!
hands-component-disarm-success-others-message = {CAPITALIZE(THE($disarmer))} disarms {THE($disarmed)}!
hands-component-disarm-success-message = You disarm {THE($disarmed)}!
hands-component-shove-success-others-message = {CAPITALIZE(THE($shover))} shoves {THE($shoved)}!
hands-component-shove-success-message = You shove {THE($shoved)}!

View File

@@ -0,0 +1,9 @@
identity-unknown-name = ???
identity-age-young = young
identity-age-middle-aged = middle-aged
identity-age-old = old
identity-gender-feminine = woman
identity-gender-masculine = man
identity-gender-person = person

View File

@@ -1,2 +1,2 @@
stunnable-component-disarm-success-others = {$source} pushes {$target}!
stunnable-component-disarm-success = You push {$target}!
stunnable-component-disarm-success-others = {CAPITALIZE(THE($source))} pushes {THE($target)}!
stunnable-component-disarm-success = You push {THE($target)}!

View File

@@ -1,2 +1,2 @@
stunned-component-disarm-success = You push {$target} down!
stunned-component-disarm-success-others = {$source} pushes {$target} down!
stunned-component-disarm-success = You push {THE($target)} down!
stunned-component-disarm-success-others = {CAPITALIZE(THE($source))} pushes {THE($target)} down!

View File

@@ -42,6 +42,7 @@
- HidesHair
- type: DiseaseProtection
protection: 0.05
- type: IdentityBlocker
- type: entity
abstract: true
@@ -76,6 +77,7 @@
- HidesHair
- type: DiseaseProtection
protection: 0.05
- type: IdentityBlocker
- type: entity
abstract: true

View File

@@ -17,6 +17,7 @@
Blunt: 0.5
Slash: 0.8
Piercing: 0.8
- type: IdentityBlocker
- type: entity
parent: ClothingHeadEVAHelmetBase
@@ -143,6 +144,7 @@
- type: Tag
tags:
- HidesHair
- type: IdentityBlocker
- type: entity
parent: ClothingHeadEVAHelmetBase
@@ -167,6 +169,7 @@
- type: Clothing
sprite: Clothing/Head/Helmets/templar.rsi
- type: IngestionBlocker
- type: IdentityBlocker
- type: entity
parent: ClothingHeadBase
@@ -190,6 +193,7 @@
- type: Clothing
sprite: Clothing/Head/Helmets/wizardhelm.rsi
- type: IngestionBlocker
- type: IdentityBlocker
- type: entity
parent: ClothingHeadHardsuitWithLightBase
@@ -221,7 +225,7 @@
- type: PressureProtection
highPressureMultiplier: 0.65
lowPressureMultiplier: 1000
- type: IdentityBlocker
- type: entity
parent: ClothingHeadHardsuitWithLightBase
@@ -250,6 +254,7 @@
- type: PressureProtection
highPressureMultiplier: 0.45
lowPressureMultiplier: 1000
- type: IdentityBlocker
- type: entity
parent: ClothingHeadBase
@@ -268,6 +273,7 @@
Slash: 0.5
Piercing: 0.5
Heat: 0.9
- type: IdentityBlocker
- type: entity
parent: ClothingHeadEVAHelmetBase

View File

@@ -6,6 +6,7 @@
components:
- type: IngestionBlocker
- type: FlashImmunity
- type: IdentityBlocker
- type: entity
parent: WeldingMaskBase

View File

@@ -12,9 +12,10 @@
- type: IngestionBlocker
- type: DiseaseProtection
protection: 0.05
- type: IdentityBlocker
- type: entity
parent: ClothingMaskPullableBase
parent: ClothingMaskGas
id: ClothingMaskGasSecurity
name: security gas mask
description: A standard issue Security gas mask.
@@ -23,13 +24,9 @@
sprite: Clothing/Mask/gassecurity.rsi
- type: Clothing
sprite: Clothing/Mask/gassecurity.rsi
- type: BreathMask
- type: IngestionBlocker
- type: DiseaseProtection
protection: 0.05
- type: entity
parent: ClothingMaskPullableBase
parent: ClothingMaskGas
id: ClothingMaskGasSyndicate
name: syndicate gas mask
description: A close-fitting tactical mask that can be connected to an air supply.
@@ -38,14 +35,10 @@
sprite: Clothing/Mask/gassyndicate.rsi
- type: Clothing
sprite: Clothing/Mask/gassyndicate.rsi
- type: BreathMask
- type: IngestionBlocker
- type: DiseaseProtection
protection: 0.05
- type: FlashImmunity
- type: entity
parent: ClothingMaskPullableBase
parent: ClothingMaskGas
id: ClothingMaskGasAtmos
name: atmospheric gas mask
description: Improved gas mask utilized by atmospheric technicians. It's flameproof!
@@ -54,10 +47,6 @@
sprite: Clothing/Mask/gasatmos.rsi
- type: Clothing
sprite: Clothing/Mask/gasatmos.rsi
- type: BreathMask
- type: IngestionBlocker
- type: DiseaseProtection
protection: 0.05
- type: Armor
modifiers:
coefficients:
@@ -94,7 +83,7 @@
protection: 0.05
- type: entity
parent: ClothingMaskPullableBase
parent: ClothingMaskGas
id: ClothingMaskGasExplorer
name: explorer gas mask
description: A military-grade gas mask that can be connected to an air supply.
@@ -103,10 +92,6 @@
sprite: Clothing/Mask/gasexplorer.rsi
- type: Clothing
sprite: Clothing/Mask/gasexplorer.rsi
- type: BreathMask
- type: IngestionBlocker
- type: DiseaseProtection
protection: 0.05
- type: Armor
modifiers:
coefficients:

View File

@@ -32,6 +32,7 @@
- !type:WashCreamPieReaction
- type: Flashable
- type: Polymorphable
- type: Identity
- type: Hands
- type: MovementSpeedModifier
- type: MovedByPressure