Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

У всех есть маленькие секреты. #132

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Content.Shared._CorvaxNext.Implants.Radio;

namespace Content.Client._CorvaxNext.Implants.Radio;

/// <inheritdoc />
public sealed class RadioImplantSystem : SharedRadioImplantSystem
{
}
Vonsant marked this conversation as resolved.
Show resolved Hide resolved
126 changes: 126 additions & 0 deletions Content.Server/_CorvaxNext/Implants/Radio/RadioImplantSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
using Content.Server.Chat.Systems;
using Content.Server.Radio;
using Content.Server.Radio.Components;
using Content.Server.Radio.EntitySystems;
using Content.Shared._CorvaxNext.Implants.Radio;
using Content.Shared.Radio.Components;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Player;

namespace Content.Server._CorvaxNext.Implants.Radio;

/// <inheritdoc />
public sealed class RadioImplantSystem : SharedRadioImplantSystem
{
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly RadioSystem _radioSystem = default!;

private EntityQuery<ActorComponent> _actorQuery;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RadioImplantComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<RadioImplantComponent, EntInsertedIntoContainerMessage>(OnInsertEncryptionKey);
SubscribeLocalEvent<RadioImplantComponent, EntRemovedFromContainerMessage>(OnRemoveEncryptionKey);
SubscribeLocalEvent<RadioImplantComponent, RadioReceiveEvent>(OnRadioReceive);
SubscribeLocalEvent<HasRadioImplantComponent, EntitySpokeEvent>(OnSpeak);
_actorQuery = GetEntityQuery<ActorComponent>();
}

/// <summary>
/// Ensures implants with fixed channels work.
/// </summary>
private void OnMapInit(Entity<RadioImplantComponent> ent, ref MapInitEvent args)
{
UpdateRadioReception(ent);
}

/// <summary>
/// Handles the implantee's speech being forwarded onto the radio channel of the implant.
/// </summary>
private void OnSpeak(Entity<HasRadioImplantComponent> ent, ref EntitySpokeEvent args)
{
// not a radio message, or already handled by another radio
if (args.Channel is null)
return;

// does the implant have access to the channel the implantee is trying to speak on?
if (ent.Comp.Implant is {} implant
&& TryComp<RadioImplantComponent>(implant, out var radioImplantComponent)
&& radioImplantComponent.Channels.Contains(args.Channel.ID))
Vonsant marked this conversation as resolved.
Show resolved Hide resolved
{
_radioSystem.SendRadioMessage(ent, args.Message, args.Channel.ID, implant);
// prevent other radios they might be wearing from sending the message again
args.Channel = null;
}
}

/// <summary>
/// Handles receiving radio messages and forwarding them to the implantee.
/// </summary>
private void OnRadioReceive(EntityUid uid, RadioImplantComponent component, ref RadioReceiveEvent args)
{
if (_actorQuery.TryComp(component.Implantee, out var actorComponent))
_netManager.ServerSendMessage(args.ChatMsg, actorComponent.PlayerSession.Channel);
}

/// <summary>
/// Handles the addition of an encryption key to the implant's storage.
/// </summary>
private void OnInsertEncryptionKey(Entity<RadioImplantComponent> ent, ref EntInsertedIntoContainerMessage args)
{
// check if the insertion is actually something getting inserted into the radio implant storage, since
// this evt also fires when the radio implant is being inserted into a person.
if (ent.Owner != args.Container.Owner
|| !TryComp<EncryptionKeyComponent>(args.Entity, out var encryptionKeyComponent))
return;
Vonsant marked this conversation as resolved.
Show resolved Hide resolved

// copy over the radio channels that can be accessed
ent.Comp.Channels.Clear();
foreach (var channel in encryptionKeyComponent.Channels)
{
ent.Comp.Channels.Add(channel);
}
Dirty(ent);
UpdateRadioReception(ent);
}

/// <summary>
/// Handles the removal of an encryption key from the implant's storage.
/// </summary>
private void OnRemoveEncryptionKey(Entity<RadioImplantComponent> ent, ref EntRemovedFromContainerMessage args)
{
// check if the insertion is actually something getting inserted into the radio implant storage, since
// this evt also fires when the radio implant is being inserted into a person.
if (ent.Owner != args.Container.Owner
|| !HasComp<EncryptionKeyComponent>(args.Entity))
return;

// clear the radio channels since there's no encryption key inserted anymore.
ent.Comp.Channels.Clear();
Dirty(ent);
UpdateRadioReception(ent);
}

/// <summary>
/// Ensures that this thing can actually hear radio messages from channels the key provides.
/// </summary>
private void UpdateRadioReception(Entity<RadioImplantComponent> ent)
{
if (ent.Comp.Channels.Count != 0)
{
// we need to add this comp to actually receive radio events.
var channels = EnsureComp<ActiveRadioComponent>(ent).Channels;
foreach (var channel in ent.Comp.Channels)
{
channels.Add(channel);
}
}
else
{
RemComp<ActiveRadioComponent>(ent);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Content.Server.Nutrition;
using Content.Server.Speech;
using Content.Server.Speech.EntitySystems;
using Content.Shared._CorvaxNext.Storage.Components;
using Content.Shared._CorvaxNext.Storage.EntitySystems;
using Content.Shared.Storage;

namespace Content.Server._CorvaxNext.Storage.EntitySystems;

public sealed class MouthStorageSystem : SharedMouthStorageSystem
{
[Dependency] private readonly ReplacementAccentSystem _replacement = default!;
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<MouthStorageComponent, AccentGetEvent>(OnAccent);
SubscribeLocalEvent<MouthStorageComponent, IngestionAttemptEvent>(OnIngestAttempt);
}

// Force you to mumble if you have items in your mouth
private void OnAccent(EntityUid uid, MouthStorageComponent component, AccentGetEvent args)
{
if (IsMouthBlocked(component))
args.Message = _replacement.ApplyReplacements(args.Message, "mumble");
Vonsant marked this conversation as resolved.
Show resolved Hide resolved
}

// Attempting to eat or drink anything with items in your mouth won't work
private void OnIngestAttempt(EntityUid uid, MouthStorageComponent component, IngestionAttemptEvent args)
{
if (!IsMouthBlocked(component))
return;

if (!TryComp<StorageComponent>(component.MouthId, out var storage))
return;

var firstItem = storage.Container.ContainedEntities[0];
args.Blocker = firstItem;
args.Cancel();
}
}
29 changes: 23 additions & 6 deletions Content.Shared/Storage/EntitySystems/DumpableSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Content.Shared.Placeable;
using Content.Shared.Storage.Components;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
Expand Down Expand Up @@ -139,27 +140,43 @@ private void StartDoAfter(EntityUid storageUid, EntityUid targetUid, EntityUid u

private void OnDoAfter(EntityUid uid, DumpableComponent component, DumpableDoAfterEvent args)
{
if (args.Handled || args.Cancelled || !TryComp<StorageComponent>(uid, out var storage) || storage.Container.ContainedEntities.Count == 0)
// Corvax-Next-MouthStorage-Start
if (args.Handled || args.Cancelled)
return;

DumpContents(uid, args.Args.Target, args.Args.User, component);
}

// Refactor to allow dumping that doesn't require a verb
[PublicAPI]
public void DumpContents(EntityUid uid, EntityUid? target, EntityUid user, DumpableComponent? component = null)
{
if (!TryComp<StorageComponent>(uid, out var storage)
Vonsant marked this conversation as resolved.
Show resolved Hide resolved
|| !Resolve(uid, ref component))
return;

if (storage.Container.ContainedEntities.Count == 0)
// Corvax-Next-MouthStorage-End
Vonsant marked this conversation as resolved.
Show resolved Hide resolved
return;

var dumpQueue = new Queue<EntityUid>(storage.Container.ContainedEntities);

var dumped = false;

if (_disposalUnitSystem.HasDisposals(args.Args.Target))
if (_disposalUnitSystem.HasDisposals(target)) // Corvax-Next-MouthStorage
{
dumped = true;

foreach (var entity in dumpQueue)
{
_disposalUnitSystem.DoInsertDisposalUnit(args.Args.Target.Value, entity, args.Args.User);
_disposalUnitSystem.DoInsertDisposalUnit(target.Value, entity, user); // Corvax-Next-MouthStorage
}
}
else if (HasComp<PlaceableSurfaceComponent>(args.Args.Target))
else if (HasComp<PlaceableSurfaceComponent>(target)) // Corvax-Next-MouthStorage
{
dumped = true;

var (targetPos, targetRot) = _transformSystem.GetWorldPositionRotation(args.Args.Target.Value);
var (targetPos, targetRot) = _transformSystem.GetWorldPositionRotation(target.Value); // Corvax-Next-MouthStorage

foreach (var entity in dumpQueue)
{
Expand All @@ -179,7 +196,7 @@ private void OnDoAfter(EntityUid uid, DumpableComponent component, DumpableDoAft

if (dumped)
{
_audio.PlayPredicted(component.DumpSound, uid, args.User);
_audio.PlayPredicted(component.DumpSound, uid, user);// Corvax-Next-MouthStorage
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Robust.Shared.GameStates;

namespace Content.Shared._CorvaxNext.Implants.Radio;

/// <summary>
/// This indicates this entity has a radio implant implanted into themselves.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedRadioImplantSystem))]
public sealed partial class HasRadioImplantComponent : Component
{
/// <summary>
/// The radio implant. We need this to be able to determine encryption keys.
/// </summary>
[DataField, AutoNetworkedField]
public EntityUid? Implant;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Content.Shared.Radio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared._CorvaxNext.Implants.Radio;

/// <summary>
/// This is for radio implants. Might be Syndie, might not be Syndie, but either way, it's an implant.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedRadioImplantSystem))]
public sealed partial class RadioImplantComponent : Component
{
/// <summary>
/// The entity this implant got added to.
/// </summary>
[DataField, AutoNetworkedField]
public EntityUid? Implantee;

/// <summary>
/// The channels this implant can talk on.
/// </summary>
[DataField, AutoNetworkedField]
public HashSet<ProtoId<RadioChannelPrototype>> Channels = new();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Content.Shared.Actions;
using Content.Shared.Implants;
using Content.Shared.Storage;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Containers;

namespace Content.Shared._CorvaxNext.Implants.Radio;

/// <summary>
/// This handles radio implants, which you can implant to get access to a radio channel.
/// </summary>
public abstract class SharedRadioImplantSystem : EntitySystem
{
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<RadioImplantComponent, ImplantImplantedEvent>(OnImplanted);
SubscribeLocalEvent<RadioImplantComponent, EntGotRemovedFromContainerMessage>(OnPossiblyUnimplanted);
}

/// <summary>
/// Handles implantation of the implant.
/// </summary>
private void OnImplanted(EntityUid uid, RadioImplantComponent component, ImplantImplantedEvent args)
{
if (args.Implanted is not { Valid: true })
return;

component.Implantee = args.Implanted.Value;
Dirty(uid, component);

// make sure the person entity gets slapped with a component so it can react to it talking.
var hasRadioImplantComponent = EnsureComp<HasRadioImplantComponent>(args.Implanted.Value);
hasRadioImplantComponent.Implant = uid;
Dirty(component.Implantee.Value, hasRadioImplantComponent);
Vonsant marked this conversation as resolved.
Show resolved Hide resolved
}


/// <summary>
/// Handles removal of the implant from its containing mob.
/// </summary>
/// <remarks>Done via <see cref="EntGotRemovedFromContainerMessage"/> because there is no specific event for an implant being removed.</remarks>
private void OnPossiblyUnimplanted(EntityUid uid, RadioImplantComponent component, EntGotRemovedFromContainerMessage args)
{
if (Terminating(uid))
return;

// this gets fired if it gets removed from ANY container but really, we just want to know if it was removed from its owner...
// so check if the ent we got implanted into matches the container's owner (here, the container's owner is the entity)
if (component.Implantee is not null && component.Implantee == args.Container.Owner)
{
RemComp<HasRadioImplantComponent>(component.Implantee.Value);
component.Implantee = null;
Vonsant marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Content.Shared._CorvaxNext.Storage.EntitySystems;
using Content.Shared.FixedPoint;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._CorvaxNext.Storage.Components;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(SharedMouthStorageSystem))]
public sealed partial class MouthStorageComponent : Component
{
public const string MouthContainerId = "mouth";
Vonsant marked this conversation as resolved.
Show resolved Hide resolved

[DataField, AutoNetworkedField]
public EntProtoId? OpenStorageAction;

[DataField, AutoNetworkedField]
public EntityUid? Action;

[DataField]
public EntProtoId MouthProto = "ActionOpenMouthStorage";

[ViewVariables]
public Container Mouth = default!;

[DataField]
public EntityUid? MouthId;

// Mimimum inflicted damage on hit to spit out items
[DataField]
public FixedPoint2 SpitDamageThreshold = FixedPoint2.New(2);
Vonsant marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading