mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
Mindshield Refactor + Changeling Fake Mindshield (#43864)
* the great mindshield rework
* it just works :D
* glory to nyanotransen, I said with my very obviously fake mindshield.
* work on things to remove manual mindshield queries
* very wip impl
* yeah there we go apc destroyed mission accomplished
* some comments
* textures!
* i will stab the person who decided to spell it differently.
* rename things since i am crazy (i was also crazy once)
* minor spelling mistake :(
* Revert "minor spelling mistake :("
This reverts commit ece1f1c3509063d1c43b7f136509ed4df2cd62e7.
* Revert "rename things since i am crazy (i was also crazy once)"
This reverts commit e75411d4bb84e819ca525b9fa0fe1792b6ace0b6.
* Revert "i will stab the person who decided to spell it differently."
This reverts commit 696ffdc062094cc9e4cf382f1e39b4ae4e3d781d.
* fixed things + better texs
* aaaa
* INSANITY RAHHHHH
* e
* changing things a bit.
* testifying things actually no insanity
* cleaning
* only doing this cuz of heseintests failing
* cleanup
* Update Content.Server/Revolutionary/RevolutionarySystem.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* Apply suggestions from code review
fucking suggestions WHY IS THE BUTTON THERE EVEN WHEN ITS DISABLED!!!!
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* half of things for scarky0
* event merging stub / beginning
* big rewrite
* there ya go scar
* e
* NETWORK YOU PRICK
* Update Content.Shared/Mindshield/SharedMindShieldSystem.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* Update Content.Shared/Mindshield/SharedMindShieldSystem.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* Update Content.Server/Revolutionary/RevolutionarySystem.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* Update Content.Shared/Mindshield/FakeMindshieldSystem.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* e
* RAHHHH
* cleaning
* Update changeling.yml
* Update meta.json
* slarti 1/3
* slarti things 2
* le real fix
* commit & push
* Update Content.Client/Overlays/ShowMindShieldIconsSystem.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* Update Content.Server/Implants/MindshieldImplantSystem.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* Update Content.Shared/Mindshield/SharedMindShieldSystem.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* Update Content.Shared/Mindshield/FakeMindshieldSystem.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* Update Content.Shared/Mindshield/Components/MindShieldStatusComponent.cs
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* scarky0
* dirty
* fml
* robust thyself now
* minor fix
* stub
* Revert "stub"
This reverts commit c1204b88d3ae1bbdca2deeb075a9dd0d6c529326.
* s
* jrwebiwerhweir
* shrimple fix
* Apply suggestions from code review
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
* yeeeuuuwhqeheueuuu
* rweifohgeterwt
* cleaning, compiling
* container bs and icons
* werewrewr
* fix
* crisis averted
* howd i forget this
* leave me ALONE
---------
Co-authored-by: nomdéraisonnablementlong <you@example.com>
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Co-authored-by: ScarKy0 <scarky0@onet.eu>
Co-authored-by: Pok245 <113675512+Pok27@users.noreply.github.com>
This commit is contained in:
parent
3a33309939
commit
ea2dd83e77
34 changed files with 583 additions and 297 deletions
|
|
@ -1,4 +1,4 @@
|
|||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Mindshield;
|
||||
using Content.Shared.Overlays;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
|
||||
|
|
@ -6,29 +6,17 @@ namespace Content.Client.Overlays;
|
|||
|
||||
public sealed partial class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShieldIconsComponent>
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
[Dependency] private MindShieldSystem _mindShield = default!;
|
||||
|
||||
SubscribeLocalEvent<MindShieldComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
|
||||
SubscribeLocalEvent<FakeMindShieldComponent, GetStatusIconsEvent>(OnGetStatusIconsEventFake);
|
||||
}
|
||||
// TODO: Probably need to get this OFF of client since this can be read by bad actors rather easily
|
||||
// ...imagine cheating in a game about silly paper dolls
|
||||
private void OnGetStatusIconsEventFake(EntityUid uid, FakeMindShieldComponent component, ref GetStatusIconsEvent ev)
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
if (component.IsEnabled && ProtoMan.Resolve(component.MindShieldStatusIcon, out var fakeStatusIconPrototype))
|
||||
ev.StatusIcons.Add(fakeStatusIconPrototype);
|
||||
}
|
||||
|
||||
private void OnGetStatusIconsEvent(EntityUid uid, MindShieldComponent component, ref GetStatusIconsEvent ev)
|
||||
[SubscribeLocalEvent]
|
||||
private void OnGetStatusIconsEvent(Entity<StatusIconComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
// Is active checks for our ability to display status icons
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
if (ProtoMan.Resolve(component.MindShieldStatusIcon, out var iconPrototype))
|
||||
ev.StatusIcons.Add(iconPrototype);
|
||||
_mindShield.GetMindshieldStatus(ent.Owner, out _, out var isVisible);
|
||||
if (isVisible && ProtoMan.Resolve(MindShieldSystem.StatusIcon, out var statusIconPrototype))
|
||||
args.StatusIcons.Add(statusIconPrototype);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +0,0 @@
|
|||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.Revolutionary;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
|
||||
namespace Content.Client.Revolutionary;
|
||||
|
||||
/// <summary>
|
||||
/// Used for the client to get status icons from other revs.
|
||||
/// </summary>
|
||||
public sealed partial class RevolutionarySystem : SharedRevolutionarySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevolutionaryComponent, GetStatusIconsEvent>(GetRevIcon);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, GetStatusIconsEvent>(GetHeadRevIcon);
|
||||
}
|
||||
|
||||
private void GetRevIcon(Entity<RevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(ent))
|
||||
return;
|
||||
|
||||
if (ProtoMan.Resolve(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
private void GetHeadRevIcon(Entity<HeadRevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (ProtoMan.Resolve(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ using Robust.Shared.Prototypes;
|
|||
using Robust.Shared.Timing;
|
||||
using Content.Shared.Cuffs.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Content.Shared.Mindshield;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
|
|
@ -52,6 +53,7 @@ public sealed partial class RevolutionaryRuleSystem : GameRuleSystem<Revolutiona
|
|||
[Dependency] private RoundEndSystem _roundEnd = default!;
|
||||
[Dependency] private SharedStunSystem _stun = default!;
|
||||
[Dependency] private StationSystem _stationSystem = default!;
|
||||
[Dependency] private MindShieldSystem _mindShield = default!;
|
||||
|
||||
//Used in OnPostFlash, no reference to the rule component is available
|
||||
public readonly ProtoId<NpcFactionPrototype> RevolutionaryNpcFaction = "Revolutionary";
|
||||
|
|
@ -152,7 +154,8 @@ public sealed partial class RevolutionaryRuleSystem : GameRuleSystem<Revolutiona
|
|||
var attemptConvertEv = new AttemptConvertRevolutionaryEvent();
|
||||
RaiseLocalEvent(ev.Target, ref attemptConvertEv);
|
||||
|
||||
if (attemptConvertEv.Cancelled)
|
||||
_mindShield.GetMindshieldStatus(ev.Target, out var isMindshielded, out _);
|
||||
if (attemptConvertEv.Cancelled || isMindshielded)
|
||||
return;
|
||||
|
||||
_npcFaction.AddFaction(ev.Target, RevolutionaryNpcFaction);
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Implants;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Revolutionary;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.Roles.Components;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Mindshield;
|
||||
|
||||
/// <summary>
|
||||
/// System used for adding or removing components with a mindshield implant
|
||||
/// as well as checking if the implanted is a Rev or Head Rev.
|
||||
/// </summary>
|
||||
public sealed partial class MindShieldSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IAdminLogManager _adminLogManager = default!;
|
||||
[Dependency] private RoleSystem _roleSystem = default!;
|
||||
[Dependency] private MindSystem _mindSystem = default!;
|
||||
[Dependency] private PopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MindShieldImplantComponent, ImplantImplantedEvent>(OnImplantImplanted);
|
||||
SubscribeLocalEvent<MindShieldImplantComponent, ImplantRemovedEvent>(OnImplantRemoved);
|
||||
SubscribeLocalEvent<MindShieldComponent, AttemptConvertRevolutionaryEvent>(OnAttemptConvert);
|
||||
}
|
||||
|
||||
private void OnImplantImplanted(Entity<MindShieldImplantComponent> ent, ref ImplantImplantedEvent ev)
|
||||
{
|
||||
EnsureComp<MindShieldComponent>(ev.Implanted);
|
||||
MindShieldRemovalCheck(ev.Implanted, ev.Implant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the implanted person was a Rev or Head Rev and remove role or destroy mindshield respectively.
|
||||
/// </summary>
|
||||
private void MindShieldRemovalCheck(EntityUid implanted, EntityUid implant)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(implanted))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("head-rev-break-mindshield"), implanted);
|
||||
QueueDel(implant);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mindSystem.TryGetMind(implanted, out var mindId, out _) &&
|
||||
_roleSystem.MindRemoveRole<RevolutionaryRoleComponent>(mindId))
|
||||
{
|
||||
_adminLogManager.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(implanted)} was deconverted due to being implanted with a Mindshield.");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnImplantRemoved(Entity<MindShieldImplantComponent> ent, ref ImplantRemovedEvent args)
|
||||
{
|
||||
RemComp<MindShieldComponent>(args.Implanted);
|
||||
}
|
||||
|
||||
private void OnAttemptConvert(Entity<MindShieldComponent> ent, ref AttemptConvertRevolutionaryEvent args)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
using Content.Shared.Revolutionary;
|
||||
|
||||
namespace Content.Server.Revolutionary;
|
||||
|
||||
public sealed class RevolutionarySystem : SharedRevolutionarySystem;
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Actions;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Shared.Mindshield;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Store.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.Store.Components;
|
||||
|
|
@ -26,6 +26,7 @@ public sealed partial class StoreSystem
|
|||
[Dependency] private SharedAudioSystem _audio = default!;
|
||||
[Dependency] private SharedHandsSystem _hands = default!;
|
||||
[Dependency] private StackSystem _stack = default!;
|
||||
[Dependency] private MindShieldSystem _mindShield = default!;
|
||||
|
||||
private void InitializeUi()
|
||||
{
|
||||
|
|
@ -221,7 +222,8 @@ public sealed partial class StoreSystem
|
|||
logImpact = LogImpact.High;
|
||||
logExtraInfo = ", but was not from an expected faction";
|
||||
|
||||
if (HasComp<MindShieldComponent>(buyer))
|
||||
_mindShield.GetMindshieldStatus(buyer, out var isMindshielded, out _);
|
||||
if (isMindshielded)
|
||||
{
|
||||
logImpact = LogImpact.Extreme;
|
||||
logExtraInfo += " while also possessing a mindshield";
|
||||
|
|
|
|||
52
Content.Shared/Implants/MindshieldImplantSystem.cs
Normal file
52
Content.Shared/Implants/MindshieldImplantSystem.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Roles.Components;
|
||||
using Content.Shared.Stunnable;
|
||||
|
||||
namespace Content.Shared.Implants;
|
||||
|
||||
public sealed partial class MindshieldImplantSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private SharedPopupSystem _popup = default!;
|
||||
[Dependency] private SharedStunSystem _sharedStun = default!;
|
||||
[Dependency] private SharedMindSystem _mind = default!;
|
||||
[Dependency] private SharedRoleSystem _role = default!;
|
||||
[Dependency] private ISharedAdminLogManager _log = default!;
|
||||
|
||||
/// <summary>
|
||||
/// When the mindshield is implanted in the rev it will popup saying they were deconverted. In Head Revs it will remove the mindshield component.
|
||||
/// </summary>
|
||||
[SubscribeLocalEvent]
|
||||
private void OnImplantImplanted(Entity<MindShieldImplantComponent> ent, ref ImplantImplantedEvent args)
|
||||
{
|
||||
// Entity that was implanted
|
||||
var uid = args.Implanted;
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("head-rev-break-mindshield"), uid);
|
||||
PredictedQueueDel(args.Implant);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryComp<RevolutionaryComponent>(uid, out var comp))
|
||||
{
|
||||
if (_mind.TryGetMind(uid, out var mindId, out _))
|
||||
{
|
||||
_log.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(uid)} was deconverted due to being implanted with a Mindshield.");
|
||||
|
||||
if (_role.MindHasRole<RevolutionaryRoleComponent>(mindId))
|
||||
_role.MindRemoveRole<RevolutionaryRoleComponent>(mindId);
|
||||
}
|
||||
var name = Identity.Entity(uid, EntityManager);
|
||||
RemComp<RevolutionaryComponent>(uid);
|
||||
_sharedStun.TryUpdateParalyzeDuration(uid, comp.StunTime);
|
||||
_popup.PopupEntity(Loc.GetString("rev-break-control", ("name", name)), uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ using Content.Shared.Chat;
|
|||
using Content.Shared.IdentityManagement.Components;
|
||||
using Content.Shared.Implants.Components;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Mindshield;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.VoiceMask;
|
||||
|
|
@ -18,10 +19,13 @@ public abstract partial class SharedSubdermalImplantSystem
|
|||
SubscribeLocalEvent<ImplantedComponent, TransformSpeechEvent>(RelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, SeeIdentityAttemptEvent>(RelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, VoiceMaskToggledEvent>(RelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, FakeMindShieldToggleEvent>(RelayToImplantEvent);
|
||||
|
||||
// Ref relays, for when you need to write to the event!
|
||||
SubscribeLocalEvent<ImplantedComponent, CurrencyInsertAttemptEvent>(RefRelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, GetStoreEvent>(RefRelayToImplantEvent);
|
||||
SubscribeLocalEvent<ImplantedComponent, GetMindShieldStatusEvent>(RefRelayToImplantEvent, after: [typeof(MindShieldSystem)]);
|
||||
SubscribeLocalEvent<ImplantedComponent, ChameleonControllerOutfitSelectedEvent>(RefRelayToImplantEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ using Content.Shared.Gravity;
|
|||
using Content.Shared.IdentityManagement.Components;
|
||||
using Content.Shared.Implants;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Mindshield;
|
||||
using Content.Shared.Lube;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
|
|
@ -64,6 +65,7 @@ public partial class InventorySystem
|
|||
SubscribeLocalEvent<InventoryComponent, ChameleonControllerOutfitSelectedEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, VoiceMaskNameUpdatedEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, VoiceMaskToggledEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, FakeMindShieldToggleEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, BeforeEmoteEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, StoodEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, DownedEvent>(RelayInventoryEvent);
|
||||
|
|
@ -85,6 +87,7 @@ public partial class InventorySystem
|
|||
SubscribeLocalEvent<InventoryComponent, WieldAttemptEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, UnwieldAttemptEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, GetMindShieldStatusEvent>(RefRelayInventoryEvent, after: [typeof(SharedSubdermalImplantSystem)]); // ordering so that things like mindshield-flipping hats can be done
|
||||
SubscribeLocalEvent<InventoryComponent, BeforeHeatExchangeEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, ScreechEffectAttemptEvent>(RefRelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, RefreshNightVisionEvent>(RefRelayInventoryEvent);
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Mindshield.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class FakeMindShieldImplantComponent : Component
|
||||
{
|
||||
}
|
||||
|
|
@ -1,22 +1,57 @@
|
|||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Mindshield.Components;
|
||||
|
||||
/// <summary>
|
||||
/// A toggleable fake mindshield that only produces mindshield visuals, but does not protect against anything.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(MindShieldSystem), typeof(FakeMindShieldSystem))]
|
||||
public sealed partial class FakeMindShieldComponent : Component
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The state of the Fake mindshield, if true the owning entity will display a mindshield effect on their job icon
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool IsEnabled { get; set; } = false;
|
||||
public bool IsEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// The Security status icon displayed to the security officer. Should be a duplicate of the one the mindshield uses since it's spoofing that
|
||||
/// This tag should be placed on the fake mindshield action so there is a way to easily identify which action goes with which fake mindshield. Of course, this implies that no two similar fake mindshields should be on the same entity at the same time.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public ProtoId<SecurityIconPrototype> MindShieldStatusIcon = "MindShieldIcon";
|
||||
public ProtoId<TagPrototype> ActionTag = "FakeMindShieldImplant";
|
||||
|
||||
/// <summary>
|
||||
/// Makes it so chameleon implants affect this fake mindshield.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool ChameleonControllable = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the fake mindshield is innate to the entity.
|
||||
/// When added to an entity while this field is set to true, the entity itself will gain the action & UI necessary to toggle its fake mindshield.
|
||||
/// When this field is set to false, then the entity with this component will be a provider (either through implanting or through wearing) of the fake mindshield abilities for another entity.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool IsInnate;
|
||||
|
||||
/// <summary>
|
||||
/// Action linked to the fake mindshield, used for innate components
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public EntProtoId Action = "FakeMindShieldToggleAction";
|
||||
|
||||
/// <summary>
|
||||
/// Popup to show to the owner of the fake mindshield when it is enabled.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public LocId EnabledPopup = "fake-mindshield-enabled";
|
||||
|
||||
/// <summary>
|
||||
/// Popup to show to the owner of the fake mindshield when it is enabled.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public LocId DisabledPopup = "fake-mindshield-disabled";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,10 @@
|
|||
using Content.Shared.Revolutionary;
|
||||
using Robust.Shared.GameStates;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Mindshield.Components;
|
||||
|
||||
/// <summary>
|
||||
/// If a player has a Mindshield they will get this component to prevent conversion.
|
||||
/// This component, on a clothing item, on an implant or on an entity, prevents "mind control". This means that you won't be convertable to the revolution, for instance.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
|
||||
public sealed partial class MindShieldComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public ProtoId<SecurityIconPrototype> MindShieldStatusIcon = "MindShieldIcon";
|
||||
}
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[Access(typeof(MindShieldSystem))]
|
||||
public sealed partial class MindShieldComponent : Component;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ using Robust.Shared.GameStates;
|
|||
namespace Content.Shared.Mindshield.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component given to an entity to mark it is a mindshield implant.
|
||||
/// Component given to an entity to mark it is a mindshield implant that will unconvert revolutionaries when implanted.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(RevolutionarySystem))]
|
||||
public sealed partial class MindShieldImplantComponent : Component;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Mindshield.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This component stores an entity's mindshield status for easier retrieval later on
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(MindShieldSystem))]
|
||||
public sealed partial class MindShieldStatusComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the entity is protected from mind control & co.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool IsMindshielded;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the sec HUD will show a mindshield icon
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool IsVisible;
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Implants;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Mindshield.FakeMindShield;
|
||||
|
||||
/// <summary>
|
||||
/// This system is responsible for handling the fake mindshield implant.
|
||||
/// </summary>
|
||||
public sealed partial class FakeMindShieldSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private SharedActionsSystem _actions = default!;
|
||||
[Dependency] private SharedPopupSystem _popup = default!;
|
||||
[Dependency] private TagSystem _tag = default!;
|
||||
|
||||
/// <summary>
|
||||
/// This tag should be placed on the fake mindshield action for identification.
|
||||
/// </summary>
|
||||
private static readonly ProtoId<TagPrototype> FakeMindShieldImplantTag = "FakeMindShieldImplant";
|
||||
|
||||
/// <summary>
|
||||
/// Displays a popup to inform the player of activation or deactivation.
|
||||
/// </summary>
|
||||
/// <param name="ent">
|
||||
/// An associated tuple. The state in <see cref="FakeMindShieldComponent"/> will be used
|
||||
/// to display the appropriate popup.
|
||||
/// </param>
|
||||
private void ShowTogglePopup(Entity<FakeMindShieldComponent> ent)
|
||||
{
|
||||
var message = ent.Comp.IsEnabled
|
||||
? Loc.GetString("fake-mindshield-enabled")
|
||||
: Loc.GetString("fake-mindshield-disabled");
|
||||
|
||||
_popup.PopupEntity(message, ent, ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run when <see cref="FakeMindShieldToggleEvent"/> is raised. Toggles the component and the action indication.
|
||||
/// </summary>
|
||||
/// <param name="ent">An associated tuple. The state in <see cref="FakeMindShieldComponent"/> will be toggled.</param>
|
||||
/// <param name="args">The event arguments. Will be marked as handled.</param>
|
||||
[SubscribeLocalEvent]
|
||||
private void OnToggleMindshield(Entity<FakeMindShieldComponent> ent, ref FakeMindShieldToggleEvent args)
|
||||
{
|
||||
ent.Comp.IsEnabled = !ent.Comp.IsEnabled;
|
||||
args.Toggle = true;
|
||||
args.Handled = true;
|
||||
ShowTogglePopup(ent);
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles implant interactions with chameleon outfits.
|
||||
/// (De)Activates the mindshield if the chameleon outfit requires it. Displays a popup if state changes.
|
||||
/// </summary>
|
||||
/// <param name="ent">An associated tuple. The state in <see cref="FakeMindShieldComponent"/> will be set if changed.</param>
|
||||
/// <param name="args">The event arguments.</param>
|
||||
[SubscribeLocalEvent]
|
||||
private void OnChameleonControllerOutfitSelected(Entity<FakeMindShieldComponent> ent, ref ChameleonControllerOutfitSelectedEvent args)
|
||||
{
|
||||
if (ent.Comp.IsEnabled == args.ChameleonOutfit.HasMindShield)
|
||||
return;
|
||||
|
||||
// This assumes there is only one fake mindshield action per entity (This is currently enforced)
|
||||
if (!TryComp<ActionsComponent>(ent, out var actionsComp))
|
||||
return;
|
||||
|
||||
ent.Comp.IsEnabled = args.ChameleonOutfit.HasMindShield;
|
||||
|
||||
foreach (var action in actionsComp.Actions)
|
||||
{
|
||||
if (!_tag.HasTag(action, FakeMindShieldImplantTag))
|
||||
continue;
|
||||
|
||||
if (!TryComp<ActionComponent>(action, out var actionComp))
|
||||
continue;
|
||||
|
||||
_actions.SetToggled(action, args.ChameleonOutfit.HasMindShield);
|
||||
ShowTogglePopup(ent);
|
||||
|
||||
if (actionComp.UseDelay != null)
|
||||
_actions.SetCooldown(action, actionComp.UseDelay.Value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic <see cref="InstantActionEvent"/>.
|
||||
/// </summary>
|
||||
public sealed partial class FakeMindShieldToggleEvent : InstantActionEvent;
|
||||
200
Content.Shared/Mindshield/FakeMindshieldSystem.cs
Normal file
200
Content.Shared/Mindshield/FakeMindshieldSystem.cs
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.Components;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Implants;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Mindshield;
|
||||
|
||||
/// <summary>
|
||||
/// This system handles fake mindshields represented by <see cref="FakeMindShieldComponent"/>. Works using <see cref="SharedMindshieldSystem"/>'s <see cref="RefreshMindshieldStatus"/>
|
||||
/// </summary>
|
||||
public sealed partial class FakeMindShieldSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private SharedActionsSystem _actions = default!;
|
||||
[Dependency] private TagSystem _tag = default!;
|
||||
[Dependency] private IGameTiming _timing = default!;
|
||||
[Dependency] private MindShieldSystem _mindShields = default!;
|
||||
[Dependency] private SharedPopupSystem _popups = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
// Other events
|
||||
SubscribeLocalEvent<FakeMindShieldComponent, ImplantRelayEvent<ChameleonControllerOutfitSelectedEvent>>((ent, ref relay) => OnChameleonControllerOutfitSelectedImplant(ent, ref relay.Args, relay.ImplantedEntity));
|
||||
|
||||
// Toggle events
|
||||
SubscribeLocalEvent<FakeMindShieldComponent, InventoryRelayedEvent<FakeMindShieldToggleEvent>>((e, ref sk) => OnToggleMindshield(e, ref sk.Args));
|
||||
SubscribeLocalEvent<FakeMindShieldComponent, ImplantRelayEvent<FakeMindShieldToggleEvent>>((e, ref sk) => OnToggleMindshield(e, ref sk.Args));
|
||||
|
||||
// Mindshield events
|
||||
SubscribeLocalEvent<FakeMindShieldComponent, ImplantRelayEvent<GetMindShieldStatusEvent>>((a, ref k) => OnQueryStatus(a, ref k.Args));
|
||||
SubscribeLocalEvent<FakeMindShieldComponent, InventoryRelayedEvent<GetMindShieldStatusEvent>>((a, ref k) => OnQueryStatus(a, ref k.Args));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays a popup to inform the player of activation or deactivation.
|
||||
/// </summary>
|
||||
/// <param name="ent">
|
||||
/// An associated tuple. The state in <see cref="FakeMindShieldComponent"/> will be used
|
||||
/// to display the appropriate popup.
|
||||
/// </param>
|
||||
/// <param name="performer">The entity that toggled the fake mindshield.</param>
|
||||
private void ShowTogglePopup(Entity<FakeMindShieldComponent> ent, EntityUid performer)
|
||||
{
|
||||
var message = ent.Comp.IsEnabled
|
||||
? Loc.GetString(ent.Comp.EnabledPopup)
|
||||
: Loc.GetString(ent.Comp.DisabledPopup);
|
||||
|
||||
_popups.PopupEntity(message, performer, performer);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnFakeMindshieldEquip(Entity<FakeMindShieldComponent> ent, ref ClothingGotEquippedEvent args)
|
||||
{
|
||||
_mindShields.RefreshMindshieldStatus(args.Wearer);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnFakeMindshieldUnequip(Entity<FakeMindShieldComponent> ent, ref ClothingGotUnequippedEvent args)
|
||||
{
|
||||
_mindShields.RefreshMindshieldStatus(args.Wearer);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnFakeMindshieldRemoved(Entity<FakeMindShieldComponent> ent, ref ComponentRemove args)
|
||||
{
|
||||
if (!ent.Comp.IsInnate)
|
||||
return;
|
||||
|
||||
_mindShields.RefreshMindshieldStatus(ent.Owner);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnFakeMindshieldImplantRemoved(Entity<FakeMindShieldComponent> ent, ref ImplantRemovedEvent args)
|
||||
{
|
||||
// when we reinject the mindshield, the action will be disabled (since its a fresh action)
|
||||
// which would cause it to be desynced with the state of the mindshield
|
||||
// so we just disable it here
|
||||
ent.Comp.IsEnabled = false;
|
||||
Dirty(ent);
|
||||
|
||||
_mindShields.RefreshMindshieldStatus(args.Implanted);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnFakeMindshieldImplanted(Entity<FakeMindShieldComponent> ent, ref ImplantImplantedEvent args)
|
||||
{
|
||||
_mindShields.RefreshMindshieldStatus(args.Implanted);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnMapInit(Entity<FakeMindShieldComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
if (!ent.Comp.IsInnate)
|
||||
return;
|
||||
|
||||
_actions.AddAction(ent.Owner, ent.Comp.Action);
|
||||
_mindShields.RefreshMindshieldStatus(ent.Owner);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnQueryStatus(Entity<FakeMindShieldComponent> ent, ref GetMindShieldStatusEvent args)
|
||||
{
|
||||
args.IsVisible |= ent.Comp.IsEnabled;
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnToggleMindshield(Entity<FakeMindShieldComponent> ent, ref FakeMindShieldToggleEvent args)
|
||||
{
|
||||
if (args.ActionTag != ent.Comp.ActionTag)
|
||||
return;
|
||||
|
||||
ent.Comp.IsEnabled = !ent.Comp.IsEnabled;
|
||||
args.Toggle = true;
|
||||
args.Handled = true;
|
||||
ShowTogglePopup(ent, args.Performer);
|
||||
Dirty(ent.Owner, ent.Comp);
|
||||
_mindShields.RefreshMindshieldStatus(args.Performer);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnChameleonControllerOutfitSelected(Entity<FakeMindShieldComponent> ent, ref ChameleonControllerOutfitSelectedEvent args)
|
||||
{
|
||||
HandleChameleonOutfit(ent, ent.Comp, ent.Owner, ent.Owner, ref args);
|
||||
}
|
||||
|
||||
private void OnChameleonControllerOutfitSelectedImplant(Entity<FakeMindShieldComponent> ent, ref ChameleonControllerOutfitSelectedEvent args, EntityUid implantedEntity)
|
||||
{
|
||||
HandleChameleonOutfit(ent, ent.Comp, implantedEntity, implantedEntity, ref args);
|
||||
}
|
||||
|
||||
private void HandleChameleonOutfit(EntityUid fakeMindshieldUid, FakeMindShieldComponent component, EntityUid actionOwner, EntityUid statusOwner, ref ChameleonControllerOutfitSelectedEvent args)
|
||||
{
|
||||
if (!component.ChameleonControllable)
|
||||
return;
|
||||
|
||||
if (component.IsEnabled == args.ChameleonOutfit.HasMindShield)
|
||||
return;
|
||||
|
||||
// This assumes there is only one fake mindshield action per entity (This is currently enforced)
|
||||
if (!TryComp<ActionsComponent>(actionOwner, out var actionsComp))
|
||||
return;
|
||||
|
||||
// In case the fake mindshield ever doesn't have an action.
|
||||
var actionFound = false;
|
||||
|
||||
foreach (var action in actionsComp.Actions)
|
||||
{
|
||||
if (!_tag.HasTag(action, component.ActionTag))
|
||||
continue;
|
||||
|
||||
if (!TryComp<ActionComponent>(action, out var actionComp))
|
||||
continue;
|
||||
|
||||
actionFound = true;
|
||||
|
||||
if (_actions.IsCooldownActive(actionComp, _timing.CurTime))
|
||||
continue;
|
||||
|
||||
component.IsEnabled = args.ChameleonOutfit.HasMindShield;
|
||||
_actions.SetToggled(action, args.ChameleonOutfit.HasMindShield);
|
||||
Dirty(fakeMindshieldUid, component);
|
||||
|
||||
if (actionComp.UseDelay != null)
|
||||
_actions.SetCooldown(action, actionComp.UseDelay.Value);
|
||||
|
||||
_mindShields.RefreshMindshieldStatus(statusOwner);
|
||||
return;
|
||||
}
|
||||
|
||||
// If they don't have the action for some reason, still set it correctly.
|
||||
if (!actionFound)
|
||||
{
|
||||
component.IsEnabled = args.ChameleonOutfit.HasMindShield;
|
||||
Dirty(fakeMindshieldUid, component);
|
||||
_mindShields.RefreshMindshieldStatus(statusOwner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inventory relayed event telling all fake mindshields with the same ActionTag to try to toggle themselves.
|
||||
/// </summary>
|
||||
public sealed partial class FakeMindShieldToggleEvent : InstantActionEvent, IInventoryRelayEvent
|
||||
{
|
||||
public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET;
|
||||
|
||||
/// <summary>
|
||||
/// The tag the fake mindshield needs to have to be successfully toggled.
|
||||
/// We seperate it out to allow an entity to, for example, have an implant one and a store bought one without conflicts.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<TagPrototype> ActionTag = "FakeMindShieldImplant";
|
||||
}
|
||||
139
Content.Shared/Mindshield/MindShieldSystem.cs
Normal file
139
Content.Shared/Mindshield/MindShieldSystem.cs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Implants;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Mindshield;
|
||||
|
||||
public sealed partial class MindShieldSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IGameTiming _timing = default!;
|
||||
/// <summary>
|
||||
/// Status icon displayed in the sec HUD.
|
||||
/// </summary>
|
||||
public static ProtoId<SecurityIconPrototype> StatusIcon = "MindShieldIcon";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
// Mind shield status events
|
||||
SubscribeLocalEvent<MindShieldComponent, ImplantRelayEvent<GetMindShieldStatusEvent>>((e, ref k) => OnStatusQuery(e, ref k.Args));
|
||||
SubscribeLocalEvent<MindShieldComponent, InventoryRelayedEvent<GetMindShieldStatusEvent>>((e, ref k) => OnStatusQuery(e, ref k.Args));
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnMindshieldUnequip(Entity<MindShieldComponent> ent, ref ClothingGotUnequippedEvent args)
|
||||
{
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
RefreshMindshieldStatus(args.Wearer);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnMindshieldEquip(Entity<MindShieldComponent> ent, ref ClothingGotEquippedEvent args)
|
||||
{
|
||||
if (_timing.ApplyingState)
|
||||
return;
|
||||
|
||||
RefreshMindshieldStatus(args.Wearer);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnMindshieldRemoved(Entity<MindShieldComponent> ent, ref ComponentRemove args)
|
||||
{
|
||||
RefreshMindshieldStatus(ent.Owner);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnMindshieldImplantRemoved(Entity<MindShieldComponent> ent, ref ImplantRemovedEvent args)
|
||||
{
|
||||
RefreshMindshieldStatus(args.Implanted);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnMindshieldImplanted(Entity<MindShieldComponent> ent, ref ImplantImplantedEvent args)
|
||||
{
|
||||
RefreshMindshieldStatus(args.Implanted);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnMindshieldMapInit(Entity<MindShieldComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
// todo: make it not refresh on implant & clothing items
|
||||
RefreshMindshieldStatus(ent.Owner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function updates <see cref="MindShieldStatusComponent"/>. It should be called when anything makes a modification of its mindshielded-ness.
|
||||
/// </summary>
|
||||
public void RefreshMindshieldStatus(EntityUid ent)
|
||||
{
|
||||
var ev = new GetMindShieldStatusEvent();
|
||||
RaiseLocalEvent(ent, ref ev);
|
||||
var mindshielded = ev.IsMindshielded;
|
||||
var visible = ev.IsVisible;
|
||||
|
||||
if (!mindshielded && !visible)
|
||||
{
|
||||
if (HasComp<MindShieldStatusComponent>(ent))
|
||||
RemCompDeferred<MindShieldStatusComponent>(ent);
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureComp<MindShieldStatusComponent>(ent, out var c);
|
||||
c.IsMindshielded = mindshielded;
|
||||
c.IsVisible = visible;
|
||||
Dirty(ent, c);
|
||||
}
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnStatusQuery(Entity<MindShieldComponent> e, ref GetMindShieldStatusEvent args)
|
||||
{
|
||||
args.IsMindshielded = true;
|
||||
args.IsVisible = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves mindshielding data of an entity. Works via <see cref="MindShieldStatusComponent"/>, and so requires proper dirtying on the part of mindshield providers.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to check the mindshield status of.</param>
|
||||
/// <param name="isMindshielded">If the entity has a functional mind shield</param>
|
||||
/// <param name="isVisible">Wether the entity shows a mindshield icon on the sec HUD</param>
|
||||
/// <remarks>You should never look for a mindshield component and instead use this function.</remarks>
|
||||
public void GetMindshieldStatus(EntityUid entity, out bool isMindshielded, out bool isVisible)
|
||||
{
|
||||
if (TryComp<MindShieldStatusComponent>(entity, out var comp))
|
||||
{
|
||||
isMindshielded = comp.IsMindshielded;
|
||||
isVisible = comp.IsVisible;
|
||||
}
|
||||
else
|
||||
{
|
||||
isMindshielded = isVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised in order to get whether an entity is mindshielded visually, mechanically or both.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public sealed class GetMindShieldStatusEvent : EntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET;
|
||||
/// <summary>
|
||||
/// Whether the entity is mindshielded.
|
||||
/// </summary>
|
||||
public bool IsMindshielded;
|
||||
|
||||
/// <summary>
|
||||
/// Whether a mindshield icon is present
|
||||
/// </summary>
|
||||
public bool IsVisible;
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ namespace Content.Shared.Revolutionary.Components;
|
|||
/// <summary>
|
||||
/// Component used for allowing non-humans to be converted. (Mainly monkeys)
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(RevolutionarySystem))]
|
||||
public sealed partial class AlwaysRevolutionaryConvertibleComponent : Component
|
||||
{
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Content.Shared.Revolutionary.Components;
|
|||
/// <summary>
|
||||
/// Component used for marking a Head Rev for conversion and winning/losing.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(RevolutionarySystem))]
|
||||
public sealed partial class HeadRevolutionaryComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace Content.Shared.Revolutionary.Components;
|
|||
/// <summary>
|
||||
/// Used for marking regular revs as well as storing icon prototypes so you can see fellow revs.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRevolutionarySystem))]
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(RevolutionarySystem))]
|
||||
public sealed partial class RevolutionaryComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -24,5 +24,11 @@ public sealed partial class RevolutionaryComponent : Component
|
|||
[DataField]
|
||||
public SoundSpecifier RevStartSound = new SoundPathSpecifier("/Audio/Ambience/Antag/headrev_start.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// The duration of the stun when you get deconverted.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan StunTime = TimeSpan.FromSeconds(4f);
|
||||
|
||||
public override bool SessionSpecific => true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +1,26 @@
|
|||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.Stunnable;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Player;
|
||||
using Content.Shared.Antag;
|
||||
using Content.Shared.StatusIcon.Components;
|
||||
|
||||
namespace Content.Shared.Revolutionary;
|
||||
|
||||
public abstract partial class SharedRevolutionarySystem : EntitySystem
|
||||
public sealed partial class RevolutionarySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private SharedStunSystem _sharedStun = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MindShieldComponent, MapInitEvent>(MindShieldImplanted);
|
||||
SubscribeLocalEvent<RevolutionaryComponent, ComponentGetStateAttemptEvent>(OnRevCompGetStateAttempt);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, ComponentGetStateAttemptEvent>(OnRevCompGetStateAttempt);
|
||||
SubscribeLocalEvent<RevolutionaryComponent, ComponentStartup>(DirtyRevComps);
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, ComponentStartup>(DirtyRevComps);
|
||||
SubscribeLocalEvent<ShowAntagIconsComponent, ComponentStartup>(DirtyRevComps);
|
||||
SubscribeLocalEvent<RevolutionaryComponent, AttemptConvertRevolutionaryEvent>(OnAttemptConvert);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the mindshield is implanted in the rev it will popup saying they were deconverted. In Head Revs it will remove the mindshield component.
|
||||
/// </summary>
|
||||
private void MindShieldImplanted(EntityUid uid, MindShieldComponent comp, MapInitEvent init)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(uid))
|
||||
{
|
||||
RemCompDeferred<MindShieldComponent>(uid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasComp<RevolutionaryComponent>(uid))
|
||||
{
|
||||
var stunTime = TimeSpan.FromSeconds(4);
|
||||
var name = Identity.Entity(uid, EntityManager);
|
||||
RemComp<RevolutionaryComponent>(uid);
|
||||
_sharedStun.TryUpdateParalyzeDuration(uid, stunTime);
|
||||
_popupSystem.PopupEntity(Loc.GetString("rev-break-control", ("name", name)), uid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a HeadRev component should be sent to the client.
|
||||
/// </summary>
|
||||
[SubscribeLocalEvent]
|
||||
private void OnRevCompGetStateAttempt(EntityUid uid, HeadRevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args)
|
||||
{
|
||||
args.Cancelled = !CanGetState(args.Player);
|
||||
|
|
@ -59,6 +29,7 @@ public abstract partial class SharedRevolutionarySystem : EntitySystem
|
|||
/// <summary>
|
||||
/// Determines if a Rev component should be sent to the client.
|
||||
/// </summary>
|
||||
[SubscribeLocalEvent]
|
||||
private void OnRevCompGetStateAttempt(EntityUid uid, RevolutionaryComponent comp, ref ComponentGetStateAttemptEvent args)
|
||||
{
|
||||
args.Cancelled = !CanGetState(args.Player);
|
||||
|
|
@ -80,6 +51,7 @@ public abstract partial class SharedRevolutionarySystem : EntitySystem
|
|||
|
||||
return HasComp<ShowAntagIconsComponent>(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dirties all the Rev components so they are sent to clients.
|
||||
///
|
||||
|
|
@ -102,6 +74,7 @@ public abstract partial class SharedRevolutionarySystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnAttemptConvert(Entity<RevolutionaryComponent> ent, ref AttemptConvertRevolutionaryEvent args)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
|
|
@ -112,4 +85,21 @@ public abstract partial class SharedRevolutionarySystem : EntitySystem
|
|||
{
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void GetRevIcon(Entity<RevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (HasComp<HeadRevolutionaryComponent>(ent))
|
||||
return;
|
||||
|
||||
if (ProtoMan.Resolve(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void GetHeadRevIcon(Entity<HeadRevolutionaryComponent> ent, ref GetStatusIconsEvent args)
|
||||
{
|
||||
if (ProtoMan.Resolve(ent.Comp.StatusIcon, out var iconPrototype))
|
||||
args.StatusIcons.Add(iconPrototype);
|
||||
}
|
||||
}
|
||||
|
|
@ -51,5 +51,9 @@ changeling-takeover-success-self = We have successfully infested the body and as
|
|||
# stings
|
||||
changeling-sting-success = We silently sting {THE($target)}!
|
||||
|
||||
# mindshield
|
||||
changeling-fake-mindshield-enabled = We emit fake mindshield waves.
|
||||
changeling-fake-mindshield-disabled = We stop emitting fake mindshield waves.
|
||||
|
||||
# other
|
||||
changeling-paused-map-name = Changeling identity storage map
|
||||
|
|
|
|||
|
|
@ -33,5 +33,8 @@ changeling-catalog-last-resort-desc = Gib your current body and escape as a weak
|
|||
|
||||
Warning! This resets your progress as a changeling, removing your store purchases and absorbed identities!
|
||||
|
||||
changeling-catalog-voice-mindshield-name = Fake Mindshield
|
||||
changeling-catalog-voice-mindshield-desc = Modify your neuron's natural waves to copy those produced by Nanotrasen mindshield implants, making you look mindshielded to security members. Can be toggled.
|
||||
|
||||
changeling-catalog-night-vision-name = Night Vision
|
||||
changeling-catalog-night-vision-desc = You modify your photoreceptors and heighten your senses as to become able to see in complete darkness.
|
||||
|
|
|
|||
|
|
@ -304,3 +304,24 @@
|
|||
probability: 0.5
|
||||
- type: InstantAction
|
||||
event: !type:ScreechActionEvent
|
||||
|
||||
- type: entity
|
||||
parent: FakeMindShieldToggleAction
|
||||
id: ActionChangelingFakeMindshieldToggle
|
||||
name: '[color=green]Toggle Fake Mindshield[/color]'
|
||||
description: Turn our mindshield visibility on or off.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Interface/Actions/changeling2.rsi
|
||||
layers:
|
||||
- state: mindshield_off
|
||||
map: ["enum.ActionVisuals.Icon"]
|
||||
- state: mindshield_on
|
||||
map: ["enum.ActionVisuals.IconToggled"]
|
||||
visible: false
|
||||
- type: InstantAction
|
||||
event: !type:FakeMindShieldToggleEvent
|
||||
actionTag: FakeMindShieldChangeling
|
||||
- type: Tag
|
||||
tags:
|
||||
- FakeMindShieldChangeling # used to distinguish the changeling's toggle mindshield action from the implant's
|
||||
|
|
|
|||
|
|
@ -174,3 +174,17 @@
|
|||
- !type:ListingLimitedStockCondition
|
||||
stock: 1
|
||||
productAction: ActionChangelingScreech
|
||||
|
||||
- type: listing
|
||||
id: ChangelingFakeMindshield
|
||||
name: changeling-catalog-voice-mindshield-name
|
||||
description: changeling-catalog-voice-mindshield-desc
|
||||
icon: { sprite: /Textures/Interface/Actions/changeling2.rsi, state: mindshield_on }
|
||||
cost:
|
||||
ChangelingDNA: 10
|
||||
categories:
|
||||
- ChangelingStoreStealth
|
||||
conditions:
|
||||
- !type:ListingLimitedStockCondition
|
||||
stock: 1
|
||||
productComponents: ChangelingFakeMindshieldDummy
|
||||
|
|
|
|||
|
|
@ -108,3 +108,15 @@
|
|||
noiseMultiplier: 2
|
||||
overlayColor: "#AB0808AA"
|
||||
prioritized: false # Technically eye augments, so we want things such as NVGs to take priority if able
|
||||
|
||||
- type: entity # dummy prototype for the store listing
|
||||
id: ChangelingFakeMindshieldDummy
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: FakeMindShield
|
||||
actionTag: FakeMindShieldChangeling
|
||||
chameleonControllable: false
|
||||
isInnate: true
|
||||
enabledPopup: changeling-fake-mindshield-enabled
|
||||
disabledPopup: changeling-fake-mindshield-disabled
|
||||
action: ActionChangelingFakeMindshieldToggle
|
||||
|
|
|
|||
|
|
@ -366,9 +366,7 @@
|
|||
components:
|
||||
- type: SubdermalImplant
|
||||
implantAction: FakeMindShieldToggleAction
|
||||
implantComponents:
|
||||
- type: FakeMindShield # TODO: put the component on the implant and use implant relay events for the status icon
|
||||
- type: FakeMindShieldImplant
|
||||
- type: FakeMindShield
|
||||
|
||||
# Sec and Command implants
|
||||
|
||||
|
|
@ -381,6 +379,7 @@
|
|||
components:
|
||||
- type: SubdermalImplant
|
||||
- type: MindShieldImplant
|
||||
- type: MindShield
|
||||
|
||||
# Centcomm implants
|
||||
|
||||
|
|
|
|||
|
|
@ -559,6 +559,9 @@
|
|||
- type: Tag
|
||||
id: FakeMindShieldImplant # Used by FakeMindShieldSystem to toggle the tagged action when a chameleon outfit is selected.
|
||||
|
||||
- type: Tag # Also used by FakeMindShieldSystem, but only for changelings
|
||||
id: FakeMindShieldChangeling
|
||||
|
||||
- type: Tag
|
||||
id: FakeNukeDisk # Exists so that the fake nuke disk can be blacklisted by storages that blacklist the real disk.
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 1 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Created by Ubaser (Discord) for SS14, Modified for purpose by brassicaprime69 (Discord)",
|
||||
"license": "CC-BY-3.0",
|
||||
"copyright": "By ketufaispikinut (Github)",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark. Biodegrade, Mimicry and Extract Dna On/Off, Blind, Mute, Neurodepressant, Cryogenic, Lethal and Hallucination stings, screech, nv on/off by ketufaispikinut, based on TiniestShark's textures. Shop icon by ketufaispikinut, based on TiniestShark's textures. last_resort taken from https://github.com/tgstation/tgstation/blob/5c90546a27a1e5ea59b9f127ae274d7341acc70b/icons/mob/actions/actions_changeling.dmi and modified by Pok27 (GitHub). take_over_corpse by Pok27 (GitHub).",
|
||||
"copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark. Biodegrade, Mimicry and Extract Dna On/Off, Blind, Mute, Neurodepressant, Cryogenic, Lethal and Hallucination stings, screech, nv on/off by ketufaispikinut, based on TiniestShark's textures. Shop icon by ketufaispikinut, based on TiniestShark's textures. last_resort taken from https://github.com/tgstation/tgstation/blob/5c90546a27a1e5ea59b9f127ae274d7341acc70b/icons/mob/actions/actions_changeling.dmi and modified by Pok27 (GitHub). take_over_corpse by Pok27 (GitHub). Mindshield on/off textures by ketufaispikinut, based on TiniestShark's textures.",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
|
|
@ -43,6 +43,12 @@
|
|||
{
|
||||
"name": "extract_dna_off"
|
||||
},
|
||||
{
|
||||
"name": "mindshield_on"
|
||||
},
|
||||
{
|
||||
"name": "mindshield_off"
|
||||
},
|
||||
{
|
||||
"name": "shop"
|
||||
},
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2 KiB |
Loading…
Add table
Reference in a new issue