Files
wwdpublic/Content.Shared/_EE/Contractors/Systems/SharedPassportSystem.cs
Timfa 6bec449856 Allow Admins to Spawn a Passport for Players (#2150)
<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Description

<!--
Explain this PR in as much detail as applicable

Some example prompts to consider:
How might this affect the game? The codebase?
What might be some alternatives to this?
How/Who does this benefit/hurt [the game/codebase]?
-->

Adds an admin command to spawn a passport

---

<!--
This is default collapsed, readers click to expand it and see all your
media
The PR media section can get very large at times, so this is a good way
to keep it clean
The title is written using HTML tags
The title must be within the <summary> tags or you won't see it
-->

<details><summary><h1>Media</h1></summary>
<p>

![image](https://github.com/user-attachments/assets/2f81d318-45d7-445c-8857-88d53a7a786e)

</p>
</details>

---

# Changelog

<!--
You can add an author after the `🆑` to change the name that appears
in the changelog (ex: `🆑 Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

🆑
- add: Added admin command to spawn a passport for a player

---------

Signed-off-by: Timfa <timfalken@hotmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
(cherry picked from commit e7497abdadc017ff2f4494f505c253059d688e83)
2025-04-04 15:02:05 +03:00

164 lines
6.4 KiB
C#

using Content.Shared._EE.Contractors.Components;
using Content.Shared._EE.Contractors.Prototypes;
using Content.Shared.Administration.Logs;
using Content.Shared.Clothing.Loadouts.Systems;
using Content.Shared.Database;
using Content.Shared.Examine;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Interaction.Events;
using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.Preferences;
using Content.Shared.Storage;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared;
using Content.Shared.CCVar;
using Content.Shared.Roles;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Shared._EE.Contractors.Systems;
public class SharedPassportSystem : EntitySystem
{
public const int CurrentYear = 2450;
const string PIDChars = "ABCDEFGHJKLMNPQRSTUVWXYZ0123456789";
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly SharedStorageSystem _storage = default!;
[Dependency] private readonly SharedTransformSystem _sharedTransformSystem = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PassportComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<PlayerLoadoutAppliedEvent>(OnPlayerLoadoutApplied);
SubscribeLocalEvent<PassportComponent, ExaminedEvent>(OnExamined);
}
private void OnExamined(EntityUid uid, PassportComponent component, ExaminedEvent args)
{
if (!args.IsInDetailsRange
|| component.IsClosed
|| component.OwnerProfile == null)
return;
var species = _prototypeManager.Index<SpeciesPrototype>(component.OwnerProfile.Species);
args.PushMarkup($"Registered to: {component.OwnerProfile.Name}", 50);
args.PushMarkup($"Species: {Loc.GetString(species.Name)}", 49);
args.PushMarkup($"Sex: {component.OwnerProfile.Gender}", 48);
args.PushMarkup($"Height: {MathF.Round(component.OwnerProfile.Height * species.AverageHeight)} cm", 47);
args.PushMarkup($"Year of Birth: {CurrentYear - component.OwnerProfile.Age}", 46);
args.PushMarkup(
$"PID: {GenerateIdentityString(component.OwnerProfile.Name
+ component.OwnerProfile.Height
+ component.OwnerProfile.Age
+ component.OwnerProfile.Height
+ component.OwnerProfile.FlavorText)}",
45);
}
private void OnPlayerLoadoutApplied(PlayerLoadoutAppliedEvent ev) =>
SpawnPassportForPlayer(ev.Mob, ev.Profile, ev.JobId);
public void SpawnPassportForPlayer(EntityUid mob, HumanoidCharacterProfile profile, string? jobId)
{
if (jobId == null || !_prototypeManager.TryIndex(
jobId,
out JobPrototype? jobPrototype)
|| !jobPrototype.CanHavePassport
|| Deleted(mob)
|| !Exists(mob)
|| !ShouldSpawnPassports)
return;
if (!_prototypeManager.TryIndex(
profile.Nationality,
out NationalityPrototype? nationalityPrototype) || !_prototypeManager.TryIndex(nationalityPrototype.PassportPrototype, out EntityPrototype? entityPrototype))
return;
var passportEntity = _entityManager.SpawnEntity(entityPrototype.ID, _sharedTransformSystem.GetMapCoordinates(mob));
var passportComponent = _entityManager.GetComponent<PassportComponent>(passportEntity);
UpdatePassportProfile(new(passportEntity, passportComponent), profile);
// Try to find back-mounted storage apparatus
if (_inventory.TryGetSlotEntity(mob, "back", out var item) &&
EntityManager.TryGetComponent<StorageComponent>(item, out var inventory))
// Try inserting the entity into the storage, if it can't, it leaves the loadout item on the ground
{
if (!EntityManager.TryGetComponent<ItemComponent>(passportEntity, out var itemComp)
|| !_storage.CanInsert(item.Value, passportEntity, out _, inventory, itemComp)
|| !_storage.Insert(item.Value, passportEntity, out _, playSound: false))
{
_adminLogManager.Add(
LogType.EntitySpawn,
LogImpact.Low,
$"Passport for {profile.Name} was spawned on the floor due to missing bag space");
}
}
}
private bool ShouldSpawnPassports =>
_configManager.GetCVar(CCVar.CCVars.ContractorsEnabled) &&
_configManager.GetCVar(CCVar.CCVars.ContractorsPassportEnabled);
public void UpdatePassportProfile(Entity<PassportComponent> passport, HumanoidCharacterProfile profile)
{
passport.Comp.OwnerProfile = profile;
var evt = new PassportProfileUpdatedEvent(profile);
RaiseLocalEvent(passport, ref evt);
}
private void OnUseInHand(Entity<PassportComponent> passport, ref UseInHandEvent evt)
{
if (evt.Handled || !_timing.IsFirstTimePredicted)
return;
evt.Handled = true;
passport.Comp.IsClosed = !passport.Comp.IsClosed;
var passportEvent = new PassportToggleEvent();
RaiseLocalEvent(passport, ref passportEvent);
}
private static string GenerateIdentityString(string seed)
{
var hashCode = seed.GetHashCode();
System.Random random = new System.Random(hashCode);
char[] result = new char[17]; // 15 characters + 2 dashes
int j = 0;
for (int i = 0; i < 15; i++)
{
if (i == 5 || i == 10)
{
result[j++] = '-';
}
result[j++] = PIDChars[random.Next(PIDChars.Length)];
}
return new string(result);
}
[ByRefEvent]
public sealed class PassportToggleEvent : HandledEntityEventArgs {}
[ByRefEvent]
public sealed class PassportProfileUpdatedEvent(HumanoidCharacterProfile profile) : HandledEntityEventArgs
{
public HumanoidCharacterProfile Profile { get; } = profile;
}
}